//
//    ██████ ██   ██  ██████   ██████
//   ██      ██   ██ ██    ██ ██            ** Classy Header-Only Classes **
//   ██      ███████ ██    ██ ██
//   ██      ██   ██ ██    ██ ██           https://github.com/Tracktion/choc
//    ██████ ██   ██  ██████   ██████
//
//   CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license:
//
//   Permission to use, copy, modify, and/or distribute this software for any purpose with or
//   without fee is hereby granted, provided that the above copyright notice and this permission
//   notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
//   WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
//   AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
//   CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
//   WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
//   CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#ifndef CHOC_JAVASCRIPT_DUKTAPE_HEADER_INCLUDED
#define CHOC_JAVASCRIPT_DUKTAPE_HEADER_INCLUDED

#include "choc_javascript.h"

/**
    This file contains the implementation of choc::javascript::createDuktapeContext(),
    and thus has the whole of Duktape inside it.

    If you use this in a project, you need to make sure that this header is included by
    one (and only one!) of your source files.
*/


//==============================================================================
//        _        _           _  _
//     __| |  ___ | |_   __ _ (_)| | ___
//    / _` | / _ \| __| / _` || || |/ __|
//   | (_| ||  __/| |_ | (_| || || |\__ \ _  _  _
//    \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_)
//
//   Code beyond this point is implementation detail...
//
//==============================================================================

#include "../platform/choc_Platform.h"

#if CHOC_WINDOWS
 #undef  WIN32_LEAN_AND_MEAN
 #define WIN32_LEAN_AND_MEAN
 #undef  NOMINMAX
 #define NOMINMAX
 #define Rectangle Rectangle_renamed_to_avoid_name_collisions
 #include <windows.h>
 #undef Rectangle
#else
 #include <cmath>
 #include <climits>
 #include <ctime>
 #include <cstddef>
 #include <cstdarg>
 #include <cstring>
 #include <ctime>
#endif

#include <csetjmp>

#if CHOC_LINUX || CHOC_OSX
 #include <sys/time.h>
#endif

namespace choc::javascript
{
namespace duktape
{

#ifdef CHOC_REGISTER_OPEN_SOURCE_LICENCE
 CHOC_REGISTER_OPEN_SOURCE_LICENCE (Duktape, R"(
==============================================================================
Duktape license: (http://opensource.org/licenses/MIT)

Copyright (c) 2013-2019 by Duktape authors (see AUTHORS.rst)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
)")
#endif

#include "../platform/choc_DisableAllWarnings.h"

/*

  The massive chunk of code inside this duktape namespace is the content
  of most of the files from duktape, copy-pasted into a huge sprawling
  mess so we can all live the single-header-file dream.

  Some hacks were made to hammer it into a shape that will work as a
  C++ header file.

  ===============
  Duktape license
  ===============

  (http://opensource.org/licenses/MIT)

  Copyright (c) 2013-2019 by Duktape authors (see AUTHORS.rst)

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.

  ===============
  Duktape authors
  ===============

  Copyright
  =========

  Duktape copyrights are held by its authors.  Each author has a copyright
  to their contribution, and agrees to irrevocably license the contribution
  under the Duktape ``LICENSE.txt``.

  Authors
  =======

  Please include an e-mail address, a link to your GitHub profile, or something
  similar to allow your contribution to be identified accurately.

  The following people have contributed code, website contents, or Wiki contents,
  and agreed to irrevocably license their contributions under the Duktape
  ``LICENSE.txt`` (in order of appearance):

  * Sami Vaarala <sami.vaarala@iki.fi>
  * Niki Dobrev
  * Andreas \u00d6man <andreas@lonelycoder.com>
  * L\u00e1szl\u00f3 Lang\u00f3 <llango.u-szeged@partner.samsung.com>
  * Legimet <legimet.calc@gmail.com>
  * Karl Skomski <karl@skomski.com>
  * Bruce Pascoe <fatcerberus1@gmail.com>
  * Ren\u00e9 Hollander <rene@rene8888.at>
  * Julien Hamaide (https://github.com/crazyjul)
  * Sebastian G\u00f6tte (https://github.com/jaseg)
  * Tomasz Magulski (https://github.com/magul)
  * \D. Bohdan (https://github.com/dbohdan)
  * Ond\u0159ej Jirman (https://github.com/megous)
  * Sa\u00fal Ibarra Corretg\u00e9 <saghul@gmail.com>
  * Jeremy HU <huxingyi@msn.com>
  * Ole Andr\u00e9 Vadla Ravn\u00e5s (https://github.com/oleavr)
  * Harold Brenes (https://github.com/harold-b)
  * Oliver Crow (https://github.com/ocrow)
  * Jakub Ch\u0142api\u0144ski (https://github.com/jchlapinski)
  * Brett Vickers (https://github.com/beevik)
  * Dominik Okwieka (https://github.com/okitec)
  * Remko Tron\u00e7on (https://el-tramo.be)
  * Romero Malaquias (rbsm@ic.ufal.br)
  * Michael Drake <michael.drake@codethink.co.uk>
  * Steven Don (https://github.com/shdon)
  * Simon Stone (https://github.com/sstone1)
  * \J. McC. (https://github.com/jmhmccr)
  * Jakub Nowakowski (https://github.com/jimvonmoon)
  * Tommy Nguyen (https://github.com/tn0502)
  * Fabrice Fontaine (https://github.com/ffontaine)
  * Christopher Hiller (https://github.com/boneskull)
  * Gonzalo Diethelm (https://github.com/gonzus)
  * Michal Kasperek (https://github.com/michalkas)
  * Andrew Janke (https://github.com/apjanke)
  * Steve Fan (https://github.com/stevefan1999)
  * Edward Betts (https://github.com/edwardbetts)
  * Ozhan Duz (https://github.com/webfolderio)
  * Akos Kiss (https://github.com/akosthekiss)
  * TheBrokenRail (https://github.com/TheBrokenRail)
  * Jesse Doyle (https://github.com/jessedoyle)
  * Gero Kuehn (https://github.com/dc6jgk)
  * James Swift (https://github.com/phraemer)
  * Luis de Bethencourt (https://github.com/luisbg)
  * Ian Whyman (https://github.com/v00d00)
  * Rick Sayre (https://github.com/whorfin)

  Other contributions
  ===================

  The following people have contributed something other than code (e.g. reported
  bugs, provided ideas, etc; roughly in order of appearance):

  * Greg Burns
  * Anthony Rabine
  * Carlos Costa
  * Aur\u00e9lien Bouilland
  * Preet Desai (Pris Matic)
  * judofyr (http://www.reddit.com/user/judofyr)
  * Jason Woofenden
  * Micha\u0142 Przyby\u015b
  * Anthony Howe
  * Conrad Pankoff
  * Jim Schimpf
  * Rajaran Gaunker (https://github.com/zimbabao)
  * Andreas \u00d6man
  * Doug Sanden
  * Josh Engebretson (https://github.com/JoshEngebretson)
  * Remo Eichenberger (https://github.com/remoe)
  * Mamod Mehyar (https://github.com/mamod)
  * David Demelier (https://github.com/markand)
  * Tim Caswell (https://github.com/creationix)
  * Mitchell Blank Jr (https://github.com/mitchblank)
  * https://github.com/yushli
  * Seo Sanghyeon (https://github.com/sanxiyn)
  * Han ChoongWoo (https://github.com/tunz)
  * Joshua Peek (https://github.com/josh)
  * Bruce E. Pascoe (https://github.com/fatcerberus)
  * https://github.com/Kelledin
  * https://github.com/sstruchtrup
  * Michael Drake (https://github.com/tlsa)
  * https://github.com/chris-y
  * Laurent Zubiaur (https://github.com/lzubiaur)
  * Neil Kolban (https://github.com/nkolban)
  * Wilhelm Wanecek (https://github.com/wanecek)
  * Andrew Janke (https://github.com/apjanke)
  * Unamer (https://github.com/unamer)
  * Karl Dahlke (eklhad@gmail.com)

  If you are accidentally missing from this list, send me an e-mail
  (``sami.vaarala@iki.fi``) and I'll fix the omission.
*/

using std::signbit;
using std::fpclassify;
using std::isnan;
using std::isinf;
using std::isfinite;

//#line 1 "duk_replacements.c"
/*
 *  Replacements for missing platform functions.
 *
 *  Unlike the originals, fpclassify() and signbit() replacements don't
 *  work on any floating point types, only doubles.  The C typing here
 *  mimics the standard prototypes.
 */

/* #include duk_internal.h */
//#line 1 "duk_internal.h"
/*
 *  Top-level include file to be used for all (internal) source files.
 *
 *  Source files should not include individual header files, as they
 *  have not been designed to be individually included.
 */

#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

#define DUK_SINGLE_FILE

/*
 *  BEGIN PUBLIC API
 */

/*
 *  Version and Git commit identification
 */

/* Duktape version, (major * 10000) + (minor * 100) + patch.  Allows C code
 * to #if (DUK_VERSION >= NNN) against Duktape API version.  The same value
 * is also available to ECMAScript code in Duktape.version.  Unofficial
 * development snapshots have 99 for patch level (e.g. 0.10.99 would be a
 * development version after 0.10.0 but before the next official release).
 */
#define DUK_VERSION                       20600L

/* Git commit, describe, and branch for Duktape build.  Useful for
 * non-official snapshot builds so that application code can easily log
 * which Duktape snapshot was used.  Not available in the ECMAScript
 * environment.
 */
#define DUK_GIT_COMMIT                    "fffa346eff06a8764b02c31d4336f63a773a95c3"
#define DUK_GIT_DESCRIBE                  "v2.6.0"
#define DUK_GIT_BRANCH                    "v2-maintenance"


/*
 *  Intermediate helper defines
 */

/* DLL build detection */
/* not configured for DLL build */
#undef DUK_F_DLL_BUILD

/* Apple OSX, iOS */
#if defined(__APPLE__)
#define DUK_F_APPLE
#endif

/* FreeBSD */
#if defined(__FreeBSD__) || defined(__FreeBSD)
#define DUK_F_FREEBSD
#endif

/* Orbis (PS4) variant */
#if defined(DUK_F_FREEBSD) && defined(__ORBIS__)
#define DUK_F_ORBIS
#endif

/* OpenBSD */
#if defined(__OpenBSD__) || defined(__OpenBSD)
#define DUK_F_OPENBSD
#endif

/* NetBSD */
#if defined(__NetBSD__) || defined(__NetBSD)
#define DUK_F_NETBSD
#endif

/* BSD variant */
#if defined(DUK_F_FREEBSD) || defined(DUK_F_NETBSD) || defined(DUK_F_OPENBSD) || \
    defined(__bsdi__) || defined(__DragonFly__)
#define DUK_F_BSD
#endif

/* Atari ST TOS.  __TOS__ defined by PureC.  No platform define in VBCC
 * apparently, so to use with VBCC user must define __TOS__ manually.
  */
#if defined(__TOS__)
#define DUK_F_TOS
#endif

/* Motorola 68K.  Not defined by VBCC, so user must define one of these
 * manually when using VBCC.
 */
#if defined(__m68k__) || defined(M68000) || defined(__MC68K__)
#define DUK_F_M68K
#endif

/* AmigaOS.  Neither AMIGA nor __amigaos__ is defined on VBCC, so user must
 * define 'AMIGA' manually when using VBCC.
 */
#if defined(AMIGA) || defined(__amigaos__)
#define DUK_F_AMIGAOS
#endif

/* PowerPC */
#if defined(__powerpc) || defined(__powerpc__) || defined(__PPC__)
#define DUK_F_PPC
#if defined(__PPC64__) || defined(__LP64__) || defined(_LP64)
#define DUK_F_PPC64
#else
#define DUK_F_PPC32
#endif
#endif

/* Durango (Xbox One) */
#if defined(_DURANGO) || defined(_XBOX_ONE)
#define DUK_F_DURANGO
#endif

/* Windows, both 32-bit and 64-bit */
#if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) || \
    defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__)
#define DUK_F_WINDOWS
#if defined(_WIN64) || defined(WIN64)
#define DUK_F_WIN64
#else
#define DUK_F_WIN32
#endif
#endif

/* Flash player (e.g. Crossbridge) */
#if defined(__FLASHPLAYER__)
#define DUK_F_FLASHPLAYER
#endif

/* QNX */
#if defined(__QNX__)
#define DUK_F_QNX
#endif

/* TI-Nspire (using Ndless) */
#if defined(_TINSPIRE)
#define DUK_F_TINSPIRE
#endif

/* Emscripten (provided explicitly by user), improve if possible */
#if defined(EMSCRIPTEN)
#define DUK_F_EMSCRIPTEN
#endif

/* BCC (Bruce's C compiler): this is a "torture target" for compilation */
#if defined(__BCC__) || defined(__BCC_VERSION__)
#define DUK_F_BCC
#endif

#if defined(ANDROID) || defined(__ANDROID__)
#define DUK_F_ANDROID
#endif

/* Linux */
#if defined(__linux) || defined(__linux__) || defined(linux)
#define DUK_F_LINUX
#endif

/* illumos / Solaris */
#if defined(__sun) && defined(__SVR4)
#define DUK_F_SUN
#if defined(__SUNPRO_C) && (__SUNPRO_C < 0x550)
#define DUK_F_OLD_SOLARIS
/* Defines _ILP32 / _LP64 required by DUK_F_X86/DUK_F_X64.  Platforms
 * are processed before architectures, so this happens before the
 * DUK_F_X86/DUK_F_X64 detection is emitted.
 */
//#include <sys/isa_defs.h>
#endif
#endif

/* AIX */
#if defined(_AIX)
/* defined(__xlc__) || defined(__IBMC__): works but too wide */
#define DUK_F_AIX
#endif

/* HPUX */
#if defined(__hpux)
#define DUK_F_HPUX
#if defined(__ia64)
#define DUK_F_HPUX_ITANIUM
#endif
#endif

/* POSIX */
#if defined(__posix)
#define DUK_F_POSIX
#endif

/* Cygwin */
#if defined(__CYGWIN__)
#define DUK_F_CYGWIN
#endif

/* Generic Unix (includes Cygwin) */
#if defined(__unix) || defined(__unix__) || defined(unix) || \
    defined(DUK_F_LINUX) || defined(DUK_F_BSD)
#define DUK_F_UNIX
#endif

/* Intel x86 (32-bit), x64 (64-bit) or x32 (64-bit but 32-bit pointers),
 * define only one of DUK_F_X86, DUK_F_X64, DUK_F_X32.
 * https://sites.google.com/site/x32abi/
 *
 * With DUK_F_OLD_SOLARIS the <sys/isa_defs.h> header must be included
 * before this.
 */
#if defined(__amd64__) || defined(__amd64) || \
    defined(__x86_64__) || defined(__x86_64) || \
    defined(_M_X64) || defined(_M_AMD64)
#if defined(__ILP32__) || defined(_ILP32)
#define DUK_F_X32
#else
#define DUK_F_X64
#endif
#elif defined(i386) || defined(__i386) || defined(__i386__) || \
      defined(__i486__) || defined(__i586__) || defined(__i686__) || \
      defined(__IA32__) || defined(_M_IX86) || defined(__X86__) || \
      defined(_X86_) || defined(__THW_INTEL__) || defined(__I86__)
#if defined(__LP64__) || defined(_LP64)
/* This should not really happen, but would indicate x64. */
#define DUK_F_X64
#else
#define DUK_F_X86
#endif
#endif

/* ARM */
#if defined(__arm__) || defined(__thumb__) || defined(_ARM) || defined(_M_ARM) || defined(_M_ARM64) || defined(__aarch64__)
#define DUK_F_ARM
#if defined(__LP64__) || defined(_LP64) || defined(__arm64) || defined(__arm64__) || defined(_M_ARM64) || defined(__aarch64__)
#define DUK_F_ARM64
#else
#define DUK_F_ARM32
#endif
#endif

/* MIPS.  Related defines: __MIPSEB__, __MIPSEL__, __mips_isa_rev, __LP64__ */
#if defined(__mips__) || defined(mips) || defined(_MIPS_ISA) || \
    defined(_R3000) || defined(_R4000) || defined(_R5900) || \
    defined(_MIPS_ISA_MIPS1) || defined(_MIPS_ISA_MIPS2) || \
    defined(_MIPS_ISA_MIPS3) || defined(_MIPS_ISA_MIPS4) || \
    defined(__mips) || defined(__MIPS__)
#define DUK_F_MIPS
#if defined(__LP64__) || defined(_LP64) || defined(__mips64) || \
    defined(__mips64__) || defined(__mips_n64)
#define DUK_F_MIPS64
#else
#define DUK_F_MIPS32
#endif
#endif

/* SPARC */
#if defined(sparc) || defined(__sparc) || defined(__sparc__)
#define DUK_F_SPARC
#if defined(__LP64__) || defined(_LP64)
#define DUK_F_SPARC64
#else
#define DUK_F_SPARC32
#endif
#endif

/* RISC-V, https://github.com/riscv/riscv-toolchain-conventions#cc-preprocessor-definitions */
#if defined(__riscv)
#define DUK_F_RISCV
#if defined(__riscv_xlen)
#if (__riscv_xlen == 32)
#define DUK_F_RISCV32
#elif (__riscv_xlen == 64)
#define DUK_F_RISCV64
#else
#error __riscv_xlen has unsupported value (not 32 or 64)
#endif
#else
#error __riscv defined without __riscv_xlen
#endif
#endif  /* __riscv */

/* SuperH */
#if defined(__sh__) || \
    defined(__sh1__) || defined(__SH1__) || \
    defined(__sh2__) || defined(__SH2__) || \
    defined(__sh3__) || defined(__SH3__) || \
    defined(__sh4__) || defined(__SH4__) || \
    defined(__sh5__) || defined(__SH5__)
#define DUK_F_SUPERH
#endif

/* Clang */
#if defined(__clang__)
#define DUK_F_CLANG
#endif

/* C++ */
#undef DUK_F_CPP
#if defined(__cplusplus)
#define DUK_F_CPP
#endif

/* C99 or above */
#undef DUK_F_C99
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
#define DUK_F_C99
#endif

/* C++11 or above */
#undef DUK_F_CPP11
#if defined(__cplusplus) && (__cplusplus >= 201103L)
#define DUK_F_CPP11
#endif

/* GCC.  Clang also defines __GNUC__ so don't detect GCC if using Clang. */
#if defined(__GNUC__) && !defined(__clang__) && !defined(DUK_F_CLANG)
#define DUK_F_GCC
#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
/* Convenience, e.g. gcc 4.5.1 == 40501; http://stackoverflow.com/questions/6031819/emulating-gccs-builtin-unreachable */
#define DUK_F_GCC_VERSION  (__GNUC__ * 10000L + __GNUC_MINOR__ * 100L + __GNUC_PATCHLEVEL__)
#else
#error cannot figure out gcc version
#endif
#endif

/* MinGW.  Also GCC flags (DUK_F_GCC) are enabled now. */
#if defined(__MINGW32__) || defined(__MINGW64__)
#define DUK_F_MINGW
#endif

/* MSVC */
#if defined(_MSC_VER)
/* MSVC preprocessor defines: http://msdn.microsoft.com/en-us/library/b0084kay.aspx
 * _MSC_FULL_VER includes the build number, but it has at least two formats, see e.g.
 * BOOST_MSVC_FULL_VER in http://www.boost.org/doc/libs/1_52_0/boost/config/compiler/visualc.hpp
 */
#define DUK_F_MSVC
#if defined(_MSC_FULL_VER)
#if (_MSC_FULL_VER > 100000000)
#define DUK_F_MSVC_FULL_VER _MSC_FULL_VER
#else
#define DUK_F_MSCV_FULL_VER (_MSC_FULL_VER * 10)
#endif
#endif
#endif  /* _MSC_VER */

/* TinyC */
#if defined(__TINYC__)
/* http://bellard.org/tcc/tcc-doc.html#SEC9 */
#define DUK_F_TINYC
#endif

/* VBCC */
#if defined(__VBCC__)
#define DUK_F_VBCC
#endif

/* Atari Mint */
#if defined(__MINT__)
#define DUK_F_MINT
#endif

/*
 *  Platform autodetection
 */

/* Workaround for older C++ compilers before including <inttypes.h>,
 * see e.g.: https://sourceware.org/bugzilla/show_bug.cgi?id=15366
 */
#if defined(__cplusplus) && !defined(__STDC_LIMIT_MACROS)
#define __STDC_LIMIT_MACROS
#endif
#if defined(__cplusplus) && !defined(__STDC_CONSTANT_MACROS)
#define __STDC_CONSTANT_MACROS
#endif

#if defined(DUK_F_APPLE)
/* --- Mac OSX, iPhone, Darwin --- */
#define DUK_USE_DATE_NOW_GETTIMEOFDAY
#define DUK_USE_DATE_TZO_GMTIME_R
#define DUK_USE_DATE_PRS_STRPTIME
#define DUK_USE_DATE_FMT_STRFTIME
//#include <TargetConditionals.h>
//#include <architecture/byte_order.h>
//#include <sys/param.h>
//#include <sys/time.h>
//#include <time.h>

/* http://stackoverflow.com/questions/5919996/how-to-detect-reliably-mac-os-x-ios-linux-windows-in-c-preprocessor */
#if TARGET_IPHONE_SIMULATOR
#define DUK_USE_OS_STRING "iphone-sim"
#elif TARGET_OS_IPHONE
#define DUK_USE_OS_STRING "iphone"
#elif TARGET_OS_MAC
#define DUK_USE_OS_STRING "osx"
#else
#define DUK_USE_OS_STRING "osx-unknown"
#endif

/* Use _setjmp() on Apple by default, see GH-55. */
#define DUK_JMPBUF_TYPE       jmp_buf
#define DUK_SETJMP(jb)        _setjmp((jb))
#define DUK_LONGJMP(jb)       _longjmp((jb), 1)
#elif defined(DUK_F_ORBIS)
/* --- Orbis --- */
/* Orbis = PS4 */
#define DUK_USE_DATE_NOW_GETTIMEOFDAY
#define DUK_USE_DATE_TZO_GMTIME_S
/* no parsing (not an error) */
#define DUK_USE_DATE_FMT_STRFTIME
//#include <sys/types.h>
//#include <machine/endian.h>
//#include <sys/param.h>
//#include <sys/time.h>
//#include <time.h>

#define DUK_USE_OS_STRING  "orbis"
#elif defined(DUK_F_OPENBSD)
/* --- OpenBSD --- */
/* http://www.monkey.org/openbsd/archive/ports/0401/msg00089.html */
#define DUK_USE_DATE_NOW_GETTIMEOFDAY
#define DUK_USE_DATE_TZO_GMTIME_R
#define DUK_USE_DATE_PRS_STRPTIME
#define DUK_USE_DATE_FMT_STRFTIME
//#include <sys/types.h>
//#include <sys/endian.h>
//#include <sys/param.h>
//#include <sys/time.h>
//#include <time.h>

#define DUK_USE_OS_STRING  "openbsd"
#elif defined(DUK_F_BSD)
/* --- Generic BSD --- */
#define DUK_USE_DATE_NOW_GETTIMEOFDAY
#define DUK_USE_DATE_TZO_GMTIME_R
#define DUK_USE_DATE_PRS_STRPTIME
#define DUK_USE_DATE_FMT_STRFTIME
//#include <sys/types.h>
//#include <sys/endian.h>
//#include <sys/param.h>
//#include <sys/time.h>
//#include <time.h>

#define DUK_USE_OS_STRING  "bsd"
#elif defined(DUK_F_TOS)
/* --- Atari ST TOS --- */
#define DUK_USE_DATE_NOW_TIME
#define DUK_USE_DATE_TZO_GMTIME
/* no parsing (not an error) */
#define DUK_USE_DATE_FMT_STRFTIME
//#include <time.h>

#define DUK_USE_OS_STRING  "tos"

/* TOS on M68K is always big endian. */
#if !defined(DUK_USE_BYTEORDER) && defined(DUK_F_M68K)
#define DUK_USE_BYTEORDER 3
#endif
#elif defined(DUK_F_AMIGAOS)
/* --- AmigaOS --- */
#if defined(DUK_F_M68K)
/* AmigaOS on M68k */
#define DUK_USE_DATE_NOW_TIME
#define DUK_USE_DATE_TZO_GMTIME
/* no parsing (not an error) */
#define DUK_USE_DATE_FMT_STRFTIME
//#include <time.h>
#elif defined(DUK_F_PPC)
#define DUK_USE_DATE_NOW_GETTIMEOFDAY
#define DUK_USE_DATE_TZO_GMTIME_R
#define DUK_USE_DATE_PRS_STRPTIME
#define DUK_USE_DATE_FMT_STRFTIME
//#include <time.h>
#if !defined(UINTPTR_MAX)
#define UINTPTR_MAX UINT_MAX
#endif
#else
#error AmigaOS but not M68K/PPC, not supported now
#endif

#define DUK_USE_OS_STRING "amigaos"

/* AmigaOS on M68K or PPC is always big endian. */
#if !defined(DUK_USE_BYTEORDER) && (defined(DUK_F_M68K) || defined(DUK_F_PPC))
#define DUK_USE_BYTEORDER 3
#endif
#elif defined(DUK_F_DURANGO)
/* --- Durango (XboxOne) --- */
/* Durango = XboxOne
 * Configuration is nearly identical to Windows, except for
 * DUK_USE_DATE_TZO_WINDOWS.
 */

/* Initial fix: disable secure CRT related warnings when compiling Duktape
 * itself (must be defined before including Windows headers).  Don't define
 * for user code including duktape.h.
 */
#if defined(DUK_COMPILING_DUKTAPE) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif

/* MSVC does not have sys/param.h */
#define DUK_USE_DATE_NOW_WINDOWS
#define DUK_USE_DATE_TZO_WINDOWS_NO_DST
/* Note: PRS and FMT are intentionally left undefined for now.  This means
 * there is no platform specific date parsing/formatting but there is still
 * the ISO 8601 standard format.
 */
#if defined(DUK_COMPILING_DUKTAPE)
/* Only include when compiling Duktape to avoid polluting application build
 * with a lot of unnecessary defines.
 */
//#include <windows.h>
#endif

#define DUK_USE_OS_STRING "durango"

#if !defined(DUK_USE_BYTEORDER)
#define DUK_USE_BYTEORDER 1
#endif
#elif defined(DUK_F_WINDOWS)
/* --- Windows --- */
/* Windows version can't obviously be determined at compile time,
 * but _WIN32_WINNT indicates the minimum version targeted:
 * - https://msdn.microsoft.com/en-us/library/6sehtctf.aspx
 */

/* Initial fix: disable secure CRT related warnings when compiling Duktape
 * itself (must be defined before including Windows headers).  Don't define
 * for user code including duktape.h.
 */
#if defined(DUK_COMPILING_DUKTAPE) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif

/* Windows 32-bit and 64-bit are currently the same. */
/* MSVC does not have sys/param.h */

#if defined(DUK_COMPILING_DUKTAPE)
/* Only include when compiling Duktape to avoid polluting application build
 * with a lot of unnecessary defines.
 */
//#include <windows.h>
#endif

/* GetSystemTimePreciseAsFileTime() available from Windows 8:
 * https://msdn.microsoft.com/en-us/library/windows/desktop/hh706895(v=vs.85).aspx
 */
#if defined(DUK_USE_DATE_NOW_WINDOWS_SUBMS) || defined(DUK_USE_DATE_NOW_WINDOWS)
/* User forced provider. */
#else
#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0602)
#define DUK_USE_DATE_NOW_WINDOWS_SUBMS
#else
#define DUK_USE_DATE_NOW_WINDOWS
#endif
#endif

#define DUK_USE_DATE_TZO_WINDOWS

/* Note: PRS and FMT are intentionally left undefined for now.  This means
 * there is no platform specific date parsing/formatting but there is still
 * the ISO 8601 standard format.
 */

/* QueryPerformanceCounter() may go backwards in Windows XP, so enable for
 * Vista and later: https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx#qpc_support_in_windows_versions
 */
#if !defined(DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC) && \
    defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600)
#define DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC
#endif

#define DUK_USE_OS_STRING "windows"

/* On Windows, assume we're little endian.  Even Itanium which has a
 * configurable endianness runs little endian in Windows.
 */
#if !defined(DUK_USE_BYTEORDER)
#define DUK_USE_BYTEORDER 1
#endif
#elif defined(DUK_F_FLASHPLAYER)
/* --- Flashplayer (Crossbridge) --- */
#define DUK_USE_DATE_NOW_GETTIMEOFDAY
#define DUK_USE_DATE_TZO_GMTIME_R
#define DUK_USE_DATE_PRS_STRPTIME
#define DUK_USE_DATE_FMT_STRFTIME
//#include <endian.h>
//#include <sys/param.h>
//#include <sys/time.h>
//#include <time.h>

#define DUK_USE_OS_STRING "flashplayer"

#if !defined(DUK_USE_BYTEORDER) && defined(DUK_F_FLASHPLAYER)
#define DUK_USE_BYTEORDER 1
#endif
#elif defined(DUK_F_QNX)
/* --- QNX --- */
#if defined(DUK_F_QNX) && defined(DUK_COMPILING_DUKTAPE)
/* See: /opt/qnx650/target/qnx6/usr/include/sys/platform.h */
#define _XOPEN_SOURCE    600
#define _POSIX_C_SOURCE  200112L
#endif

#define DUK_USE_DATE_NOW_GETTIMEOFDAY
#define DUK_USE_DATE_TZO_GMTIME_R
#define DUK_USE_DATE_PRS_STRPTIME
#define DUK_USE_DATE_FMT_STRFTIME
//#include <sys/types.h>
//#include <sys/param.h>
//#include <sys/time.h>
//#include <time.h>

#define DUK_USE_OS_STRING "qnx"
#elif defined(DUK_F_TINSPIRE)
/* --- TI-Nspire --- */
#if defined(DUK_COMPILING_DUKTAPE) && !defined(_XOPEN_SOURCE)
#define _XOPEN_SOURCE    /* e.g. strptime */
#endif

#define DUK_USE_DATE_NOW_GETTIMEOFDAY
#define DUK_USE_DATE_TZO_GMTIME_R
#define DUK_USE_DATE_PRS_STRPTIME
#define DUK_USE_DATE_FMT_STRFTIME
//#include <sys/types.h>
//#include <sys/param.h>
//#include <sys/time.h>
//#include <time.h>

#define DUK_USE_OS_STRING "tinspire"
#elif defined(DUK_F_EMSCRIPTEN)
/* --- Emscripten --- */
#if defined(DUK_COMPILING_DUKTAPE)
#if !defined(_POSIX_C_SOURCE)
#define _POSIX_C_SOURCE  200809L
#endif
#if !defined(_GNU_SOURCE)
#define _GNU_SOURCE      /* e.g. getdate_r */
#endif
#if !defined(_XOPEN_SOURCE)
#define _XOPEN_SOURCE    /* e.g. strptime */
#endif
#endif  /* DUK_COMPILING_DUKTAPE */

//#include <sys/types.h>
#if defined(DUK_F_BCC)
/* no endian.h */
#else
//#include <endian.h>
#endif  /* DUK_F_BCC */
//#include <sys/param.h>
//#include <sys/time.h>
//#include <time.h>
//#include <stdint.h>

#define DUK_USE_DATE_NOW_GETTIMEOFDAY
#define DUK_USE_DATE_TZO_GMTIME_R
#define DUK_USE_DATE_PRS_STRPTIME
#define DUK_USE_DATE_FMT_STRFTIME

#define DUK_USE_OS_STRING "emscripten"
#elif defined(DUK_F_ANDROID)
/* --- Android --- */
#if defined(DUK_COMPILING_DUKTAPE)
#if !defined(_POSIX_C_SOURCE)
#define _POSIX_C_SOURCE  200809L
#endif
#if !defined(_GNU_SOURCE)
#define _GNU_SOURCE      /* e.g. getdate_r */
#endif
#if !defined(_XOPEN_SOURCE)
#define _XOPEN_SOURCE    /* e.g. strptime */
#endif
#endif  /* DUK_COMPILING_DUKTAPE */

//#include <sys/types.h>
#if defined(DUK_F_BCC)
/* no endian.h or stdint.h */
#else
//#include <endian.h>
//#include <stdint.h>
#endif  /* DUK_F_BCC */
//#include <sys/param.h>
//#include <sys/time.h>
//#include <time.h>

#define DUK_USE_DATE_NOW_GETTIMEOFDAY
#define DUK_USE_DATE_TZO_GMTIME_R
#define DUK_USE_DATE_PRS_STRPTIME
#define DUK_USE_DATE_FMT_STRFTIME

#if 0  /* XXX: safe condition? */
#define DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME
#endif

#define DUK_USE_OS_STRING "android"
#elif defined(DUK_F_LINUX)
/* --- Linux --- */
#if defined(DUK_COMPILING_DUKTAPE)
#if !defined(_POSIX_C_SOURCE)
#define _POSIX_C_SOURCE  200809L
#endif
#if !defined(_GNU_SOURCE)
#define _GNU_SOURCE      /* e.g. getdate_r */
#endif
#if !defined(_XOPEN_SOURCE)
#define _XOPEN_SOURCE    /* e.g. strptime */
#endif
#endif  /* DUK_COMPILING_DUKTAPE */

//#include <sys/types.h>
#if defined(DUK_F_BCC)
/* no endian.h or stdint.h */
#else
//#include <endian.h>
//#include <stdint.h>
#endif  /* DUK_F_BCC */
//#include <sys/param.h>
//#include <sys/time.h>
//#include <time.h>

#define DUK_USE_DATE_NOW_GETTIMEOFDAY
#define DUK_USE_DATE_TZO_GMTIME_R
#define DUK_USE_DATE_PRS_STRPTIME
#define DUK_USE_DATE_FMT_STRFTIME

#if 0  /* XXX: safe condition? */
#define DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME
#endif

#define DUK_USE_OS_STRING "linux"
#elif defined(DUK_F_SUN)
/* --- Solaris --- */
#define DUK_USE_DATE_NOW_GETTIMEOFDAY
#define DUK_USE_DATE_TZO_GMTIME_R
#define DUK_USE_DATE_PRS_STRPTIME
#define DUK_USE_DATE_FMT_STRFTIME

//#include <sys/types.h>
#if defined(DUK_F_OLD_SOLARIS)
/* Old Solaris with no endian.h, stdint.h */
#define DUK_F_NO_STDINT_H
#if !defined(DUK_USE_BYTEORDER)
#define DUK_USE_BYTEORDER 3
#endif
#else  /* DUK_F_OLD_SOLARIS */
//#include <sys/param.h>
#endif  /* DUK_F_OLD_SOLARIS */

//#include <sys/param.h>
//#include <sys/time.h>
//#include <time.h>

#define DUK_USE_OS_STRING "solaris"
#elif defined(DUK_F_AIX)
/* --- AIX --- */
#if !defined(DUK_USE_BYTEORDER)
#define DUK_USE_BYTEORDER 3
#endif
#define DUK_USE_DATE_NOW_GETTIMEOFDAY
#define DUK_USE_DATE_TZO_GMTIME_R
#define DUK_USE_DATE_PRS_STRPTIME
#define DUK_USE_DATE_FMT_STRFTIME
//#include <sys/param.h>
//#include <sys/time.h>
//#include <time.h>

#define DUK_USE_OS_STRING "aix"
#elif defined(DUK_F_HPUX)
/* --- HPUX --- */
#define DUK_F_NO_STDINT_H
#if !defined(DUK_USE_BYTEORDER)
#define DUK_USE_BYTEORDER 3
#endif
#define DUK_USE_DATE_NOW_GETTIMEOFDAY
#define DUK_USE_DATE_TZO_GMTIME_R
#define DUK_USE_DATE_PRS_STRPTIME
#define DUK_USE_DATE_FMT_STRFTIME
//#include <sys/param.h>
//#include <sys/time.h>
//#include <time.h>

#define DUK_USE_OS_STRING "hpux"
#elif defined(DUK_F_POSIX)
/* --- Generic POSIX --- */
#define DUK_USE_DATE_NOW_GETTIMEOFDAY
#define DUK_USE_DATE_TZO_GMTIME_R
#define DUK_USE_DATE_PRS_STRPTIME
#define DUK_USE_DATE_FMT_STRFTIME
//#include <sys/types.h>
//#include <endian.h>
//#include <sys/param.h>
//#include <sys/time.h>
//#include <time.h>

#define DUK_USE_OS_STRING "posix"
#elif defined(DUK_F_CYGWIN)
/* --- Cygwin --- */
/* don't use strptime() for now */
#define DUK_USE_DATE_NOW_GETTIMEOFDAY
#define DUK_USE_DATE_TZO_GMTIME_R
#define DUK_USE_DATE_FMT_STRFTIME
//#include <sys/types.h>
//#include <endian.h>
//#include <sys/param.h>
//#include <sys/time.h>
//#include <time.h>

#define DUK_JMPBUF_TYPE       jmp_buf
#define DUK_SETJMP(jb)        _setjmp((jb))
#define DUK_LONGJMP(jb)       _longjmp((jb), 1)

#define DUK_USE_OS_STRING "windows"
#elif defined(DUK_F_UNIX)
/* --- Generic UNIX --- */
#define DUK_USE_DATE_NOW_GETTIMEOFDAY
#define DUK_USE_DATE_TZO_GMTIME_R
#define DUK_USE_DATE_PRS_STRPTIME
#define DUK_USE_DATE_FMT_STRFTIME
//#include <time.h>
//#include <sys/time.h>
#define DUK_USE_OS_STRING "unknown"
#else
/* --- Generic fallback --- */
/* The most portable current time provider is time(), but it only has a
 * one second resolution.
 */
#define DUK_USE_DATE_NOW_TIME

/* The most portable way to figure out local time offset is gmtime(),
 * but it's not thread safe so use with caution.
 */
#define DUK_USE_DATE_TZO_GMTIME

/* Avoid custom date parsing and formatting for portability. */
#undef DUK_USE_DATE_PRS_STRPTIME
#undef DUK_USE_DATE_FMT_STRFTIME

/* Rely on C89 headers only; time.h must be here. */
//#include <time.h>

#define DUK_USE_OS_STRING "unknown"
#endif  /* autodetect platform */

/* Shared includes: C89 */
//#include <stdio.h>
//#include <stdlib.h>
//#include <string.h>
//#include <stdarg.h>  /* varargs */
//#include <setjmp.h>
//#include <stddef.h>  /* e.g. ptrdiff_t */
//#include <math.h>
//#include <limits.h>

/* date.h is omitted, and included per platform */

/* Shared includes: stdint.h is C99 */
#if defined(DUK_F_NO_STDINT_H)
/* stdint.h not available */
#else
/* Technically C99 (C++11) but found in many systems.  On some systems
 * __STDC_LIMIT_MACROS and __STDC_CONSTANT_MACROS must be defined before
 * including stdint.h (see above).
 */
//#include <stdint.h>
#endif

/* <exception> is only included if needed, based on DUK_USE_xxx flags. */

/*
 *  Architecture autodetection
 */

#if defined(DUK_F_X86)
/* --- x86 --- */
#define DUK_USE_ARCH_STRING "x86"
#if !defined(DUK_USE_BYTEORDER)
#define DUK_USE_BYTEORDER 1
#endif

#define DUK_USE_PACKED_TVAL

/* FreeBSD, -m32, and clang prior to 5.0 has union aliasing issues which
 * break duk_tval copying.  Disable packed duk_tval automatically.
 */
#if defined(DUK_F_FREEBSD) && defined(DUK_F_X86) && \
    defined(__clang__) && defined(__clang_major__) && (__clang_major__ < 5)
#undef DUK_USE_PACKED_TVAL
#endif
#define DUK_F_PACKED_TVAL_PROVIDED
#elif defined(DUK_F_X64)
/* --- x64 --- */
#define DUK_USE_ARCH_STRING "x64"
#if !defined(DUK_USE_BYTEORDER)
#define DUK_USE_BYTEORDER 1
#endif
#undef DUK_USE_PACKED_TVAL
#define DUK_F_PACKED_TVAL_PROVIDED
#elif defined(DUK_F_X32)
/* --- x32 --- */
#define DUK_USE_ARCH_STRING "x32"
#if !defined(DUK_USE_BYTEORDER)
#define DUK_USE_BYTEORDER 1
#endif
#define DUK_USE_PACKED_TVAL
#define DUK_F_PACKED_TVAL_PROVIDED
#elif defined(DUK_F_ARM32)
/* --- ARM 32-bit --- */
#define DUK_USE_ARCH_STRING "arm32"
/* Byte order varies, so rely on autodetect. */
#define DUK_USE_PACKED_TVAL
#define DUK_F_PACKED_TVAL_PROVIDED
#elif defined(DUK_F_ARM64)
/* --- ARM 64-bit --- */
#define DUK_USE_ARCH_STRING "arm64"
/* Byte order varies, so rely on autodetect. */
#undef DUK_USE_PACKED_TVAL
#define DUK_F_PACKED_TVAL_PROVIDED
#elif defined(DUK_F_MIPS32)
/* --- MIPS 32-bit --- */
#define DUK_USE_ARCH_STRING "mips32"
/* MIPS byte order varies so rely on autodetection. */
#define DUK_USE_PACKED_TVAL
#define DUK_F_PACKED_TVAL_PROVIDED
#elif defined(DUK_F_MIPS64)
/* --- MIPS 64-bit --- */
#define DUK_USE_ARCH_STRING "mips64"
/* MIPS byte order varies so rely on autodetection. */
#undef DUK_USE_PACKED_TVAL
#define DUK_F_PACKED_TVAL_PROVIDED
#elif defined(DUK_F_PPC32)
/* --- PowerPC 32-bit --- */
#define DUK_USE_ARCH_STRING "ppc32"
#if !defined(DUK_USE_BYTEORDER)
#define DUK_USE_BYTEORDER 3
#endif
#define DUK_USE_PACKED_TVAL
#define DUK_F_PACKED_TVAL_PROVIDED
#elif defined(DUK_F_PPC64)
/* --- PowerPC 64-bit --- */
#define DUK_USE_ARCH_STRING "ppc64"
/* No forced byteorder (both little and big endian are possible). */
#undef DUK_USE_PACKED_TVAL
#define DUK_F_PACKED_TVAL_PROVIDED
#elif defined(DUK_F_SPARC32)
/* --- SPARC 32-bit --- */
#define DUK_USE_ARCH_STRING "sparc32"
/* SPARC byte order varies so rely on autodetection. */
#define DUK_USE_PACKED_TVAL
#define DUK_F_PACKED_TVAL_PROVIDED
#elif defined(DUK_F_SPARC64)
/* --- SPARC 64-bit --- */
#define DUK_USE_ARCH_STRING "sparc64"
/* SPARC byte order varies so rely on autodetection. */
#undef DUK_USE_PACKED_TVAL
#define DUK_F_PACKED_TVAL_PROVIDED
#elif defined(DUK_F_RISCV32)
/* --- RISC-V 32-bit --- */
#define DUK_USE_ARCH_STRING "riscv32"
#define DUK_USE_BYTEORDER 1
#define DUK_USE_PACKED_TVAL
#define DUK_F_PACKED_TVAL_PROVIDED
#elif defined(DUK_F_RISCV64)
/* --- RISC-V 64-bit --- */
#define DUK_USE_ARCH_STRING "riscv64"
#define DUK_USE_BYTEORDER 1
#undef DUK_USE_PACKED_TVAL
#define DUK_F_PACKED_TVAL_PROVIDED
#elif defined(DUK_F_SUPERH)
/* --- SuperH --- */
#define DUK_USE_ARCH_STRING "sh"
/* Byte order varies, rely on autodetection. */
#define DUK_USE_PACKED_TVAL
#define DUK_F_PACKED_TVAL_PROVIDED
#elif defined(DUK_F_M68K)
/* --- Motorola 68k --- */
#define DUK_USE_ARCH_STRING "m68k"
#if !defined(DUK_USE_BYTEORDER)
#define DUK_USE_BYTEORDER 3
#endif
#define DUK_USE_PACKED_TVAL
#define DUK_F_PACKED_TVAL_PROVIDED
#elif defined(DUK_F_EMSCRIPTEN)
/* --- Emscripten --- */
#define DUK_USE_ARCH_STRING "emscripten"
#if !defined(DUK_USE_BYTEORDER)
#define DUK_USE_BYTEORDER 1
#endif
#undef DUK_USE_PACKED_TVAL
#define DUK_F_PACKED_TVAL_PROVIDED
#else
/* --- Generic --- */
/* These are necessary wild guesses. */
#define DUK_USE_ARCH_STRING "generic"
/* Rely on autodetection for byte order, alignment, and packed tval. */
#endif  /* autodetect architecture */

/*
 *  Compiler autodetection
 */

#if defined(DUK_F_CLANG)
/* --- Clang --- */
#if defined(DUK_F_C99) || defined(DUK_F_CPP11)
/* C99 / C++11 and above: rely on va_copy() which is required. */
#define DUK_VA_COPY(dest,src) va_copy(dest,src)
#else
/* Clang: assume we have __va_copy() in non-C99 mode. */
#define DUK_VA_COPY(dest,src) __va_copy(dest,src)
#endif

#define DUK_NORETURN(decl)  decl __attribute__((noreturn))

#if defined(__clang__) && defined(__has_builtin)
#if __has_builtin(__builtin_unreachable)
#define DUK_UNREACHABLE()  do { __builtin_unreachable(); } while (0)
#endif
#endif

#define DUK_USE_BRANCH_HINTS
#define DUK_LIKELY(x)    __builtin_expect((x), 1)
#define DUK_UNLIKELY(x)  __builtin_expect((x), 0)
#if defined(__clang__) && defined(__has_builtin)
#if __has_builtin(__builtin_unpredictable)
#define DUK_UNPREDICTABLE(x)  __builtin_unpredictable((x))
#endif
#endif

#if defined(DUK_F_C99) || defined(DUK_F_CPP11)
#define DUK_NOINLINE        __attribute__((noinline))
#define DUK_INLINE          inline
#define DUK_ALWAYS_INLINE   inline __attribute__((always_inline))
#endif

/* DUK_HOT */
/* DUK_COLD */

#if defined(DUK_F_DLL_BUILD) && defined(DUK_F_WINDOWS)
/* MSVC dllexport/dllimport: appropriate __declspec depends on whether we're
 * compiling Duktape or the application.
 */
#if defined(DUK_COMPILING_DUKTAPE)
#define DUK_EXTERNAL_DECL  extern __declspec(dllexport)
#define DUK_EXTERNAL       __declspec(dllexport)
#else
#define DUK_EXTERNAL_DECL  extern __declspec(dllimport)
#define DUK_EXTERNAL       should_not_happen
#endif
#if defined(DUK_SINGLE_FILE)
#define DUK_INTERNAL_DECL  static
#define DUK_INTERNAL       static
#else
#define DUK_INTERNAL_DECL  extern
#define DUK_INTERNAL       /*empty*/
#endif
#define DUK_LOCAL_DECL     static
#define DUK_LOCAL          static
#else
#define DUK_EXTERNAL_DECL  __attribute__ ((visibility("default"))) extern
#define DUK_EXTERNAL       __attribute__ ((visibility("default")))
#if defined(DUK_SINGLE_FILE)
#if (defined(DUK_F_GCC_VERSION) && DUK_F_GCC_VERSION >= 30101) || defined(DUK_F_CLANG)
/* Minimize warnings for unused internal functions with GCC >= 3.1.1 and
 * Clang.  Based on documentation it should suffice to have the attribute
 * in the declaration only, but in practice some warnings are generated unless
 * the attribute is also applied to the definition.
 */
#define DUK_INTERNAL_DECL  static __attribute__ ((unused))
#define DUK_INTERNAL       static __attribute__ ((unused))
#else
#define DUK_INTERNAL_DECL  static
#define DUK_INTERNAL       static
#endif
#else
#if (defined(DUK_F_GCC_VERSION) && DUK_F_GCC_VERSION >= 30101) || defined(DUK_F_CLANG)
#define DUK_INTERNAL_DECL  __attribute__ ((visibility("hidden"))) __attribute__ ((unused)) extern
#define DUK_INTERNAL       __attribute__ ((visibility("hidden"))) __attribute__ ((unused))
#else
#define DUK_INTERNAL_DECL  __attribute__ ((visibility("hidden"))) extern
#define DUK_INTERNAL       __attribute__ ((visibility("hidden")))
#endif
#endif
#define DUK_LOCAL_DECL     static
#define DUK_LOCAL          static
#endif

#if defined(DUK_F_CPP)
#define DUK_USE_COMPILER_STRING "clang"
#else
#define DUK_USE_COMPILER_STRING "clang"
#endif

#undef DUK_USE_VARIADIC_MACROS
#if defined(DUK_F_C99) || defined(DUK_F_CPP11)
#define DUK_USE_VARIADIC_MACROS
#endif

#define DUK_USE_UNION_INITIALIZERS

#undef DUK_USE_FLEX_C99
#undef DUK_USE_FLEX_ZEROSIZE
#undef DUK_USE_FLEX_ONESIZE
#if defined(DUK_F_C99)
#define DUK_USE_FLEX_C99
#else
#define DUK_USE_FLEX_ZEROSIZE
#endif

#define DUK_USE_CLANG_PRAGMAS
#define DUK_USE_PACK_CLANG_ATTR

#if defined(__clang__) && defined(__has_builtin)
#if __has_builtin(__builtin_bswap64)
#define DUK_BSWAP64(x) ((duk_uint64_t) __builtin_bswap64((duk_uint64_t) (x)))
#endif
#if __has_builtin(__builtin_bswap32)
#define DUK_BSWAP32(x) ((duk_uint32_t) __builtin_bswap32((duk_uint32_t) (x)))
#endif
#if __has_builtin(__builtin_bswap16)
#define DUK_BSWAP16(x) ((duk_uint16_t) __builtin_bswap16((duk_uint16_t) (x)))
#endif
#endif
#elif defined(DUK_F_GCC)
/* --- GCC --- */
#if defined(DUK_F_C99) || defined(DUK_F_CPP11)
/* C99 / C++11 and above: rely on va_copy() which is required. */
#define DUK_VA_COPY(dest,src) va_copy(dest,src)
#else
/* GCC: assume we have __va_copy() in non-C99 mode. */
#define DUK_VA_COPY(dest,src) __va_copy(dest,src)
#endif

#if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 20500L) && (DUK_F_GCC_VERSION < 50000L)
/* Since gcc-2.5.
 *
 * Disabled temporarily in GCC 5+ because of an unresolved noreturn-related
 * issue: https://github.com/svaarala/duktape/issues/2155.
 */
#define DUK_NORETURN(decl)  decl __attribute__((noreturn))
#endif

#if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 40500L)
/* Since gcc-4.5. */
#define DUK_UNREACHABLE()  do { __builtin_unreachable(); } while (0)
#endif

#define DUK_USE_BRANCH_HINTS
#if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 40500L)
/* GCC: test not very accurate; enable only in relatively recent builds
 * because of bugs in gcc-4.4 (http://lists.debian.org/debian-gcc/2010/04/msg00000.html)
 */
#define DUK_LIKELY(x)    __builtin_expect((x), 1)
#define DUK_UNLIKELY(x)  __builtin_expect((x), 0)
#endif
/* XXX: equivalent of clang __builtin_unpredictable? */

#if (defined(DUK_F_C99) || defined(DUK_F_CPP11)) && \
    defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 30101)
#define DUK_NOINLINE        __attribute__((noinline))
#define DUK_INLINE          inline
#define DUK_ALWAYS_INLINE   inline __attribute__((always_inline))
#endif

#if (defined(DUK_F_C99) || defined(DUK_F_CPP11)) && \
    defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 40300)
#define DUK_HOT             __attribute__((hot))
#define DUK_COLD            __attribute__((cold))
#endif

#if defined(DUK_F_DLL_BUILD) && defined(DUK_F_WINDOWS)
/* MSVC dllexport/dllimport: appropriate __declspec depends on whether we're
 * compiling Duktape or the application.
 */
#if defined(DUK_COMPILING_DUKTAPE)
#define DUK_EXTERNAL_DECL  extern __declspec(dllexport)
#define DUK_EXTERNAL       __declspec(dllexport)
#else
#define DUK_EXTERNAL_DECL  extern __declspec(dllimport)
#define DUK_EXTERNAL       should_not_happen
#endif
#if defined(DUK_SINGLE_FILE)
#define DUK_INTERNAL_DECL  static
#define DUK_INTERNAL       static
#else
#define DUK_INTERNAL_DECL  extern
#define DUK_INTERNAL       /*empty*/
#endif
#define DUK_LOCAL_DECL     static
#define DUK_LOCAL          static
#elif defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 40000)
#define DUK_EXTERNAL_DECL  __attribute__ ((visibility("default"))) extern
#define DUK_EXTERNAL       __attribute__ ((visibility("default")))
#if defined(DUK_SINGLE_FILE)
#if (defined(DUK_F_GCC_VERSION) && DUK_F_GCC_VERSION >= 30101) || defined(DUK_F_CLANG)
/* Minimize warnings for unused internal functions with GCC >= 3.1.1 and
 * Clang.  Based on documentation it should suffice to have the attribute
 * in the declaration only, but in practice some warnings are generated unless
 * the attribute is also applied to the definition.
 */
#define DUK_INTERNAL_DECL  static __attribute__ ((unused))
#define DUK_INTERNAL       static __attribute__ ((unused))
#else
#define DUK_INTERNAL_DECL  static
#define DUK_INTERNAL       static
#endif
#else
#if (defined(DUK_F_GCC_VERSION) && DUK_F_GCC_VERSION >= 30101) || defined(DUK_F_CLANG)
#define DUK_INTERNAL_DECL  __attribute__ ((visibility("hidden"))) __attribute__ ((unused)) extern
#define DUK_INTERNAL       __attribute__ ((visibility("hidden"))) __attribute__ ((unused))
#else
#define DUK_INTERNAL_DECL  __attribute__ ((visibility("hidden"))) extern
#define DUK_INTERNAL       __attribute__ ((visibility("hidden")))
#endif
#endif
#define DUK_LOCAL_DECL     static
#define DUK_LOCAL          static
#endif

#if defined(DUK_F_MINGW)
#if defined(DUK_F_CPP)
#define DUK_USE_COMPILER_STRING "mingw++"
#else
#define DUK_USE_COMPILER_STRING "mingw"
#endif
#else
#if defined(DUK_F_CPP)
#define DUK_USE_COMPILER_STRING "g++"
#else
#define DUK_USE_COMPILER_STRING "gcc"
#endif
#endif

#undef DUK_USE_VARIADIC_MACROS
#if defined(DUK_F_C99) || (defined(DUK_F_CPP11) && defined(__GNUC__))
#define DUK_USE_VARIADIC_MACROS
#endif

#define DUK_USE_UNION_INITIALIZERS

#undef DUK_USE_FLEX_C99
#undef DUK_USE_FLEX_ZEROSIZE
#undef DUK_USE_FLEX_ONESIZE
#if defined(DUK_F_C99)
#define DUK_USE_FLEX_C99
#else
#define DUK_USE_FLEX_ZEROSIZE
#endif

/* Since 4.6 one can '#pragma GCC diagnostic push/pop'. */
#if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 40600)
#define DUK_USE_GCC_PRAGMAS
#else
#undef DUK_USE_GCC_PRAGMAS
#endif

#define DUK_USE_PACK_GCC_ATTR

/* Availability varies based on platform (between GCC 4.4 and 4.8), and there
 * are apparently some bugs in GCC 4.x.  Check for GCC 5.0 before enabling
 * these to be safe.
 */
#if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 50000L)
#define DUK_BSWAP64(x) ((duk_uint64_t) __builtin_bswap64((duk_uint64_t) (x)))
#define DUK_BSWAP32(x) ((duk_uint32_t) __builtin_bswap32((duk_uint32_t) (x)))
#define DUK_BSWAP16(x) ((duk_uint16_t) __builtin_bswap16((duk_uint16_t) (x)))
#endif
#elif defined(DUK_F_MSVC)
/* --- MSVC --- */
/* http://msdn.microsoft.com/en-us/library/aa235362(VS.60).aspx */
#define DUK_NORETURN(decl)  __declspec(noreturn) decl

/* XXX: DUK_UNREACHABLE for msvc? */

#undef DUK_USE_BRANCH_HINTS

/* XXX: DUK_LIKELY, DUK_UNLIKELY for msvc? */
/* XXX: DUK_NOINLINE, DUK_INLINE, DUK_ALWAYS_INLINE for msvc? */

#if defined(DUK_F_DLL_BUILD) && defined(DUK_F_WINDOWS)
/* MSVC dllexport/dllimport: appropriate __declspec depends on whether we're
 * compiling Duktape or the application.
 */
#if defined(DUK_COMPILING_DUKTAPE)
#define DUK_EXTERNAL_DECL  extern __declspec(dllexport)
#define DUK_EXTERNAL       __declspec(dllexport)
#else
#define DUK_EXTERNAL_DECL  extern __declspec(dllimport)
#define DUK_EXTERNAL       should_not_happen
#endif
#if defined(DUK_SINGLE_FILE)
#define DUK_INTERNAL_DECL  static
#define DUK_INTERNAL       static
#else
#define DUK_INTERNAL_DECL  extern
#define DUK_INTERNAL       /*empty*/
#endif
#define DUK_LOCAL_DECL     static
#define DUK_LOCAL          static
#endif

#if defined(DUK_F_CPP)
#define DUK_USE_COMPILER_STRING "msvc++"
#else
#define DUK_USE_COMPILER_STRING "msvc"
#endif

#undef DUK_USE_VARIADIC_MACROS
#if defined(DUK_F_C99)
#define DUK_USE_VARIADIC_MACROS
#elif defined(_MSC_VER) && (_MSC_VER >= 1400)
/* VS2005+ should have variadic macros even when they're not C99. */
#define DUK_USE_VARIADIC_MACROS
#endif

#undef DUK_USE_UNION_INITIALIZERS
#if defined(_MSC_VER) && (_MSC_VER >= 1800)
/* VS2013+ supports union initializers but there's a bug involving union-inside-struct:
 * https://connect.microsoft.com/VisualStudio/feedback/details/805981
 * The bug was fixed (at least) in VS2015 so check for VS2015 for now:
 * https://blogs.msdn.microsoft.com/vcblog/2015/07/01/c-compiler-front-end-fixes-in-vs2015/
 * Manually tested using VS2013, CL reports 18.00.31101, so enable for VS2013 too.
 */
#define DUK_USE_UNION_INITIALIZERS
#endif

#undef DUK_USE_FLEX_C99
#undef DUK_USE_FLEX_ZEROSIZE
#undef DUK_USE_FLEX_ONESIZE
#if defined(DUK_F_C99)
#define DUK_USE_FLEX_C99
#else
#define DUK_USE_FLEX_ZEROSIZE
#endif

#undef DUK_USE_GCC_PRAGMAS

#define DUK_USE_PACK_MSVC_PRAGMA

/* These have been tested from VS2008 onwards; may work in older VS versions
 * too but not enabled by default.
 */
#if defined(_MSC_VER) && (_MSC_VER >= 1500)
#define DUK_NOINLINE        __declspec(noinline)
#define DUK_INLINE          __inline
#define DUK_ALWAYS_INLINE   __forceinline
#endif

#if defined(_MSC_VER) && (_MSC_VER >= 1900)
#define DUK_SNPRINTF     snprintf
#define DUK_VSNPRINTF    vsnprintf
#else
/* (v)snprintf() is missing before MSVC 2015.  Note that _(v)snprintf() does
 * NOT NUL terminate on truncation, but Duktape code never assumes that.
 * http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
 */
#define DUK_SNPRINTF     _snprintf
#define DUK_VSNPRINTF    _vsnprintf
#endif

/* Avoid warning when doing DUK_UNREF(some_function). */
#if defined(_MSC_VER) && (_MSC_VER < 1500)
#pragma warning(disable: 4100 4101 4550 4551)
#define DUK_UNREF(x)
#else
#define DUK_UNREF(x)  do { __pragma(warning(suppress:4100 4101 4550 4551)) (x); } while (0)
#endif

/* Older versions of MSVC don't support the LL/ULL suffix. */
#define DUK_U64_CONSTANT(x) x##ui64
#define DUK_I64_CONSTANT(x) x##i64
#elif defined(DUK_F_EMSCRIPTEN)
/* --- Emscripten --- */
#define DUK_NORETURN(decl)  decl __attribute__((noreturn))

#if defined(__clang__) && defined(__has_builtin)
#if __has_builtin(__builtin_unreachable)
#define DUK_UNREACHABLE()  do { __builtin_unreachable(); } while (0)
#endif
#endif

#define DUK_USE_BRANCH_HINTS
#define DUK_LIKELY(x)    __builtin_expect((x), 1)
#define DUK_UNLIKELY(x)  __builtin_expect((x), 0)
#if defined(__clang__) && defined(__has_builtin)
#if __has_builtin(__builtin_unpredictable)
#define DUK_UNPREDICTABLE(x)  __builtin_unpredictable((x))
#endif
#endif

#if defined(DUK_F_C99) || defined(DUK_F_CPP11)
#define DUK_NOINLINE        __attribute__((noinline))
#define DUK_INLINE          inline
#define DUK_ALWAYS_INLINE   inline __attribute__((always_inline))
#endif

#define DUK_EXTERNAL_DECL  __attribute__ ((visibility("default"))) extern
#define DUK_EXTERNAL       __attribute__ ((visibility("default")))
#if defined(DUK_SINGLE_FILE)
#if (defined(DUK_F_GCC_VERSION) && DUK_F_GCC_VERSION >= 30101) || defined(DUK_F_CLANG)
/* Minimize warnings for unused internal functions with GCC >= 3.1.1 and
 * Clang.  Based on documentation it should suffice to have the attribute
 * in the declaration only, but in practice some warnings are generated unless
 * the attribute is also applied to the definition.
 */
#define DUK_INTERNAL_DECL  static __attribute__ ((unused))
#define DUK_INTERNAL       static __attribute__ ((unused))
#else
#define DUK_INTERNAL_DECL  static
#define DUK_INTERNAL       static
#endif
#else
#if (defined(DUK_F_GCC_VERSION) && DUK_F_GCC_VERSION >= 30101) || defined(DUK_F_CLANG)
#define DUK_INTERNAL_DECL  __attribute__ ((visibility("hidden"))) __attribute__ ((unused)) extern
#define DUK_INTERNAL       __attribute__ ((visibility("hidden"))) __attribute__ ((unused))
#else
#define DUK_INTERNAL_DECL  __attribute__ ((visibility("hidden"))) extern
#define DUK_INTERNAL       __attribute__ ((visibility("hidden")))
#endif
#endif
#define DUK_LOCAL_DECL     static
#define DUK_LOCAL          static

#define DUK_USE_COMPILER_STRING "emscripten"

#undef DUK_USE_VARIADIC_MACROS
#if defined(DUK_F_C99) || defined(DUK_F_CPP11)
#define DUK_USE_VARIADIC_MACROS
#endif

#define DUK_USE_UNION_INITIALIZERS

#undef DUK_USE_FLEX_C99
#undef DUK_USE_FLEX_ZEROSIZE
#undef DUK_USE_FLEX_ONESIZE
#if defined(DUK_F_C99)
#define DUK_USE_FLEX_C99
#else
#define DUK_USE_FLEX_ZEROSIZE
#endif

#undef DUK_USE_GCC_PRAGMAS
#define DUK_USE_PACK_CLANG_ATTR
#elif defined(DUK_F_TINYC)
/* --- TinyC --- */
#undef DUK_USE_BRANCH_HINTS

#if defined(DUK_F_CPP)
#define DUK_USE_COMPILER_STRING "tinyc++"
#else
#define DUK_USE_COMPILER_STRING "tinyc"
#endif

/* http://bellard.org/tcc/tcc-doc.html#SEC7 */
#define DUK_USE_VARIADIC_MACROS

#define DUK_USE_UNION_INITIALIZERS

/* Most portable, wastes space */
#define DUK_USE_FLEX_ONESIZE

/* Most portable, potentially wastes space */
#define DUK_USE_PACK_DUMMY_MEMBER
#elif defined(DUK_F_VBCC)
/* --- VBCC --- */
#undef DUK_USE_BRANCH_HINTS

#if defined(DUK_F_CPP)
#define DUK_USE_COMPILER_STRING "vbcc-c++"
#else
#define DUK_USE_COMPILER_STRING "vbcc"
#endif

#undef DUK_USE_VARIADIC_MACROS
#if defined(DUK_F_C99) || defined(DUK_F_CPP11)
#define DUK_USE_VARIADIC_MACROS
#endif

/* VBCC supports C99 so check only for C99 for union initializer support.
 * Designated union initializers would possibly work even without a C99 check.
 */
#undef DUK_USE_UNION_INITIALIZERS
#if defined(DUK_F_C99)
#define DUK_USE_UNION_INITIALIZERS
#endif

#define DUK_USE_FLEX_ZEROSIZE
#define DUK_USE_PACK_DUMMY_MEMBER
#elif defined(DUK_F_BCC)
/* --- Bruce's C compiler --- */
#undef DUK_USE_BRANCH_HINTS

#if defined(DUK_F_CPP)
#define DUK_USE_COMPILER_STRING "bcc++"
#else
#define DUK_USE_COMPILER_STRING "bcc"
#endif

/* Most portable */
#undef DUK_USE_VARIADIC_MACROS

/* Most portable, wastes space */
#undef DUK_USE_UNION_INITIALIZERS

/* Most portable, wastes space */
#define DUK_USE_FLEX_ONESIZE

/* Most portable, potentially wastes space */
#define DUK_USE_PACK_DUMMY_MEMBER

/* BCC, assume we're on x86. */
#if !defined(DUK_USE_BYTEORDER)
#define DUK_USE_BYTEORDER 1
#endif
#else
/* --- Generic --- */
#undef DUK_USE_BRANCH_HINTS

#if defined(DUK_F_CPP)
#define DUK_USE_COMPILER_STRING "generic-c++"
#else
#define DUK_USE_COMPILER_STRING "generic"
#endif

#undef DUK_USE_VARIADIC_MACROS
#if defined(DUK_F_C99) || defined(DUK_F_CPP11)
#define DUK_USE_VARIADIC_MACROS
#endif

/* C++ doesn't have standard designated union initializers ({ .foo = 1 }). */
#undef DUK_USE_UNION_INITIALIZERS
#if defined(DUK_F_C99)
#define DUK_USE_UNION_INITIALIZERS
#endif

/* Most portable, wastes space */
#define DUK_USE_FLEX_ONESIZE

/* Most portable, potentially wastes space */
#define DUK_USE_PACK_DUMMY_MEMBER
#endif  /* autodetect compiler */

/* uclibc */
#if defined(__UCLIBC__)
#define DUK_F_UCLIBC
#endif

/*
 *  Wrapper typedefs and constants for integer types, also sanity check types.
 *
 *  C99 typedefs are quite good but not always available, and we want to avoid
 *  forcibly redefining the C99 typedefs.  So, there are Duktape wrappers for
 *  all C99 typedefs and Duktape code should only use these typedefs.  Type
 *  detection when C99 is not supported is best effort and may end up detecting
 *  some types incorrectly.
 *
 *  Pointer sizes are a portability problem: pointers to different types may
 *  have a different size and function pointers are very difficult to manage
 *  portably.
 *
 *  http://en.wikipedia.org/wiki/C_data_types#Fixed-width_integer_types
 *
 *  Note: there's an interesting corner case when trying to define minimum
 *  signed integer value constants which leads to the current workaround of
 *  defining e.g. -0x80000000 as (-0x7fffffffL - 1L).  See doc/code-issues.txt
 *  for a longer discussion.
 *
 *  Note: avoid typecasts and computations in macro integer constants as they
 *  can then no longer be used in macro relational expressions (such as
 *  #if DUK_SIZE_MAX < 0xffffffffUL).  There is internal code which relies on
 *  being able to compare DUK_SIZE_MAX against a limit.
 */

/* XXX: add feature options to force basic types from outside? */

#if !defined(INT_MAX)
#error INT_MAX not defined
#endif

/* Check that architecture is two's complement, standard C allows e.g.
 * INT_MIN to be -2**31+1 (instead of -2**31).
 */
#if defined(INT_MAX) && defined(INT_MIN)
#if INT_MAX != -(INT_MIN + 1)
#error platform does not seem complement of two
#endif
#else
#error cannot check complement of two
#endif

/* Pointer size determination based on __WORDSIZE or architecture when
 * that's not available.
 */
#if defined(DUK_F_X86) || defined(DUK_F_X32) || \
    defined(DUK_F_M68K) || defined(DUK_F_PPC32) || \
    defined(DUK_F_BCC) || \
    (defined(__WORDSIZE) && (__WORDSIZE == 32)) || \
    ((defined(DUK_F_OLD_SOLARIS) || defined(DUK_F_AIX) || \
      defined(DUK_F_HPUX)) && defined(_ILP32)) || \
    defined(DUK_F_ARM32)
#define DUK_F_32BIT_PTRS
#elif defined(DUK_F_X64) || \
      (defined(__WORDSIZE) && (__WORDSIZE == 64)) || \
   ((defined(DUK_F_OLD_SOLARIS) || defined(DUK_F_AIX) || \
     defined(DUK_F_HPUX)) && defined(_LP64)) || \
    defined(DUK_F_ARM64)
#define DUK_F_64BIT_PTRS
#else
/* not sure, not needed with C99 anyway */
#endif

/* Intermediate define for 'have inttypes.h' */
#undef DUK_F_HAVE_INTTYPES
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
    !(defined(DUK_F_AMIGAOS) && defined(DUK_F_VBCC))
/* vbcc + AmigaOS has C99 but no inttypes.h */
#define DUK_F_HAVE_INTTYPES
#elif defined(__cplusplus) && (__cplusplus >= 201103L)
/* C++11 apparently ratified stdint.h */
#define DUK_F_HAVE_INTTYPES
#endif

/* Basic integer typedefs and limits, preferably from inttypes.h, otherwise
 * through automatic detection.
 */
#if defined(DUK_F_HAVE_INTTYPES)
/* C99 or compatible */

#define DUK_F_HAVE_64BIT
//#include <inttypes.h>

typedef uint8_t duk_uint8_t;
typedef int8_t duk_int8_t;
typedef uint16_t duk_uint16_t;
typedef int16_t duk_int16_t;
typedef uint32_t duk_uint32_t;
typedef int32_t duk_int32_t;
typedef uint64_t duk_uint64_t;
typedef int64_t duk_int64_t;
typedef uint_least8_t duk_uint_least8_t;
typedef int_least8_t duk_int_least8_t;
typedef uint_least16_t duk_uint_least16_t;
typedef int_least16_t duk_int_least16_t;
typedef uint_least32_t duk_uint_least32_t;
typedef int_least32_t duk_int_least32_t;
typedef uint_least64_t duk_uint_least64_t;
typedef int_least64_t duk_int_least64_t;
typedef uint_fast8_t duk_uint_fast8_t;
typedef int_fast8_t duk_int_fast8_t;
typedef uint_fast16_t duk_uint_fast16_t;
typedef int_fast16_t duk_int_fast16_t;
typedef uint_fast32_t duk_uint_fast32_t;
typedef int_fast32_t duk_int_fast32_t;
typedef uint_fast64_t duk_uint_fast64_t;
typedef int_fast64_t duk_int_fast64_t;
typedef uintptr_t duk_uintptr_t;
typedef intptr_t duk_intptr_t;
typedef uintmax_t duk_uintmax_t;
typedef intmax_t duk_intmax_t;

#define DUK_UINT8_MIN         0
#define DUK_UINT8_MAX         UINT8_MAX
#define DUK_INT8_MIN          INT8_MIN
#define DUK_INT8_MAX          INT8_MAX
#define DUK_UINT_LEAST8_MIN   0
#define DUK_UINT_LEAST8_MAX   UINT_LEAST8_MAX
#define DUK_INT_LEAST8_MIN    INT_LEAST8_MIN
#define DUK_INT_LEAST8_MAX    INT_LEAST8_MAX
#define DUK_UINT_FAST8_MIN    0
#define DUK_UINT_FAST8_MAX    UINT_FAST8_MAX
#define DUK_INT_FAST8_MIN     INT_FAST8_MIN
#define DUK_INT_FAST8_MAX     INT_FAST8_MAX
#define DUK_UINT16_MIN        0
#define DUK_UINT16_MAX        UINT16_MAX
#define DUK_INT16_MIN         INT16_MIN
#define DUK_INT16_MAX         INT16_MAX
#define DUK_UINT_LEAST16_MIN  0
#define DUK_UINT_LEAST16_MAX  UINT_LEAST16_MAX
#define DUK_INT_LEAST16_MIN   INT_LEAST16_MIN
#define DUK_INT_LEAST16_MAX   INT_LEAST16_MAX
#define DUK_UINT_FAST16_MIN   0
#define DUK_UINT_FAST16_MAX   UINT_FAST16_MAX
#define DUK_INT_FAST16_MIN    INT_FAST16_MIN
#define DUK_INT_FAST16_MAX    INT_FAST16_MAX
#define DUK_UINT32_MIN        0
#define DUK_UINT32_MAX        UINT32_MAX
#define DUK_INT32_MIN         INT32_MIN
#define DUK_INT32_MAX         INT32_MAX
#define DUK_UINT_LEAST32_MIN  0
#define DUK_UINT_LEAST32_MAX  UINT_LEAST32_MAX
#define DUK_INT_LEAST32_MIN   INT_LEAST32_MIN
#define DUK_INT_LEAST32_MAX   INT_LEAST32_MAX
#define DUK_UINT_FAST32_MIN   0
#define DUK_UINT_FAST32_MAX   UINT_FAST32_MAX
#define DUK_INT_FAST32_MIN    INT_FAST32_MIN
#define DUK_INT_FAST32_MAX    INT_FAST32_MAX
#define DUK_UINT64_MIN        0
#define DUK_UINT64_MAX        UINT64_MAX
#define DUK_INT64_MIN         INT64_MIN
#define DUK_INT64_MAX         INT64_MAX
#define DUK_UINT_LEAST64_MIN  0
#define DUK_UINT_LEAST64_MAX  UINT_LEAST64_MAX
#define DUK_INT_LEAST64_MIN   INT_LEAST64_MIN
#define DUK_INT_LEAST64_MAX   INT_LEAST64_MAX
#define DUK_UINT_FAST64_MIN   0
#define DUK_UINT_FAST64_MAX   UINT_FAST64_MAX
#define DUK_INT_FAST64_MIN    INT_FAST64_MIN
#define DUK_INT_FAST64_MAX    INT_FAST64_MAX

#define DUK_UINTPTR_MIN       0
#define DUK_UINTPTR_MAX       UINTPTR_MAX
#define DUK_INTPTR_MIN        INTPTR_MIN
#define DUK_INTPTR_MAX        INTPTR_MAX

#define DUK_UINTMAX_MIN       0
#define DUK_UINTMAX_MAX       UINTMAX_MAX
#define DUK_INTMAX_MIN        INTMAX_MIN
#define DUK_INTMAX_MAX        INTMAX_MAX

#define DUK_SIZE_MIN          0
#define DUK_SIZE_MAX          SIZE_MAX
#undef DUK_SIZE_MAX_COMPUTED

#else  /* C99 types */

/* When C99 types are not available, we use heuristic detection to get
 * the basic 8, 16, 32, and (possibly) 64 bit types.  The fast/least
 * types are then assumed to be exactly the same for now: these could
 * be improved per platform but C99 types are very often now available.
 * 64-bit types are not available on all platforms; this is OK at least
 * on 32-bit platforms.
 *
 * This detection code is necessarily a bit hacky and can provide typedefs
 * and defines that won't work correctly on some exotic platform.
 */

#if (defined(CHAR_BIT) && (CHAR_BIT == 8)) || \
    (defined(UCHAR_MAX) && (UCHAR_MAX == 255))
typedef unsigned char duk_uint8_t;
typedef signed char duk_int8_t;
#else
#error cannot detect 8-bit type
#endif

#if defined(USHRT_MAX) && (USHRT_MAX == 65535UL)
typedef unsigned short duk_uint16_t;
typedef signed short duk_int16_t;
#elif defined(UINT_MAX) && (UINT_MAX == 65535UL)
/* On some platforms int is 16-bit but long is 32-bit (e.g. PureC) */
typedef unsigned int duk_uint16_t;
typedef signed int duk_int16_t;
#else
#error cannot detect 16-bit type
#endif

#if defined(UINT_MAX) && (UINT_MAX == 4294967295UL)
typedef unsigned int duk_uint32_t;
typedef signed int duk_int32_t;
#elif defined(ULONG_MAX) && (ULONG_MAX == 4294967295UL)
/* On some platforms int is 16-bit but long is 32-bit (e.g. PureC) */
typedef unsigned long duk_uint32_t;
typedef signed long duk_int32_t;
#else
#error cannot detect 32-bit type
#endif

/* 64-bit type detection is a bit tricky.
 *
 * ULLONG_MAX is a standard define.  __LONG_LONG_MAX__ and __ULONG_LONG_MAX__
 * are used by at least GCC (even if system headers don't provide ULLONG_MAX).
 * Some GCC variants may provide __LONG_LONG_MAX__ but not __ULONG_LONG_MAX__.
 *
 * ULL / LL constants are rejected / warned about by some compilers, even if
 * the compiler has a 64-bit type and the compiler/system headers provide an
 * unsupported constant (ULL/LL)!  Try to avoid using ULL / LL constants.
 * As a side effect we can only check that e.g. ULONG_MAX is larger than 32
 * bits but can't be sure it is exactly 64 bits.  Self tests will catch such
 * cases.
 */
#undef DUK_F_HAVE_64BIT
#if !defined(DUK_F_HAVE_64BIT) && defined(ULONG_MAX)
#if (ULONG_MAX > 4294967295UL)
#define DUK_F_HAVE_64BIT
typedef unsigned long duk_uint64_t;
typedef signed long duk_int64_t;
#endif
#endif
#if !defined(DUK_F_HAVE_64BIT) && defined(ULLONG_MAX)
#if (ULLONG_MAX > 4294967295UL)
#define DUK_F_HAVE_64BIT
typedef unsigned long long duk_uint64_t;
typedef signed long long duk_int64_t;
#endif
#endif
#if !defined(DUK_F_HAVE_64BIT) && defined(__ULONG_LONG_MAX__)
#if (__ULONG_LONG_MAX__ > 4294967295UL)
#define DUK_F_HAVE_64BIT
typedef unsigned long long duk_uint64_t;
typedef signed long long duk_int64_t;
#endif
#endif
#if !defined(DUK_F_HAVE_64BIT) && defined(__LONG_LONG_MAX__)
#if (__LONG_LONG_MAX__ > 2147483647L)
#define DUK_F_HAVE_64BIT
typedef unsigned long long duk_uint64_t;
typedef signed long long duk_int64_t;
#endif
#endif
#if !defined(DUK_F_HAVE_64BIT) && defined(DUK_F_MINGW)
#define DUK_F_HAVE_64BIT
typedef unsigned long duk_uint64_t;
typedef signed long duk_int64_t;
#endif
#if !defined(DUK_F_HAVE_64BIT) && defined(DUK_F_MSVC)
#define DUK_F_HAVE_64BIT
typedef unsigned __int64 duk_uint64_t;
typedef signed __int64 duk_int64_t;
#endif
#if !defined(DUK_F_HAVE_64BIT)
/* cannot detect 64-bit type, not always needed so don't error */
#endif

typedef duk_uint8_t duk_uint_least8_t;
typedef duk_int8_t duk_int_least8_t;
typedef duk_uint16_t duk_uint_least16_t;
typedef duk_int16_t duk_int_least16_t;
typedef duk_uint32_t duk_uint_least32_t;
typedef duk_int32_t duk_int_least32_t;
typedef duk_uint8_t duk_uint_fast8_t;
typedef duk_int8_t duk_int_fast8_t;
typedef duk_uint16_t duk_uint_fast16_t;
typedef duk_int16_t duk_int_fast16_t;
typedef duk_uint32_t duk_uint_fast32_t;
typedef duk_int32_t duk_int_fast32_t;
#if defined(DUK_F_HAVE_64BIT)
typedef duk_uint64_t duk_uint_least64_t;
typedef duk_int64_t duk_int_least64_t;
typedef duk_uint64_t duk_uint_fast64_t;
typedef duk_int64_t duk_int_fast64_t;
#endif
#if defined(DUK_F_HAVE_64BIT)
typedef duk_uint64_t duk_uintmax_t;
typedef duk_int64_t duk_intmax_t;
#else
typedef duk_uint32_t duk_uintmax_t;
typedef duk_int32_t duk_intmax_t;
#endif

/* Note: the funny looking computations for signed minimum 16-bit, 32-bit, and
 * 64-bit values are intentional as the obvious forms (e.g. -0x80000000L) are
 * -not- portable.  See code-issues.txt for a detailed discussion.
 */
#define DUK_UINT8_MIN         0UL
#define DUK_UINT8_MAX         0xffUL
#define DUK_INT8_MIN          (-0x80L)
#define DUK_INT8_MAX          0x7fL
#define DUK_UINT_LEAST8_MIN   0UL
#define DUK_UINT_LEAST8_MAX   0xffUL
#define DUK_INT_LEAST8_MIN    (-0x80L)
#define DUK_INT_LEAST8_MAX    0x7fL
#define DUK_UINT_FAST8_MIN    0UL
#define DUK_UINT_FAST8_MAX    0xffUL
#define DUK_INT_FAST8_MIN     (-0x80L)
#define DUK_INT_FAST8_MAX     0x7fL
#define DUK_UINT16_MIN        0UL
#define DUK_UINT16_MAX        0xffffUL
#define DUK_INT16_MIN         (-0x7fffL - 1L)
#define DUK_INT16_MAX         0x7fffL
#define DUK_UINT_LEAST16_MIN  0UL
#define DUK_UINT_LEAST16_MAX  0xffffUL
#define DUK_INT_LEAST16_MIN   (-0x7fffL - 1L)
#define DUK_INT_LEAST16_MAX   0x7fffL
#define DUK_UINT_FAST16_MIN   0UL
#define DUK_UINT_FAST16_MAX   0xffffUL
#define DUK_INT_FAST16_MIN    (-0x7fffL - 1L)
#define DUK_INT_FAST16_MAX    0x7fffL
#define DUK_UINT32_MIN        0UL
#define DUK_UINT32_MAX        0xffffffffUL
#define DUK_INT32_MIN         (-0x7fffffffL - 1L)
#define DUK_INT32_MAX         0x7fffffffL
#define DUK_UINT_LEAST32_MIN  0UL
#define DUK_UINT_LEAST32_MAX  0xffffffffUL
#define DUK_INT_LEAST32_MIN   (-0x7fffffffL - 1L)
#define DUK_INT_LEAST32_MAX   0x7fffffffL
#define DUK_UINT_FAST32_MIN   0UL
#define DUK_UINT_FAST32_MAX   0xffffffffUL
#define DUK_INT_FAST32_MIN    (-0x7fffffffL - 1L)
#define DUK_INT_FAST32_MAX    0x7fffffffL

/* 64-bit constants.  Since LL / ULL constants are not always available,
 * use computed values.  These values can't be used in preprocessor
 * comparisons; flag them as such.
 */
#if defined(DUK_F_HAVE_64BIT)
#define DUK_UINT64_MIN        ((duk_uint64_t) 0)
#define DUK_UINT64_MAX        ((duk_uint64_t) -1)
#define DUK_INT64_MIN         ((duk_int64_t) (~(DUK_UINT64_MAX >> 1)))
#define DUK_INT64_MAX         ((duk_int64_t) (DUK_UINT64_MAX >> 1))
#define DUK_UINT_LEAST64_MIN  DUK_UINT64_MIN
#define DUK_UINT_LEAST64_MAX  DUK_UINT64_MAX
#define DUK_INT_LEAST64_MIN   DUK_INT64_MIN
#define DUK_INT_LEAST64_MAX   DUK_INT64_MAX
#define DUK_UINT_FAST64_MIN   DUK_UINT64_MIN
#define DUK_UINT_FAST64_MAX   DUK_UINT64_MAX
#define DUK_INT_FAST64_MIN    DUK_INT64_MIN
#define DUK_INT_FAST64_MAX    DUK_INT64_MAX
#define DUK_UINT64_MIN_COMPUTED
#define DUK_UINT64_MAX_COMPUTED
#define DUK_INT64_MIN_COMPUTED
#define DUK_INT64_MAX_COMPUTED
#define DUK_UINT_LEAST64_MIN_COMPUTED
#define DUK_UINT_LEAST64_MAX_COMPUTED
#define DUK_INT_LEAST64_MIN_COMPUTED
#define DUK_INT_LEAST64_MAX_COMPUTED
#define DUK_UINT_FAST64_MIN_COMPUTED
#define DUK_UINT_FAST64_MAX_COMPUTED
#define DUK_INT_FAST64_MIN_COMPUTED
#define DUK_INT_FAST64_MAX_COMPUTED
#endif

#if defined(DUK_F_HAVE_64BIT)
#define DUK_UINTMAX_MIN       DUK_UINT64_MIN
#define DUK_UINTMAX_MAX       DUK_UINT64_MAX
#define DUK_INTMAX_MIN        DUK_INT64_MIN
#define DUK_INTMAX_MAX        DUK_INT64_MAX
#define DUK_UINTMAX_MIN_COMPUTED
#define DUK_UINTMAX_MAX_COMPUTED
#define DUK_INTMAX_MIN_COMPUTED
#define DUK_INTMAX_MAX_COMPUTED
#else
#define DUK_UINTMAX_MIN       0UL
#define DUK_UINTMAX_MAX       0xffffffffUL
#define DUK_INTMAX_MIN        (-0x7fffffffL - 1L)
#define DUK_INTMAX_MAX        0x7fffffffL
#endif

/* This detection is not very reliable. */
#if defined(DUK_F_32BIT_PTRS)
typedef duk_int32_t duk_intptr_t;
typedef duk_uint32_t duk_uintptr_t;
#define DUK_UINTPTR_MIN       DUK_UINT32_MIN
#define DUK_UINTPTR_MAX       DUK_UINT32_MAX
#define DUK_INTPTR_MIN        DUK_INT32_MIN
#define DUK_INTPTR_MAX        DUK_INT32_MAX
#elif defined(DUK_F_64BIT_PTRS) && defined(DUK_F_HAVE_64BIT)
typedef duk_int64_t duk_intptr_t;
typedef duk_uint64_t duk_uintptr_t;
#define DUK_UINTPTR_MIN       DUK_UINT64_MIN
#define DUK_UINTPTR_MAX       DUK_UINT64_MAX
#define DUK_INTPTR_MIN        DUK_INT64_MIN
#define DUK_INTPTR_MAX        DUK_INT64_MAX
#define DUK_UINTPTR_MIN_COMPUTED
#define DUK_UINTPTR_MAX_COMPUTED
#define DUK_INTPTR_MIN_COMPUTED
#define DUK_INTPTR_MAX_COMPUTED
#else
#error cannot determine intptr type
#endif

/* SIZE_MAX may be missing so use an approximate value for it. */
#undef DUK_SIZE_MAX_COMPUTED
#if !defined(SIZE_MAX)
#define DUK_SIZE_MAX_COMPUTED
#define SIZE_MAX              ((size_t) (-1))
#endif
#define DUK_SIZE_MIN          0
#define DUK_SIZE_MAX          SIZE_MAX

#endif  /* C99 types */

/* A few types are assumed to always exist. */
typedef size_t duk_size_t;
typedef ptrdiff_t duk_ptrdiff_t;

/* The best type for an "all around int" in Duktape internals is "at least
 * 32 bit signed integer" which is most convenient.  Same for unsigned type.
 * Prefer 'int' when large enough, as it is almost always a convenient type.
 */
#if defined(UINT_MAX) && (UINT_MAX >= 0xffffffffUL)
typedef int duk_int_t;
typedef unsigned int duk_uint_t;
#define DUK_INT_MIN           INT_MIN
#define DUK_INT_MAX           INT_MAX
#define DUK_UINT_MIN          0
#define DUK_UINT_MAX          UINT_MAX
#else
typedef duk_int_fast32_t duk_int_t;
typedef duk_uint_fast32_t duk_uint_t;
#define DUK_INT_MIN           DUK_INT_FAST32_MIN
#define DUK_INT_MAX           DUK_INT_FAST32_MAX
#define DUK_UINT_MIN          DUK_UINT_FAST32_MIN
#define DUK_UINT_MAX          DUK_UINT_FAST32_MAX
#endif

/* Same as 'duk_int_t' but guaranteed to be a 'fast' variant if this
 * distinction matters for the CPU.  These types are used mainly in the
 * executor where it might really matter.
 */
typedef duk_int_fast32_t duk_int_fast_t;
typedef duk_uint_fast32_t duk_uint_fast_t;
#define DUK_INT_FAST_MIN      DUK_INT_FAST32_MIN
#define DUK_INT_FAST_MAX      DUK_INT_FAST32_MAX
#define DUK_UINT_FAST_MIN     DUK_UINT_FAST32_MIN
#define DUK_UINT_FAST_MAX     DUK_UINT_FAST32_MAX

/* Small integers (16 bits or more) can fall back to the 'int' type, but
 * have a typedef so they are marked "small" explicitly.
 */
typedef int duk_small_int_t;
typedef unsigned int duk_small_uint_t;
#define DUK_SMALL_INT_MIN     INT_MIN
#define DUK_SMALL_INT_MAX     INT_MAX
#define DUK_SMALL_UINT_MIN    0
#define DUK_SMALL_UINT_MAX    UINT_MAX

/* Fast variants of small integers, again for really fast paths like the
 * executor.
 */
typedef duk_int_fast16_t duk_small_int_fast_t;
typedef duk_uint_fast16_t duk_small_uint_fast_t;
#define DUK_SMALL_INT_FAST_MIN    DUK_INT_FAST16_MIN
#define DUK_SMALL_INT_FAST_MAX    DUK_INT_FAST16_MAX
#define DUK_SMALL_UINT_FAST_MIN   DUK_UINT_FAST16_MIN
#define DUK_SMALL_UINT_FAST_MAX   DUK_UINT_FAST16_MAX

/* Boolean values are represented with the platform 'unsigned int'. */
typedef duk_small_uint_t duk_bool_t;
#define DUK_BOOL_MIN              DUK_SMALL_UINT_MIN
#define DUK_BOOL_MAX              DUK_SMALL_UINT_MAX

/* Index values must have at least 32-bit signed range. */
typedef duk_int_t duk_idx_t;
#define DUK_IDX_MIN               DUK_INT_MIN
#define DUK_IDX_MAX               DUK_INT_MAX

/* Unsigned index variant. */
typedef duk_uint_t duk_uidx_t;
#define DUK_UIDX_MIN              DUK_UINT_MIN
#define DUK_UIDX_MAX              DUK_UINT_MAX

/* Array index values, could be exact 32 bits.
 * Currently no need for signed duk_arridx_t.
 */
typedef duk_uint_t duk_uarridx_t;
#define DUK_UARRIDX_MIN           DUK_UINT_MIN
#define DUK_UARRIDX_MAX           DUK_UINT_MAX

/* Duktape/C function return value, platform int is enough for now to
 * represent 0, 1, or negative error code.  Must be compatible with
 * assigning truth values (e.g. duk_ret_t rc = (foo == bar);).
 */
typedef duk_small_int_t duk_ret_t;
#define DUK_RET_MIN               DUK_SMALL_INT_MIN
#define DUK_RET_MAX               DUK_SMALL_INT_MAX

/* Error codes are represented with platform int.  High bits are used
 * for flags and such, so 32 bits are needed.
 */
typedef duk_int_t duk_errcode_t;
#define DUK_ERRCODE_MIN           DUK_INT_MIN
#define DUK_ERRCODE_MAX           DUK_INT_MAX

/* Codepoint type.  Must be 32 bits or more because it is used also for
 * internal codepoints.  The type is signed because negative codepoints
 * are used as internal markers (e.g. to mark EOF or missing argument).
 * (X)UTF-8/CESU-8 encode/decode take and return an unsigned variant to
 * ensure duk_uint32_t casts back and forth nicely.  Almost everything
 * else uses the signed one.
 */
typedef duk_int_t duk_codepoint_t;
typedef duk_uint_t duk_ucodepoint_t;
#define DUK_CODEPOINT_MIN         DUK_INT_MIN
#define DUK_CODEPOINT_MAX         DUK_INT_MAX
#define DUK_UCODEPOINT_MIN        DUK_UINT_MIN
#define DUK_UCODEPOINT_MAX        DUK_UINT_MAX

/* IEEE float/double typedef. */
typedef float duk_float_t;
typedef double duk_double_t;

/* We're generally assuming that we're working on a platform with a 32-bit
 * address space.  If DUK_SIZE_MAX is a typecast value (which is necessary
 * if SIZE_MAX is missing), the check must be avoided because the
 * preprocessor can't do a comparison.
 */
#if !defined(DUK_SIZE_MAX)
#error DUK_SIZE_MAX is undefined, probably missing SIZE_MAX
#elif !defined(DUK_SIZE_MAX_COMPUTED)
#if DUK_SIZE_MAX < 0xffffffffUL
/* On some systems SIZE_MAX can be smaller than max unsigned 32-bit value
 * which seems incorrect if size_t is (at least) an unsigned 32-bit type.
 * However, it doesn't seem useful to error out compilation if this is the
 * case.
 */
#endif
#endif

/* Type used in public API declarations and user code.  Typedef maps to
 * 'struct duk_hthread' like the 'duk_hthread' typedef which is used
 * exclusively in internals.
 */
typedef struct duk_hthread duk_context;

/* Check whether we should use 64-bit integers or not.
 *
 * Quite incomplete now.  Use 64-bit types if detected (C99 or other detection)
 * unless they are known to be unreliable.  For instance, 64-bit types are
 * available on VBCC but seem to misbehave.
 */
#if defined(DUK_F_HAVE_64BIT) && !defined(DUK_F_VBCC)
#define DUK_USE_64BIT_OPS
#else
#undef DUK_USE_64BIT_OPS
#endif

/*
 *  Fill-ins for platform, architecture, and compiler
 */

/* An abort()-like primitive is needed by the default fatal error handler. */
#if !defined(DUK_ABORT)
#define DUK_ABORT             abort
#endif

#if !defined(DUK_SETJMP)
#define DUK_JMPBUF_TYPE       jmp_buf
#define DUK_SETJMP(jb)        setjmp((jb))
#define DUK_LONGJMP(jb)       longjmp((jb), 1)
#endif

#if 0
/* sigsetjmp() alternative */
#define DUK_JMPBUF_TYPE       sigjmp_buf
#define DUK_SETJMP(jb)        sigsetjmp((jb))
#define DUK_LONGJMP(jb)       siglongjmp((jb), 1)
#endif

/* Special naming to avoid conflict with e.g. DUK_FREE() in duk_heap.h
 * (which is unfortunately named).  May sometimes need replacement, e.g.
 * some compilers don't handle zero length or NULL correctly in realloc().
 */
#if !defined(DUK_ANSI_MALLOC)
#define DUK_ANSI_MALLOC      malloc
#endif
#if !defined(DUK_ANSI_REALLOC)
#define DUK_ANSI_REALLOC     realloc
#endif
#if !defined(DUK_ANSI_CALLOC)
#define DUK_ANSI_CALLOC      calloc
#endif
#if !defined(DUK_ANSI_FREE)
#define DUK_ANSI_FREE        free
#endif

/* ANSI C (various versions) and some implementations require that the
 * pointer arguments to memset(), memcpy(), and memmove() be valid values
 * even when byte size is 0 (even a NULL pointer is considered invalid in
 * this context).  Zero-size operations as such are allowed, as long as their
 * pointer arguments point to a valid memory area.  The DUK_MEMSET(),
 * DUK_MEMCPY(), and DUK_MEMMOVE() macros require this same behavior, i.e.:
 * (1) pointers must be valid and non-NULL, (2) zero size must otherwise be
 * allowed.  If these are not fulfilled, a macro wrapper is needed.
 *
 *   http://stackoverflow.com/questions/5243012/is-it-guaranteed-to-be-safe-to-perform-memcpy0-0-0
 *   http://lists.cs.uiuc.edu/pipermail/llvmdev/2007-October/011065.html
 *
 * Not sure what's the required behavior when a pointer points just past the
 * end of a buffer, which often happens in practice (e.g. zero size memmoves).
 * For example, if allocation size is 3, the following pointer would not
 * technically point to a valid memory byte:
 *
 *   <-- alloc -->
 *   | 0 | 1 | 2 | .....
 *                 ^-- p=3, points after last valid byte (2)
 */
#if !defined(DUK_MEMCPY)
#if defined(DUK_F_UCLIBC)
/* Old uclibcs have a broken memcpy so use memmove instead (this is overly wide
 * now on purpose): http://lists.uclibc.org/pipermail/uclibc-cvs/2008-October/025511.html
 */
#define DUK_MEMCPY       memmove
#else
#define DUK_MEMCPY       memcpy
#endif
#endif
#if !defined(DUK_MEMMOVE)
#define DUK_MEMMOVE      memmove
#endif
#if !defined(DUK_MEMCMP)
#define DUK_MEMCMP       memcmp
#endif
#if !defined(DUK_MEMSET)
#define DUK_MEMSET       memset
#endif
#if !defined(DUK_STRLEN)
#define DUK_STRLEN       strlen
#endif
#if !defined(DUK_STRCMP)
#define DUK_STRCMP       strcmp
#endif
#if !defined(DUK_STRNCMP)
#define DUK_STRNCMP      strncmp
#endif
#if !defined(DUK_SPRINTF)
#define DUK_SPRINTF      sprintf
#endif
#if !defined(DUK_SNPRINTF)
/* snprintf() is technically not part of C89 but usually available. */
#define DUK_SNPRINTF     snprintf
#endif
#if !defined(DUK_VSPRINTF)
#define DUK_VSPRINTF     vsprintf
#endif
#if !defined(DUK_VSNPRINTF)
/* vsnprintf() is technically not part of C89 but usually available. */
#define DUK_VSNPRINTF    vsnprintf
#endif
#if !defined(DUK_SSCANF)
#define DUK_SSCANF       sscanf
#endif
#if !defined(DUK_VSSCANF)
#define DUK_VSSCANF      vsscanf
#endif
#if !defined(DUK_MEMZERO)
#define DUK_MEMZERO(p,n) DUK_MEMSET((p), 0, (n))
#endif

#if !defined(DUK_DOUBLE_INFINITY)
#undef DUK_USE_COMPUTED_INFINITY
#if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION < 40600)
/* GCC older than 4.6: avoid overflow warnings related to using INFINITY */
#define DUK_DOUBLE_INFINITY  (__builtin_inf())
#elif defined(INFINITY)
#define DUK_DOUBLE_INFINITY  ((double) INFINITY)
#elif !defined(DUK_F_VBCC) && !defined(DUK_F_MSVC) && !defined(DUK_F_BCC) && \
      !defined(DUK_F_OLD_SOLARIS) && !defined(DUK_F_AIX)
#define DUK_DOUBLE_INFINITY  (1.0 / 0.0)
#else
/* In VBCC (1.0 / 0.0) results in a warning and 0.0 instead of infinity.
 * Use a computed infinity (initialized when a heap is created at the
 * latest).
 */
#define DUK_USE_COMPUTED_INFINITY
#define DUK_DOUBLE_INFINITY  duk_computed_infinity
#endif
#endif

#if !defined(DUK_DOUBLE_NAN)
#undef DUK_USE_COMPUTED_NAN
#if defined(NAN)
#define DUK_DOUBLE_NAN       NAN
#elif !defined(DUK_F_VBCC) && !defined(DUK_F_MSVC) && !defined(DUK_F_BCC) && \
      !defined(DUK_F_OLD_SOLARIS) && !defined(DUK_F_AIX)
#define DUK_DOUBLE_NAN       (0.0 / 0.0)
#else
/* In VBCC (0.0 / 0.0) results in a warning and 0.0 instead of NaN.
 * In MSVC (VS2010 Express) (0.0 / 0.0) results in a compile error.
 * Use a computed NaN (initialized when a heap is created at the
 * latest).
 */
#define DUK_USE_COMPUTED_NAN
#define DUK_DOUBLE_NAN       duk_computed_nan
#endif
#endif

/* Many platforms are missing fpclassify() and friends, so use replacements
 * if necessary.  The replacement constants (FP_NAN etc) can be anything but
 * match Linux constants now.
 */
#undef DUK_USE_REPL_FPCLASSIFY
#undef DUK_USE_REPL_SIGNBIT
#undef DUK_USE_REPL_ISFINITE
#undef DUK_USE_REPL_ISNAN
#undef DUK_USE_REPL_ISINF

/* Complex condition broken into separate parts. */
#undef DUK_F_USE_REPL_ALL
#if !(defined(FP_NAN) && defined(FP_INFINITE) && defined(FP_ZERO) && \
      defined(FP_SUBNORMAL) && defined(FP_NORMAL))
/* Missing some obvious constants. */
#define DUK_F_USE_REPL_ALL
#elif defined(DUK_F_AMIGAOS) && defined(DUK_F_VBCC)
/* VBCC is missing the built-ins even in C99 mode (perhaps a header issue). */
#define DUK_F_USE_REPL_ALL
#elif defined(DUK_F_AMIGAOS) && defined(DUK_F_M68K)
/* AmigaOS + M68K seems to have math issues even when using GCC cross
 * compilation.  Use replacements for all AmigaOS versions on M68K
 * regardless of compiler.
 */
#define DUK_F_USE_REPL_ALL
#elif defined(DUK_F_FREEBSD) && defined(DUK_F_CLANG)
/* Placeholder fix for (detection is wider than necessary):
 * http://llvm.org/bugs/show_bug.cgi?id=17788
 */
#define DUK_F_USE_REPL_ALL
#elif defined(DUK_F_UCLIBC)
/* At least some uclibc versions have broken floating point math.  For
 * example, fpclassify() can incorrectly classify certain NaN formats.
 * To be safe, use replacements.
 */
#define DUK_F_USE_REPL_ALL
#elif defined(DUK_F_AIX)
/* Older versions may be missing isnan(), etc. */
#define DUK_F_USE_REPL_ALL
#endif

#if defined(DUK_F_USE_REPL_ALL)
#define DUK_USE_REPL_FPCLASSIFY
#define DUK_USE_REPL_SIGNBIT
#define DUK_USE_REPL_ISFINITE
#define DUK_USE_REPL_ISNAN
#define DUK_USE_REPL_ISINF
#define DUK_FPCLASSIFY       duk_repl_fpclassify
#define DUK_SIGNBIT          duk_repl_signbit
#define DUK_ISFINITE         duk_repl_isfinite
#define DUK_ISNAN            duk_repl_isnan
#define DUK_ISINF            duk_repl_isinf
#define DUK_FP_NAN           0
#define DUK_FP_INFINITE      1
#define DUK_FP_ZERO          2
#define DUK_FP_SUBNORMAL     3
#define DUK_FP_NORMAL        4
#else
#define DUK_FPCLASSIFY       fpclassify
#define DUK_SIGNBIT          signbit
#define DUK_ISFINITE         isfinite
#define DUK_ISNAN            isnan
#define DUK_ISINF            isinf
#define DUK_FP_NAN           FP_NAN
#define DUK_FP_INFINITE      FP_INFINITE
#define DUK_FP_ZERO          FP_ZERO
#define DUK_FP_SUBNORMAL     FP_SUBNORMAL
#define DUK_FP_NORMAL        FP_NORMAL
#endif

#if defined(DUK_F_USE_REPL_ALL)
#undef DUK_F_USE_REPL_ALL
#endif

/* These functions don't currently need replacement but are wrapped for
 * completeness.  Because these are used as function pointers, they need
 * to be defined as concrete C functions (not macros).
 */
#if !defined(DUK_FABS)
#define DUK_FABS             fabs
#endif
#if !defined(DUK_FLOOR)
#define DUK_FLOOR            floor
#endif
#if !defined(DUK_CEIL)
#define DUK_CEIL             ceil
#endif
#if !defined(DUK_FMOD)
#define DUK_FMOD             fmod
#endif
#if !defined(DUK_POW)
#define DUK_POW              pow
#endif
#if !defined(DUK_ACOS)
#define DUK_ACOS             acos
#endif
#if !defined(DUK_ASIN)
#define DUK_ASIN             asin
#endif
#if !defined(DUK_ATAN)
#define DUK_ATAN             atan
#endif
#if !defined(DUK_ATAN2)
#define DUK_ATAN2            atan2
#endif
#if !defined(DUK_SIN)
#define DUK_SIN              sin
#endif
#if !defined(DUK_COS)
#define DUK_COS              cos
#endif
#if !defined(DUK_TAN)
#define DUK_TAN              tan
#endif
#if !defined(DUK_EXP)
#define DUK_EXP              exp
#endif
#if !defined(DUK_LOG)
#define DUK_LOG              log
#endif
#if !defined(DUK_SQRT)
#define DUK_SQRT             sqrt
#endif

/* The functions below exist only in C99/C++11 or later and need a workaround
 * for platforms that don't include them.  MSVC isn't detected as C99, but
 * these functions also exist in MSVC 2013 and later so include a clause for
 * that too.  Android doesn't have log2; disable all of these for Android.
 */
#if (defined(DUK_F_C99) || defined(DUK_F_CPP11) || (defined(_MSC_VER) && (_MSC_VER >= 1800))) && \
    !defined(DUK_F_ANDROID) && !defined(DUK_F_MINT)
#if !defined(DUK_CBRT)
#define DUK_CBRT             cbrt
#endif
#if !defined(DUK_LOG2)
#define DUK_LOG2             log2
#endif
#if !defined(DUK_LOG10)
#define DUK_LOG10            log10
#endif
#if !defined(DUK_TRUNC)
#define DUK_TRUNC            trunc
#endif
#endif  /* DUK_F_C99 etc */

/* NetBSD 6.0 x86 (at least) has a few problems with pow() semantics,
 * see test-bug-netbsd-math-pow.js.  MinGW has similar (but different)
 * issues, see test-bug-mingw-math-issues.js.  Enable pow() workarounds
 * for these targets.
 */
#undef DUK_USE_POW_WORKAROUNDS
#if defined(DUK_F_NETBSD) || defined(DUK_F_MINGW)
#define DUK_USE_POW_WORKAROUNDS
#endif

/* Similar workarounds for atan2() semantics issues.  MinGW issues are
 * documented in test-bug-mingw-math-issues.js.
 */
#undef DUK_USE_ATAN2_WORKAROUNDS
#if defined(DUK_F_MINGW)
#define DUK_USE_ATAN2_WORKAROUNDS
#endif

/* Rely as little as possible on compiler behavior for NaN comparison,
 * signed zero handling, etc.  Currently never activated but may be needed
 * for broken compilers.
 */
#undef DUK_USE_PARANOID_MATH

/* There was a curious bug where test-bi-date-canceling.js would fail e.g.
 * on 64-bit Ubuntu, gcc-4.8.1, -m32, and no -std=c99.  Some date computations
 * using doubles would be optimized which then broke some corner case tests.
 * The problem goes away by adding 'volatile' to the datetime computations.
 * Not sure what the actual triggering conditions are, but using this on
 * non-C99 systems solves the known issues and has relatively little cost
 * on other platforms.
 */
#undef DUK_USE_PARANOID_DATE_COMPUTATION
#if !defined(DUK_F_C99)
#define DUK_USE_PARANOID_DATE_COMPUTATION
#endif

/*
 *  Byte order and double memory layout detection
 *
 *  Endianness detection is a major portability hassle because the macros
 *  and headers are not standardized.  There's even variance across UNIX
 *  platforms.  Even with "standard" headers, details like underscore count
 *  varies between platforms, e.g. both __BYTE_ORDER and _BYTE_ORDER are used
 *  (Crossbridge has a single underscore, for instance).
 *
 *  The checks below are structured with this in mind: several approaches are
 *  used, and at the end we check if any of them worked.  This allows generic
 *  approaches to be tried first, and platform/compiler specific hacks tried
 *  last.  As a last resort, the user can force a specific endianness, as it's
 *  not likely that automatic detection will work on the most exotic platforms.
 *
 *  Duktape supports little and big endian machines.  There's also support
 *  for a hybrid used by some ARM machines where integers are little endian
 *  but IEEE double values use a mixed order (12345678 -> 43218765).  This
 *  byte order for doubles is referred to as "mixed endian".
 */

/* GCC and Clang provide endianness defines as built-in predefines, with
 * leading and trailing double underscores (e.g. __BYTE_ORDER__).  See
 * output of "make gccpredefs" and "make clangpredefs".  Clang doesn't
 * seem to provide __FLOAT_WORD_ORDER__; assume not mixed endian for clang.
 * http://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
 */
#if !defined(DUK_USE_BYTEORDER) && defined(__BYTE_ORDER__)
#if defined(__ORDER_LITTLE_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
#if defined(__FLOAT_WORD_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && (__FLOAT_WORD_ORDER__ == __ORDER_LITTLE_ENDIAN__)
#define DUK_USE_BYTEORDER 1
#elif defined(__FLOAT_WORD_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && (__FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__)
#define DUK_USE_BYTEORDER 2
#elif !defined(__FLOAT_WORD_ORDER__)
/* Float word order not known, assume not a hybrid. */
#define DUK_USE_BYTEORDER 1
#else
/* Byte order is little endian but cannot determine IEEE double word order. */
#endif  /* float word order */
#elif defined(__ORDER_BIG_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#if defined(__FLOAT_WORD_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && (__FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__)
#define DUK_USE_BYTEORDER 3
#elif !defined(__FLOAT_WORD_ORDER__)
/* Float word order not known, assume not a hybrid. */
#define DUK_USE_BYTEORDER 3
#else
/* Byte order is big endian but cannot determine IEEE double word order. */
#endif  /* float word order */
#else
/* Cannot determine byte order; __ORDER_PDP_ENDIAN__ is related to 32-bit
 * integer ordering and is not relevant.
 */
#endif  /* integer byte order */
#endif  /* !defined(DUK_USE_BYTEORDER) && defined(__BYTE_ORDER__) */

/* More or less standard endianness predefines provided by header files.
 * The ARM hybrid case is detected by assuming that __FLOAT_WORD_ORDER
 * will be big endian, see: http://lists.mysql.com/internals/443.
 * On some platforms some defines may be present with an empty value which
 * causes comparisons to fail: https://github.com/svaarala/duktape/issues/453.
 */
#if !defined(DUK_USE_BYTEORDER)
#if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && (__BYTE_ORDER == __LITTLE_ENDIAN) || \
    defined(_BYTE_ORDER) && defined(_LITTLE_ENDIAN) && (_BYTE_ORDER == _LITTLE_ENDIAN) || \
    defined(__LITTLE_ENDIAN__)
#if defined(__FLOAT_WORD_ORDER) && defined(__LITTLE_ENDIAN) && (__FLOAT_WORD_ORDER == __LITTLE_ENDIAN) || \
    defined(_FLOAT_WORD_ORDER) && defined(_LITTLE_ENDIAN) && (_FLOAT_WORD_ORDER == _LITTLE_ENDIAN)
#define DUK_USE_BYTEORDER 1
#elif defined(__FLOAT_WORD_ORDER) && defined(__BIG_ENDIAN) && (__FLOAT_WORD_ORDER == __BIG_ENDIAN) || \
      defined(_FLOAT_WORD_ORDER) && defined(_BIG_ENDIAN) && (_FLOAT_WORD_ORDER == _BIG_ENDIAN)
#define DUK_USE_BYTEORDER 2
#elif !defined(__FLOAT_WORD_ORDER) && !defined(_FLOAT_WORD_ORDER)
/* Float word order not known, assume not a hybrid. */
#define DUK_USE_BYTEORDER 1
#else
/* Byte order is little endian but cannot determine IEEE double word order. */
#endif  /* float word order */
#elif defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && (__BYTE_ORDER == __BIG_ENDIAN) || \
      defined(_BYTE_ORDER) && defined(_BIG_ENDIAN) && (_BYTE_ORDER == _BIG_ENDIAN) || \
      defined(__BIG_ENDIAN__)
#if defined(__FLOAT_WORD_ORDER) && defined(__BIG_ENDIAN) && (__FLOAT_WORD_ORDER == __BIG_ENDIAN) || \
    defined(_FLOAT_WORD_ORDER) && defined(_BIG_ENDIAN) && (_FLOAT_WORD_ORDER == _BIG_ENDIAN)
#define DUK_USE_BYTEORDER 3
#elif !defined(__FLOAT_WORD_ORDER) && !defined(_FLOAT_WORD_ORDER)
/* Float word order not known, assume not a hybrid. */
#define DUK_USE_BYTEORDER 3
#else
/* Byte order is big endian but cannot determine IEEE double word order. */
#endif  /* float word order */
#else
/* Cannot determine byte order. */
#endif  /* integer byte order */
#endif  /* !defined(DUK_USE_BYTEORDER) */

/* QNX gcc cross compiler seems to define e.g. __LITTLEENDIAN__ or __BIGENDIAN__:
 *  $ /opt/qnx650/host/linux/x86/usr/bin/i486-pc-nto-qnx6.5.0-gcc -dM -E - </dev/null | grep -ni endian
 *  67:#define __LITTLEENDIAN__ 1
 *  $ /opt/qnx650/host/linux/x86/usr/bin/mips-unknown-nto-qnx6.5.0-gcc -dM -E - </dev/null | grep -ni endian
 *  81:#define __BIGENDIAN__ 1
 *  $ /opt/qnx650/host/linux/x86/usr/bin/arm-unknown-nto-qnx6.5.0-gcc -dM -E - </dev/null | grep -ni endian
 *  70:#define __LITTLEENDIAN__ 1
 */
#if !defined(DUK_USE_BYTEORDER)
#if defined(__LITTLEENDIAN__)
#define DUK_USE_BYTEORDER 1
#elif defined(__BIGENDIAN__)
#define DUK_USE_BYTEORDER 3
#endif
#endif

/*
 *  Alignment requirement and support for unaligned accesses
 *
 *  Assume unaligned accesses are not supported unless specifically allowed
 *  in the target platform.  Some platforms may support unaligned accesses
 *  but alignment to 4 or 8 may still be desirable.  Note that unaligned
 *  accesses (and even pointers) relative to natural alignment (regardless
 *  of target alignment) are technically undefined behavior and thus
 *  compiler/architecture specific.
 */

/* If not forced, use safe default for alignment. */
#if !defined(DUK_USE_ALIGN_BY)
#define DUK_USE_ALIGN_BY 8
#endif

/* Compiler specific hackery needed to force struct size to match alignment,
 * see e.g. duk_hbuffer.h.
 *
 * http://stackoverflow.com/questions/11130109/c-struct-size-alignment
 * http://stackoverflow.com/questions/10951039/specifying-64-bit-alignment
 */
#if !(defined(DUK_USE_PACK_MSVC_PRAGMA) || defined(DUK_USE_PACK_GCC_ATTR) || \
      defined(DUK_USE_PACK_CLANG_ATTR) || defined(DUK_USE_PACK_DUMMY_MEMBER))
#define DUK_USE_PACK_DUMMY_MEMBER
#endif

#if !defined(DUK_U64_CONSTANT)
#define DUK_U64_CONSTANT(x) x##ULL
#endif
#if !defined(DUK_I64_CONSTANT)
#define DUK_I64_CONSTANT(x) x##LL
#endif

#if !defined(DUK_VA_COPY)
/* We need va_copy() which is defined in C99 / C++11, so an awkward
 * replacement is needed for pre-C99 / pre-C++11 environments.  This
 * will quite likely need portability hacks for some non-C99
 * environments.
 */
#if defined(DUK_F_C99) || defined(DUK_F_CPP11)
/* C99 / C++11 and above: rely on va_copy() which is required.
 * Omit parenthesis on macro right side on purpose to minimize differences
 * to direct use.
 */
#define DUK_VA_COPY(dest,src) va_copy(dest,src)
#else
/* Pre-C99: va_list type is implementation dependent.  This replacement
 * assumes it is a plain value so that a simple assignment will work.
 * This is not the case on all platforms (it may be a single-array element,
 * for instance).
 */
#define DUK_VA_COPY(dest,src) do { (dest) = (src); } while (0)
#endif
#endif

#if !defined(DUK_MACRO_STRINGIFY)
/* Macro hackery to convert e.g. __LINE__ to a string without formatting,
 * see: http://stackoverflow.com/questions/240353/convert-a-preprocessor-token-to-a-string
 */
#define DUK_MACRO_STRINGIFY_HELPER(x)  #x
#define DUK_MACRO_STRINGIFY(x)  DUK_MACRO_STRINGIFY_HELPER(x)
#endif

#if !defined(DUK_CAUSE_SEGFAULT)
/* This can be used for testing; valgrind will then indicate the C call stack
 * leading to the call site.
 */
#define DUK_CAUSE_SEGFAULT()  do { *((volatile duk_uint32_t *) NULL) = (duk_uint32_t) 0xdeadbeefUL; } while (0)
#endif

#if !defined(DUK_UNREF)
/* Macro for suppressing warnings for potentially unreferenced variables.
 * The variables can be actually unreferenced or unreferenced in some
 * specific cases only; for instance, if a variable is only debug printed,
 * it is unreferenced when debug printing is disabled.  May cause warnings
 * for volatile arguments.
 */
#define DUK_UNREF(x)  do { (void) (x); } while (0)
#endif

/* Fillin for DUK_NORETURN; DUK_WO_NORETURN() is used to insert dummy
 * dummy statements after noreturn calls to silence harmless compiler
 * warnings, e.g.:
 *
 *   DUK_ERROR_TYPE(thr, "aiee");
 *   DUK_WO_NORETURN(return 0;);
 *
 * Statements inside DUK_WO_NORETURN() must NEVER be actually reachable,
 * and they're only included to satisfy the compiler.
 */
#if defined(DUK_NORETURN)
#define DUK_WO_NORETURN(stmt) do { } while (0)
#else
#define DUK_NORETURN(decl)  decl
#define DUK_WO_NORETURN(stmt) do { stmt } while (0)
#endif

#if defined(DUK_UNREACHABLE)
#define DUK_WO_UNREACHABLE(stmt) do { } while (0)
#else
/* Don't know how to declare unreachable point, so don't do it; this
 * may cause some spurious compilation warnings (e.g. "variable used
 * uninitialized").
 */
#define DUK_UNREACHABLE()  do { } while (0)
#define DUK_WO_UNREACHABLE(stmt) do { stmt } while (0)
#endif

#if !defined(DUK_LOSE_CONST)
/* Convert any input pointer into a "void *", losing a const qualifier.
 * This is not fully portable because casting through duk_uintptr_t may
 * not work on all architectures (e.g. those with long, segmented pointers).
 */
#define DUK_LOSE_CONST(src) ((void *) (duk_uintptr_t) (src))
#endif

#if !defined(DUK_LIKELY)
#define DUK_LIKELY(x)    (x)
#endif
#if !defined(DUK_UNLIKELY)
#define DUK_UNLIKELY(x)  (x)
#endif
#if !defined(DUK_UNPREDICTABLE)
#define DUK_UNPREDICTABLE(x)  (x)
#endif

#if !defined(DUK_NOINLINE)
#define DUK_NOINLINE       /*nop*/
#endif
#if !defined(DUK_INLINE)
#define DUK_INLINE         /*nop*/
#endif
#if !defined(DUK_ALWAYS_INLINE)
#define DUK_ALWAYS_INLINE  /*nop*/
#endif

#if !defined(DUK_HOT)
#define DUK_HOT            /*nop*/
#endif
#if !defined(DUK_COLD)
#define DUK_COLD           /*nop*/
#endif

#if !defined(DUK_EXTERNAL_DECL)
#define DUK_EXTERNAL_DECL  extern
#endif
#if !defined(DUK_EXTERNAL)
#define DUK_EXTERNAL       /*empty*/
#endif
#if !defined(DUK_INTERNAL_DECL)
#if defined(DUK_SINGLE_FILE)
#define DUK_INTERNAL_DECL  static
#else
#define DUK_INTERNAL_DECL  extern
#endif
#endif
#if !defined(DUK_INTERNAL)
#if defined(DUK_SINGLE_FILE)
#define DUK_INTERNAL       static
#else
#define DUK_INTERNAL       /*empty*/
#endif
#endif
#if !defined(DUK_LOCAL_DECL)
#define DUK_LOCAL_DECL     static
#endif
#if !defined(DUK_LOCAL)
#define DUK_LOCAL          static
#endif

#if !defined(DUK_FILE_MACRO)
#define DUK_FILE_MACRO  __FILE__
#endif
#if !defined(DUK_LINE_MACRO)
#define DUK_LINE_MACRO  __LINE__
#endif
#if !defined(DUK_FUNC_MACRO)
#if defined(DUK_F_C99) || defined(DUK_F_CPP11)
#define DUK_FUNC_MACRO  __func__
#elif defined(__FUNCTION__)
#define DUK_FUNC_MACRO  __FUNCTION__
#else
#define DUK_FUNC_MACRO  "unknown"
#endif
#endif

#if defined(DUK_F_HAVE_64BIT)
#if !defined(DUK_BSWAP64)
#define DUK_BSWAP64(x) \
    ((((duk_uint64_t) (x)) >> 56U) | \
     ((((duk_uint64_t) (x)) >> 40U) & DUK_U64_CONSTANT(0xff00)) | \
     ((((duk_uint64_t) (x)) >> 24U) & DUK_U64_CONSTANT(0xff0000)) | \
     ((((duk_uint64_t) (x)) >> 8U) & DUK_U64_CONSTANT(0xff000000)) | \
     ((((duk_uint64_t) (x)) << 8U) & DUK_U64_CONSTANT(0xff00000000)) | \
     ((((duk_uint64_t) (x)) << 24U) & DUK_U64_CONSTANT(0xff0000000000)) | \
     ((((duk_uint64_t) (x)) << 40U) & DUK_U64_CONSTANT(0xff000000000000)) | \
     (((duk_uint64_t) (x)) << 56U))
#endif
#endif
#if !defined(DUK_BSWAP32)
#define DUK_BSWAP32(x) \
    ((((duk_uint32_t) (x)) >> 24U) | \
     ((((duk_uint32_t) (x)) >> 8U) & 0xff00UL) | \
     ((((duk_uint32_t) (x)) << 8U) & 0xff0000UL) | \
     (((duk_uint32_t) (x)) << 24U))
#endif
#if !defined(DUK_BSWAP16)
#define DUK_BSWAP16(x) \
    ((duk_uint16_t) (x) >> 8U) | \
    ((duk_uint16_t) (x) << 8U)
#endif

/* DUK_USE_VARIADIC_MACROS: required from compilers, so no fill-in. */
/* DUK_USE_UNION_INITIALIZERS: required from compilers, so no fill-in. */

#if !(defined(DUK_USE_FLEX_C99) || defined(DUK_USE_FLEX_ZEROSIZE) || defined(DUK_USE_FLEX_ONESIZE))
#if defined(DUK_F_C99)
#define DUK_USE_FLEX_C99
#else
#define DUK_USE_FLEX_ZEROSIZE  /* Not standard but common enough */
#endif
#endif

#if !(defined(DUK_USE_PACK_GCC_ATTR) || defined(DUK_USE_PACK_CLANG_ATTR) || \
      defined(DUK_USE_PACK_MSVC_PRAGMA) || defined(DUK_USE_PACK_DUMMY_MEMBER))
#define DUK_USE_PACK_DUMMY_MEMBER
#endif

#if 0  /* not defined by default */
#undef DUK_USE_GCC_PRAGMAS
#endif

/* Workaround for GH-323: avoid inlining control when compiling from
 * multiple sources, as it causes compiler portability trouble.
 */
#if !defined(DUK_SINGLE_FILE)
#undef DUK_NOINLINE
#undef DUK_INLINE
#undef DUK_ALWAYS_INLINE
#define DUK_NOINLINE       /*nop*/
#define DUK_INLINE         /*nop*/
#define DUK_ALWAYS_INLINE  /*nop*/
#endif

/*
 *  Check whether or not a packed duk_tval representation is possible.
 *  What's basically required is that pointers are 32-bit values
 *  (sizeof(void *) == 4).  Best effort check, not always accurate.
 *  If guess goes wrong, crashes may result; self tests also verify
 *  the guess.
 */

/* Explicit marker needed; may be 'defined', 'undefined, 'or 'not provided'. */
#if !defined(DUK_F_PACKED_TVAL_PROVIDED)
#undef DUK_F_PACKED_TVAL_POSSIBLE

/* Strict C99 case: DUK_UINTPTR_MAX (= UINTPTR_MAX) should be very reliable */
#if !defined(DUK_F_PACKED_TVAL_POSSIBLE) && defined(DUK_UINTPTR_MAX)
#if (DUK_UINTPTR_MAX <= 0xffffffffUL)
#define DUK_F_PACKED_TVAL_POSSIBLE
#endif
#endif

/* Non-C99 case, still relying on DUK_UINTPTR_MAX, as long as it is not a computed value */
#if !defined(DUK_F_PACKED_TVAL_POSSIBLE) && defined(DUK_UINTPTR_MAX) && !defined(DUK_UINTPTR_MAX_COMPUTED)
#if (DUK_UINTPTR_MAX <= 0xffffffffUL)
#define DUK_F_PACKED_TVAL_POSSIBLE
#endif
#endif

/* DUK_SIZE_MAX (= SIZE_MAX) is often reliable */
#if !defined(DUK_F_PACKED_TVAL_POSSIBLE) && defined(DUK_SIZE_MAX) && !defined(DUK_SIZE_MAX_COMPUTED)
#if (DUK_SIZE_MAX <= 0xffffffffUL)
#define DUK_F_PACKED_TVAL_POSSIBLE
#endif
#endif

#undef DUK_USE_PACKED_TVAL
#if defined(DUK_F_PACKED_TVAL_POSSIBLE)
#define DUK_USE_PACKED_TVAL
#endif
#undef DUK_F_PACKED_TVAL_POSSIBLE

#endif  /* DUK_F_PACKED_TVAL_PROVIDED */
/* Object property allocation layout has implications for memory and code
 * footprint and generated code size/speed.  The best layout also depends
 * on whether the platform has alignment requirements or benefits from
 * having mostly aligned accesses.
 */
#undef DUK_USE_HOBJECT_LAYOUT_1
#undef DUK_USE_HOBJECT_LAYOUT_2
#undef DUK_USE_HOBJECT_LAYOUT_3
#if (DUK_USE_ALIGN_BY == 1)
/* On platforms without any alignment issues, layout 1 is preferable
 * because it compiles to slightly less code and provides direct access
 * to property keys.
 */
#define DUK_USE_HOBJECT_LAYOUT_1
#else
/* On other platforms use layout 2, which requires some padding but
 * is a bit more natural than layout 3 in ordering the entries.  Layout
 * 3 is currently not used.
 */
#define DUK_USE_HOBJECT_LAYOUT_2
#endif

/* GCC/clang inaccurate math would break compliance and probably duk_tval,
 * so refuse to compile.  Relax this if -ffast-math is tested to work.
 */
#if defined(__FAST_MATH__)
#error __FAST_MATH__ defined, refusing to compile
#endif

/*
 *  Autogenerated defaults
 */

#undef DUK_USE_ALLOW_UNDEFINED_BEHAVIOR
#define DUK_USE_ARRAY_BUILTIN
#define DUK_USE_ARRAY_FASTPATH
#define DUK_USE_ARRAY_PROP_FASTPATH
#undef DUK_USE_ASSERTIONS
#define DUK_USE_AUGMENT_ERROR_CREATE
#define DUK_USE_AUGMENT_ERROR_THROW
#define DUK_USE_AVOID_PLATFORM_FUNCPTRS
#define DUK_USE_BASE64_FASTPATH
#define DUK_USE_BASE64_SUPPORT
#define DUK_USE_BOOLEAN_BUILTIN
#define DUK_USE_BUFFEROBJECT_SUPPORT
#undef DUK_USE_BUFLEN16
#define DUK_USE_BYTECODE_DUMP_SUPPORT
#define DUK_USE_CACHE_ACTIVATION
#define DUK_USE_CACHE_CATCHER
#define DUK_USE_CALLSTACK_LIMIT 10000
#define DUK_USE_CBOR_BUILTIN
#define DUK_USE_CBOR_DEC_RECLIMIT 1000
#define DUK_USE_CBOR_ENC_RECLIMIT 1000
#define DUK_USE_CBOR_SUPPORT
#define DUK_USE_COMPILER_RECLIMIT 2500
#define DUK_USE_COROUTINE_SUPPORT
#undef DUK_USE_CPP_EXCEPTIONS
#undef DUK_USE_DATAPTR16
#undef DUK_USE_DATAPTR_DEC16
#undef DUK_USE_DATAPTR_ENC16
#define DUK_USE_DATE_BUILTIN
#undef DUK_USE_DATE_FORMAT_STRING
#undef DUK_USE_DATE_GET_LOCAL_TZOFFSET
#undef DUK_USE_DATE_GET_NOW
#undef DUK_USE_DATE_PARSE_STRING
#undef DUK_USE_DATE_PRS_GETDATE
#undef DUK_USE_DEBUG
#undef DUK_USE_DEBUGGER_DUMPHEAP
#undef DUK_USE_DEBUGGER_INSPECT
#undef DUK_USE_DEBUGGER_PAUSE_UNCAUGHT
#undef DUK_USE_DEBUGGER_SUPPORT
#define DUK_USE_DEBUGGER_THROW_NOTIFY
#undef DUK_USE_DEBUGGER_TRANSPORT_TORTURE
#define DUK_USE_DEBUG_BUFSIZE 65536L
#define DUK_USE_DEBUG_LEVEL 0
#undef DUK_USE_DEBUG_WRITE
#define DUK_USE_DOUBLE_LINKED_HEAP
#define DUK_USE_DUKTAPE_BUILTIN
#define DUK_USE_ENCODING_BUILTINS
#define DUK_USE_ERRCREATE
#define DUK_USE_ERRTHROW
#define DUK_USE_ES6
#define DUK_USE_ES6_OBJECT_PROTO_PROPERTY
#define DUK_USE_ES6_OBJECT_SETPROTOTYPEOF
#define DUK_USE_ES6_PROXY
#define DUK_USE_ES6_REGEXP_SYNTAX
#define DUK_USE_ES6_UNICODE_ESCAPE
#define DUK_USE_ES7
#define DUK_USE_ES7_EXP_OPERATOR
#define DUK_USE_ES8
#define DUK_USE_ES9
#define DUK_USE_ESBC_LIMITS
#define DUK_USE_ESBC_MAX_BYTES 2147418112L
#define DUK_USE_ESBC_MAX_LINENUMBER 2147418112L
#undef DUK_USE_EXEC_FUN_LOCAL
#undef DUK_USE_EXEC_INDIRECT_BOUND_CHECK
#undef DUK_USE_EXEC_PREFER_SIZE
#define DUK_USE_EXEC_REGCONST_OPTIMIZE
#undef DUK_USE_EXEC_TIMEOUT_CHECK
#undef DUK_USE_EXPLICIT_NULL_INIT
#undef DUK_USE_EXTSTR_FREE
#undef DUK_USE_EXTSTR_INTERN_CHECK
#undef DUK_USE_FASTINT
#define DUK_USE_FAST_REFCOUNT_DEFAULT
#undef DUK_USE_FATAL_HANDLER
#define DUK_USE_FATAL_MAXLEN 128
#define DUK_USE_FINALIZER_SUPPORT
#undef DUK_USE_FINALIZER_TORTURE
#undef DUK_USE_FUNCPTR16
#undef DUK_USE_FUNCPTR_DEC16
#undef DUK_USE_FUNCPTR_ENC16
#define DUK_USE_FUNCTION_BUILTIN
#define DUK_USE_FUNC_FILENAME_PROPERTY
#define DUK_USE_FUNC_NAME_PROPERTY
#undef DUK_USE_GC_TORTURE
#undef DUK_USE_GET_MONOTONIC_TIME
#undef DUK_USE_GET_RANDOM_DOUBLE
#define DUK_USE_GLOBAL_BINDING
#define DUK_USE_GLOBAL_BUILTIN
#undef DUK_USE_HEAPPTR16
#undef DUK_USE_HEAPPTR_DEC16
#undef DUK_USE_HEAPPTR_ENC16
#define DUK_USE_HEX_FASTPATH
#define DUK_USE_HEX_SUPPORT
#define DUK_USE_HOBJECT_ARRAY_ABANDON_LIMIT 2
#define DUK_USE_HOBJECT_ARRAY_ABANDON_MINSIZE 257
#define DUK_USE_HOBJECT_ARRAY_FAST_RESIZE_LIMIT 9
#define DUK_USE_HOBJECT_ARRAY_MINGROW_ADD 16
#define DUK_USE_HOBJECT_ARRAY_MINGROW_DIVISOR 8
#define DUK_USE_HOBJECT_ENTRY_MINGROW_ADD 16
#define DUK_USE_HOBJECT_ENTRY_MINGROW_DIVISOR 8
#define DUK_USE_HOBJECT_HASH_PART
#define DUK_USE_HOBJECT_HASH_PROP_LIMIT 8
#define DUK_USE_HSTRING_ARRIDX
#define DUK_USE_HSTRING_CLEN
#undef DUK_USE_HSTRING_EXTDATA
#define DUK_USE_HSTRING_LAZY_CLEN
#define DUK_USE_HTML_COMMENTS
#define DUK_USE_IDCHAR_FASTPATH
#undef DUK_USE_INJECT_HEAP_ALLOC_ERROR
#undef DUK_USE_INTERRUPT_COUNTER
#undef DUK_USE_INTERRUPT_DEBUG_FIXUP
#define DUK_USE_JC
#define DUK_USE_JSON_BUILTIN
#define DUK_USE_JSON_DECNUMBER_FASTPATH
#define DUK_USE_JSON_DECSTRING_FASTPATH
#define DUK_USE_JSON_DEC_RECLIMIT 1000
#define DUK_USE_JSON_EATWHITE_FASTPATH
#define DUK_USE_JSON_ENC_RECLIMIT 1000
#define DUK_USE_JSON_QUOTESTRING_FASTPATH
#undef DUK_USE_JSON_STRINGIFY_FASTPATH
#define DUK_USE_JSON_SUPPORT
#define DUK_USE_JX
#define DUK_USE_LEXER_SLIDING_WINDOW
#undef DUK_USE_LIGHTFUNC_BUILTINS
#define DUK_USE_LITCACHE_SIZE 256
#define DUK_USE_MARK_AND_SWEEP_RECLIMIT 256
#define DUK_USE_MATH_BUILTIN
#define DUK_USE_NATIVE_CALL_RECLIMIT 1000
#undef DUK_USE_NATIVE_STACK_CHECK
#define DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT
#undef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY
#undef DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY
#define DUK_USE_NONSTD_FUNC_STMT
#define DUK_USE_NONSTD_GETTER_KEY_ARGUMENT
#define DUK_USE_NONSTD_JSON_ESC_U2028_U2029
#define DUK_USE_NONSTD_SETTER_KEY_ARGUMENT
#define DUK_USE_NONSTD_STRING_FROMCHARCODE_32BIT
#define DUK_USE_NUMBER_BUILTIN
#define DUK_USE_OBJECT_BUILTIN
#undef DUK_USE_OBJSIZES16
#undef DUK_USE_PARANOID_ERRORS
#define DUK_USE_PC2LINE
#define DUK_USE_PERFORMANCE_BUILTIN
#undef DUK_USE_PREFER_SIZE
#undef DUK_USE_PROMISE_BUILTIN
#define DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS
#undef DUK_USE_REFCOUNT16
#define DUK_USE_REFCOUNT32
#define DUK_USE_REFERENCE_COUNTING
#define DUK_USE_REFLECT_BUILTIN
#define DUK_USE_REGEXP_CANON_BITMAP
#undef DUK_USE_REGEXP_CANON_WORKAROUND
#define DUK_USE_REGEXP_COMPILER_RECLIMIT 10000
#define DUK_USE_REGEXP_EXECUTOR_RECLIMIT 10000
#define DUK_USE_REGEXP_SUPPORT
#undef DUK_USE_ROM_GLOBAL_CLONE
#undef DUK_USE_ROM_GLOBAL_INHERIT
#undef DUK_USE_ROM_OBJECTS
#define DUK_USE_ROM_PTRCOMP_FIRST 63488L
#undef DUK_USE_ROM_STRINGS
#define DUK_USE_SECTION_B
#undef DUK_USE_SELF_TESTS
#define DUK_USE_SHEBANG_COMMENTS
#undef DUK_USE_SHUFFLE_TORTURE
#define DUK_USE_SOURCE_NONBMP
#undef DUK_USE_STRHASH16
#undef DUK_USE_STRHASH_DENSE
#define DUK_USE_STRHASH_SKIP_SHIFT 5
#define DUK_USE_STRICT_DECL
#undef DUK_USE_STRICT_UTF8_SOURCE
#define DUK_USE_STRING_BUILTIN
#undef DUK_USE_STRLEN16
#define DUK_USE_STRTAB_GROW_LIMIT 17
#define DUK_USE_STRTAB_MAXSIZE 268435456L
#define DUK_USE_STRTAB_MINSIZE 1024
#undef DUK_USE_STRTAB_PTRCOMP
#define DUK_USE_STRTAB_RESIZE_CHECK_MASK 255
#define DUK_USE_STRTAB_SHRINK_LIMIT 6
#undef DUK_USE_STRTAB_TORTURE
#define DUK_USE_SYMBOL_BUILTIN
#define DUK_USE_TAILCALL
#define DUK_USE_TARGET_INFO "unknown"
#define DUK_USE_TRACEBACKS
#define DUK_USE_TRACEBACK_DEPTH 10
#define DUK_USE_VALSTACK_GROW_SHIFT 2
#define DUK_USE_VALSTACK_LIMIT 1000000L
#define DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT 2
#define DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT 4
#undef DUK_USE_VALSTACK_UNSAFE
#define DUK_USE_VERBOSE_ERRORS
#define DUK_USE_VERBOSE_EXECUTOR_ERRORS
#define DUK_USE_VOLUNTARY_GC
#define DUK_USE_ZERO_BUFFER_DATA

/*
 *  You may add overriding #define/#undef directives below for
 *  customization.  You of course cannot un-#include or un-typedef
 *  anything; these require direct changes above.
 */

/* __OVERRIDE_DEFINES__ */

/*
 *  Conditional includes
 */

#if defined(DUK_F_CPP) && defined(DUK_USE_CPP_EXCEPTIONS)
//#include <exception>  /* std::exception */
//#include <stdexcept>  /* std::runtime_error */
#endif

/*
 *  Date provider selection
 *
 *  User may define DUK_USE_DATE_GET_NOW() etc directly, in which case we'll
 *  rely on an external provider.  If this is not done, revert to previous
 *  behavior and use Unix/Windows built-in provider.
 */

#if defined(DUK_COMPILING_DUKTAPE)

#if defined(DUK_USE_DATE_GET_NOW)
/* External provider already defined. */
#elif defined(DUK_USE_DATE_NOW_GETTIMEOFDAY)
#define DUK_USE_DATE_GET_NOW(ctx)            duk_bi_date_get_now_gettimeofday()
#elif defined(DUK_USE_DATE_NOW_TIME)
#define DUK_USE_DATE_GET_NOW(ctx)            duk_bi_date_get_now_time()
#elif defined(DUK_USE_DATE_NOW_WINDOWS)
#define DUK_USE_DATE_GET_NOW(ctx)            duk_bi_date_get_now_windows()
#elif defined(DUK_USE_DATE_NOW_WINDOWS_SUBMS)
#define DUK_USE_DATE_GET_NOW(ctx)            duk_bi_date_get_now_windows_subms()
#else
#error no provider for DUK_USE_DATE_GET_NOW()
#endif

#if defined(DUK_USE_DATE_GET_LOCAL_TZOFFSET)
/* External provider already defined. */
#elif defined(DUK_USE_DATE_TZO_GMTIME_R) || defined(DUK_USE_DATE_TZO_GMTIME_S) || defined(DUK_USE_DATE_TZO_GMTIME)
#define DUK_USE_DATE_GET_LOCAL_TZOFFSET(d)   duk_bi_date_get_local_tzoffset_gmtime((d))
#elif defined(DUK_USE_DATE_TZO_WINDOWS)
#define DUK_USE_DATE_GET_LOCAL_TZOFFSET(d)   duk_bi_date_get_local_tzoffset_windows((d))
#elif defined(DUK_USE_DATE_TZO_WINDOWS_NO_DST)
#define DUK_USE_DATE_GET_LOCAL_TZOFFSET(d)   duk_bi_date_get_local_tzoffset_windows_no_dst((d))
#else
#error no provider for DUK_USE_DATE_GET_LOCAL_TZOFFSET()
#endif

#if defined(DUK_USE_DATE_PARSE_STRING)
/* External provider already defined. */
#elif defined(DUK_USE_DATE_PRS_STRPTIME)
#define DUK_USE_DATE_PARSE_STRING(ctx,str)   duk_bi_date_parse_string_strptime((ctx), (str))
#elif defined(DUK_USE_DATE_PRS_GETDATE)
#define DUK_USE_DATE_PARSE_STRING(ctx,str)   duk_bi_date_parse_string_getdate((ctx), (str))
#else
/* No provider for DUK_USE_DATE_PARSE_STRING(), fall back to ISO 8601 only. */
#endif

#if defined(DUK_USE_DATE_FORMAT_STRING)
/* External provider already defined. */
#elif defined(DUK_USE_DATE_FMT_STRFTIME)
#define DUK_USE_DATE_FORMAT_STRING(ctx,parts,tzoffset,flags) \
    duk_bi_date_format_parts_strftime((ctx), (parts), (tzoffset), (flags))
#else
/* No provider for DUK_USE_DATE_FORMAT_STRING(), fall back to ISO 8601 only. */
#endif

#if defined(DUK_USE_GET_MONOTONIC_TIME)
/* External provider already defined. */
#elif defined(DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME)
#define DUK_USE_GET_MONOTONIC_TIME(ctx)  duk_bi_date_get_monotonic_time_clock_gettime()
#elif defined(DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC)
#define DUK_USE_GET_MONOTONIC_TIME(ctx)  duk_bi_date_get_monotonic_time_windows_qpc()
#else
/* No provider for DUK_USE_GET_MONOTONIC_TIME(), fall back to DUK_USE_DATE_GET_NOW(). */
#endif

#endif  /* DUK_COMPILING_DUKTAPE */

/*
 *  Checks for legacy feature options (DUK_OPT_xxx)
 */

#if defined(DUK_OPT_ASSERTIONS)
#error unsupported legacy feature option DUK_OPT_ASSERTIONS used
#endif
#if defined(DUK_OPT_BUFFEROBJECT_SUPPORT)
#error unsupported legacy feature option DUK_OPT_BUFFEROBJECT_SUPPORT used
#endif
#if defined(DUK_OPT_BUFLEN16)
#error unsupported legacy feature option DUK_OPT_BUFLEN16 used
#endif
#if defined(DUK_OPT_DATAPTR16)
#error unsupported legacy feature option DUK_OPT_DATAPTR16 used
#endif
#if defined(DUK_OPT_DATAPTR_DEC16)
#error unsupported legacy feature option DUK_OPT_DATAPTR_DEC16 used
#endif
#if defined(DUK_OPT_DATAPTR_ENC16)
#error unsupported legacy feature option DUK_OPT_DATAPTR_ENC16 used
#endif
#if defined(DUK_OPT_DDDPRINT)
#error unsupported legacy feature option DUK_OPT_DDDPRINT used
#endif
#if defined(DUK_OPT_DDPRINT)
#error unsupported legacy feature option DUK_OPT_DDPRINT used
#endif
#if defined(DUK_OPT_DEBUG)
#error unsupported legacy feature option DUK_OPT_DEBUG used
#endif
#if defined(DUK_OPT_DEBUGGER_DUMPHEAP)
#error unsupported legacy feature option DUK_OPT_DEBUGGER_DUMPHEAP used
#endif
#if defined(DUK_OPT_DEBUGGER_FWD_LOGGING)
#error unsupported legacy feature option DUK_OPT_DEBUGGER_FWD_LOGGING used
#endif
#if defined(DUK_OPT_DEBUGGER_FWD_PRINTALERT)
#error unsupported legacy feature option DUK_OPT_DEBUGGER_FWD_PRINTALERT used
#endif
#if defined(DUK_OPT_DEBUGGER_SUPPORT)
#error unsupported legacy feature option DUK_OPT_DEBUGGER_SUPPORT used
#endif
#if defined(DUK_OPT_DEBUGGER_TRANSPORT_TORTURE)
#error unsupported legacy feature option DUK_OPT_DEBUGGER_TRANSPORT_TORTURE used
#endif
#if defined(DUK_OPT_DEBUG_BUFSIZE)
#error unsupported legacy feature option DUK_OPT_DEBUG_BUFSIZE used
#endif
#if defined(DUK_OPT_DECLARE)
#error unsupported legacy feature option DUK_OPT_DECLARE used
#endif
#if defined(DUK_OPT_DEEP_C_STACK)
#error unsupported legacy feature option DUK_OPT_DEEP_C_STACK used
#endif
#if defined(DUK_OPT_DLL_BUILD)
#error unsupported legacy feature option DUK_OPT_DLL_BUILD used
#endif
#if defined(DUK_OPT_DPRINT)
#error unsupported legacy feature option DUK_OPT_DPRINT used
#endif
#if defined(DUK_OPT_DPRINT_COLORS)
#error unsupported legacy feature option DUK_OPT_DPRINT_COLORS used
#endif
#if defined(DUK_OPT_DPRINT_RDTSC)
#error unsupported legacy feature option DUK_OPT_DPRINT_RDTSC used
#endif
#if defined(DUK_OPT_EXEC_TIMEOUT_CHECK)
#error unsupported legacy feature option DUK_OPT_EXEC_TIMEOUT_CHECK used
#endif
#if defined(DUK_OPT_EXTERNAL_STRINGS)
#error unsupported legacy feature option DUK_OPT_EXTERNAL_STRINGS used
#endif
#if defined(DUK_OPT_EXTSTR_FREE)
#error unsupported legacy feature option DUK_OPT_EXTSTR_FREE used
#endif
#if defined(DUK_OPT_EXTSTR_INTERN_CHECK)
#error unsupported legacy feature option DUK_OPT_EXTSTR_INTERN_CHECK used
#endif
#if defined(DUK_OPT_FASTINT)
#error unsupported legacy feature option DUK_OPT_FASTINT used
#endif
#if defined(DUK_OPT_FORCE_ALIGN)
#error unsupported legacy feature option DUK_OPT_FORCE_ALIGN used
#endif
#if defined(DUK_OPT_FORCE_BYTEORDER)
#error unsupported legacy feature option DUK_OPT_FORCE_BYTEORDER used
#endif
#if defined(DUK_OPT_FUNCPTR16)
#error unsupported legacy feature option DUK_OPT_FUNCPTR16 used
#endif
#if defined(DUK_OPT_FUNCPTR_DEC16)
#error unsupported legacy feature option DUK_OPT_FUNCPTR_DEC16 used
#endif
#if defined(DUK_OPT_FUNCPTR_ENC16)
#error unsupported legacy feature option DUK_OPT_FUNCPTR_ENC16 used
#endif
#if defined(DUK_OPT_FUNC_NONSTD_CALLER_PROPERTY)
#error unsupported legacy feature option DUK_OPT_FUNC_NONSTD_CALLER_PROPERTY used
#endif
#if defined(DUK_OPT_FUNC_NONSTD_SOURCE_PROPERTY)
#error unsupported legacy feature option DUK_OPT_FUNC_NONSTD_SOURCE_PROPERTY used
#endif
#if defined(DUK_OPT_GC_TORTURE)
#error unsupported legacy feature option DUK_OPT_GC_TORTURE used
#endif
#if defined(DUK_OPT_HAVE_CUSTOM_H)
#error unsupported legacy feature option DUK_OPT_HAVE_CUSTOM_H used
#endif
#if defined(DUK_OPT_HEAPPTR16)
#error unsupported legacy feature option DUK_OPT_HEAPPTR16 used
#endif
#if defined(DUK_OPT_HEAPPTR_DEC16)
#error unsupported legacy feature option DUK_OPT_HEAPPTR_DEC16 used
#endif
#if defined(DUK_OPT_HEAPPTR_ENC16)
#error unsupported legacy feature option DUK_OPT_HEAPPTR_ENC16 used
#endif
#if defined(DUK_OPT_INTERRUPT_COUNTER)
#error unsupported legacy feature option DUK_OPT_INTERRUPT_COUNTER used
#endif
#if defined(DUK_OPT_JSON_STRINGIFY_FASTPATH)
#error unsupported legacy feature option DUK_OPT_JSON_STRINGIFY_FASTPATH used
#endif
#if defined(DUK_OPT_LIGHTFUNC_BUILTINS)
#error unsupported legacy feature option DUK_OPT_LIGHTFUNC_BUILTINS used
#endif
#if defined(DUK_OPT_NONSTD_FUNC_CALLER_PROPERTY)
#error unsupported legacy feature option DUK_OPT_NONSTD_FUNC_CALLER_PROPERTY used
#endif
#if defined(DUK_OPT_NONSTD_FUNC_SOURCE_PROPERTY)
#error unsupported legacy feature option DUK_OPT_NONSTD_FUNC_SOURCE_PROPERTY used
#endif
#if defined(DUK_OPT_NO_ARRAY_SPLICE_NONSTD_DELCOUNT)
#error unsupported legacy feature option DUK_OPT_NO_ARRAY_SPLICE_NONSTD_DELCOUNT used
#endif
#if defined(DUK_OPT_NO_AUGMENT_ERRORS)
#error unsupported legacy feature option DUK_OPT_NO_AUGMENT_ERRORS used
#endif
#if defined(DUK_OPT_NO_BROWSER_LIKE)
#error unsupported legacy feature option DUK_OPT_NO_BROWSER_LIKE used
#endif
#if defined(DUK_OPT_NO_BUFFEROBJECT_SUPPORT)
#error unsupported legacy feature option DUK_OPT_NO_BUFFEROBJECT_SUPPORT used
#endif
#if defined(DUK_OPT_NO_BYTECODE_DUMP_SUPPORT)
#error unsupported legacy feature option DUK_OPT_NO_BYTECODE_DUMP_SUPPORT used
#endif
#if defined(DUK_OPT_NO_COMMONJS_MODULES)
#error unsupported legacy feature option DUK_OPT_NO_COMMONJS_MODULES used
#endif
#if defined(DUK_OPT_NO_ES6_OBJECT_PROTO_PROPERTY)
#error unsupported legacy feature option DUK_OPT_NO_ES6_OBJECT_PROTO_PROPERTY used
#endif
#if defined(DUK_OPT_NO_ES6_OBJECT_SETPROTOTYPEOF)
#error unsupported legacy feature option DUK_OPT_NO_ES6_OBJECT_SETPROTOTYPEOF used
#endif
#if defined(DUK_OPT_NO_ES6_PROXY)
#error unsupported legacy feature option DUK_OPT_NO_ES6_PROXY used
#endif
#if defined(DUK_OPT_NO_FILE_IO)
#error unsupported legacy feature option DUK_OPT_NO_FILE_IO used
#endif
#if defined(DUK_OPT_NO_FUNC_STMT)
#error unsupported legacy feature option DUK_OPT_NO_FUNC_STMT used
#endif
#if defined(DUK_OPT_NO_JC)
#error unsupported legacy feature option DUK_OPT_NO_JC used
#endif
#if defined(DUK_OPT_NO_JSONC)
#error unsupported legacy feature option DUK_OPT_NO_JSONC used
#endif
#if defined(DUK_OPT_NO_JSONX)
#error unsupported legacy feature option DUK_OPT_NO_JSONX used
#endif
#if defined(DUK_OPT_NO_JX)
#error unsupported legacy feature option DUK_OPT_NO_JX used
#endif
#if defined(DUK_OPT_NO_MARK_AND_SWEEP)
#error unsupported legacy feature option DUK_OPT_NO_MARK_AND_SWEEP used
#endif
#if defined(DUK_OPT_NO_MS_STRINGTABLE_RESIZE)
#error unsupported legacy feature option DUK_OPT_NO_MS_STRINGTABLE_RESIZE used
#endif
#if defined(DUK_OPT_NO_NONSTD_ACCESSOR_KEY_ARGUMENT)
#error unsupported legacy feature option DUK_OPT_NO_NONSTD_ACCESSOR_KEY_ARGUMENT used
#endif
#if defined(DUK_OPT_NO_NONSTD_ARRAY_CONCAT_TRAILER)
#error unsupported legacy feature option DUK_OPT_NO_NONSTD_ARRAY_CONCAT_TRAILER used
#endif
#if defined(DUK_OPT_NO_NONSTD_ARRAY_MAP_TRAILER)
#error unsupported legacy feature option DUK_OPT_NO_NONSTD_ARRAY_MAP_TRAILER used
#endif
#if defined(DUK_OPT_NO_NONSTD_ARRAY_SPLICE_DELCOUNT)
#error unsupported legacy feature option DUK_OPT_NO_NONSTD_ARRAY_SPLICE_DELCOUNT used
#endif
#if defined(DUK_OPT_NO_NONSTD_FUNC_STMT)
#error unsupported legacy feature option DUK_OPT_NO_NONSTD_FUNC_STMT used
#endif
#if defined(DUK_OPT_NO_NONSTD_JSON_ESC_U2028_U2029)
#error unsupported legacy feature option DUK_OPT_NO_NONSTD_JSON_ESC_U2028_U2029 used
#endif
#if defined(DUK_OPT_NO_NONSTD_STRING_FROMCHARCODE_32BIT)
#error unsupported legacy feature option DUK_OPT_NO_NONSTD_STRING_FROMCHARCODE_32BIT used
#endif
#if defined(DUK_OPT_NO_OBJECT_ES6_PROTO_PROPERTY)
#error unsupported legacy feature option DUK_OPT_NO_OBJECT_ES6_PROTO_PROPERTY used
#endif
#if defined(DUK_OPT_NO_OBJECT_ES6_SETPROTOTYPEOF)
#error unsupported legacy feature option DUK_OPT_NO_OBJECT_ES6_SETPROTOTYPEOF used
#endif
#if defined(DUK_OPT_NO_OCTAL_SUPPORT)
#error unsupported legacy feature option DUK_OPT_NO_OCTAL_SUPPORT used
#endif
#if defined(DUK_OPT_NO_PACKED_TVAL)
#error unsupported legacy feature option DUK_OPT_NO_PACKED_TVAL used
#endif
#if defined(DUK_OPT_NO_PC2LINE)
#error unsupported legacy feature option DUK_OPT_NO_PC2LINE used
#endif
#if defined(DUK_OPT_NO_REFERENCE_COUNTING)
#error unsupported legacy feature option DUK_OPT_NO_REFERENCE_COUNTING used
#endif
#if defined(DUK_OPT_NO_REGEXP_SUPPORT)
#error unsupported legacy feature option DUK_OPT_NO_REGEXP_SUPPORT used
#endif
#if defined(DUK_OPT_NO_SECTION_B)
#error unsupported legacy feature option DUK_OPT_NO_SECTION_B used
#endif
#if defined(DUK_OPT_NO_SOURCE_NONBMP)
#error unsupported legacy feature option DUK_OPT_NO_SOURCE_NONBMP used
#endif
#if defined(DUK_OPT_NO_STRICT_DECL)
#error unsupported legacy feature option DUK_OPT_NO_STRICT_DECL used
#endif
#if defined(DUK_OPT_NO_TRACEBACKS)
#error unsupported legacy feature option DUK_OPT_NO_TRACEBACKS used
#endif
#if defined(DUK_OPT_NO_VERBOSE_ERRORS)
#error unsupported legacy feature option DUK_OPT_NO_VERBOSE_ERRORS used
#endif
#if defined(DUK_OPT_NO_VOLUNTARY_GC)
#error unsupported legacy feature option DUK_OPT_NO_VOLUNTARY_GC used
#endif
#if defined(DUK_OPT_NO_ZERO_BUFFER_DATA)
#error unsupported legacy feature option DUK_OPT_NO_ZERO_BUFFER_DATA used
#endif
#if defined(DUK_OPT_OBJSIZES16)
#error unsupported legacy feature option DUK_OPT_OBJSIZES16 used
#endif
#if defined(DUK_OPT_PANIC_HANDLER)
#error unsupported legacy feature option DUK_OPT_PANIC_HANDLER used
#endif
#if defined(DUK_OPT_REFCOUNT16)
#error unsupported legacy feature option DUK_OPT_REFCOUNT16 used
#endif
#if defined(DUK_OPT_SEGFAULT_ON_PANIC)
#error unsupported legacy feature option DUK_OPT_SEGFAULT_ON_PANIC used
#endif
#if defined(DUK_OPT_SELF_TESTS)
#error unsupported legacy feature option DUK_OPT_SELF_TESTS used
#endif
#if defined(DUK_OPT_SETJMP)
#error unsupported legacy feature option DUK_OPT_SETJMP used
#endif
#if defined(DUK_OPT_SHUFFLE_TORTURE)
#error unsupported legacy feature option DUK_OPT_SHUFFLE_TORTURE used
#endif
#if defined(DUK_OPT_SIGSETJMP)
#error unsupported legacy feature option DUK_OPT_SIGSETJMP used
#endif
#if defined(DUK_OPT_STRHASH16)
#error unsupported legacy feature option DUK_OPT_STRHASH16 used
#endif
#if defined(DUK_OPT_STRICT_UTF8_SOURCE)
#error unsupported legacy feature option DUK_OPT_STRICT_UTF8_SOURCE used
#endif
#if defined(DUK_OPT_STRLEN16)
#error unsupported legacy feature option DUK_OPT_STRLEN16 used
#endif
#if defined(DUK_OPT_STRTAB_CHAIN)
#error unsupported legacy feature option DUK_OPT_STRTAB_CHAIN used
#endif
#if defined(DUK_OPT_STRTAB_CHAIN_SIZE)
#error unsupported legacy feature option DUK_OPT_STRTAB_CHAIN_SIZE used
#endif
#if defined(DUK_OPT_TARGET_INFO)
#error unsupported legacy feature option DUK_OPT_TARGET_INFO used
#endif
#if defined(DUK_OPT_TRACEBACK_DEPTH)
#error unsupported legacy feature option DUK_OPT_TRACEBACK_DEPTH used
#endif
#if defined(DUK_OPT_UNDERSCORE_SETJMP)
#error unsupported legacy feature option DUK_OPT_UNDERSCORE_SETJMP used
#endif
#if defined(DUK_OPT_USER_INITJS)
#error unsupported legacy feature option DUK_OPT_USER_INITJS used
#endif

/*
 *  Checks for config option consistency (DUK_USE_xxx)
 */

#if defined(DUK_USE_32BIT_PTRS)
#error unsupported config option used (option has been removed): DUK_USE_32BIT_PTRS
#endif
#if defined(DUK_USE_ALIGN_4)
#error unsupported config option used (option has been removed): DUK_USE_ALIGN_4
#endif
#if defined(DUK_USE_ALIGN_8)
#error unsupported config option used (option has been removed): DUK_USE_ALIGN_8
#endif
#if defined(DUK_USE_BROWSER_LIKE)
#error unsupported config option used (option has been removed): DUK_USE_BROWSER_LIKE
#endif
#if defined(DUK_USE_BUILTIN_INITJS)
#error unsupported config option used (option has been removed): DUK_USE_BUILTIN_INITJS
#endif
#if defined(DUK_USE_BYTEORDER_FORCED)
#error unsupported config option used (option has been removed): DUK_USE_BYTEORDER_FORCED
#endif
#if defined(DUK_USE_COMMONJS_MODULES)
#error unsupported config option used (option has been removed): DUK_USE_COMMONJS_MODULES
#endif
#if defined(DUK_USE_DATAPTR_DEC16) && !defined(DUK_USE_DATAPTR16)
#error config option DUK_USE_DATAPTR_DEC16 requires option DUK_USE_DATAPTR16 (which is missing)
#endif
#if defined(DUK_USE_DATAPTR_ENC16) && !defined(DUK_USE_DATAPTR16)
#error config option DUK_USE_DATAPTR_ENC16 requires option DUK_USE_DATAPTR16 (which is missing)
#endif
#if defined(DUK_USE_DDDPRINT)
#error unsupported config option used (option has been removed): DUK_USE_DDDPRINT
#endif
#if defined(DUK_USE_DDPRINT)
#error unsupported config option used (option has been removed): DUK_USE_DDPRINT
#endif
#if defined(DUK_USE_DEBUGGER_FWD_LOGGING)
#error unsupported config option used (option has been removed): DUK_USE_DEBUGGER_FWD_LOGGING
#endif
#if defined(DUK_USE_DEBUGGER_FWD_PRINTALERT)
#error unsupported config option used (option has been removed): DUK_USE_DEBUGGER_FWD_PRINTALERT
#endif
#if defined(DUK_USE_DEBUGGER_SUPPORT) && !defined(DUK_USE_INTERRUPT_COUNTER)
#error config option DUK_USE_DEBUGGER_SUPPORT requires option DUK_USE_INTERRUPT_COUNTER (which is missing)
#endif
#if defined(DUK_USE_DEEP_C_STACK)
#error unsupported config option used (option has been removed): DUK_USE_DEEP_C_STACK
#endif
#if defined(DUK_USE_DOUBLE_BE)
#error unsupported config option used (option has been removed): DUK_USE_DOUBLE_BE
#endif
#if defined(DUK_USE_DOUBLE_BE) && defined(DUK_USE_DOUBLE_LE)
#error config option DUK_USE_DOUBLE_BE conflicts with option DUK_USE_DOUBLE_LE (which is also defined)
#endif
#if defined(DUK_USE_DOUBLE_BE) && defined(DUK_USE_DOUBLE_ME)
#error config option DUK_USE_DOUBLE_BE conflicts with option DUK_USE_DOUBLE_ME (which is also defined)
#endif
#if defined(DUK_USE_DOUBLE_LE)
#error unsupported config option used (option has been removed): DUK_USE_DOUBLE_LE
#endif
#if defined(DUK_USE_DOUBLE_LE) && defined(DUK_USE_DOUBLE_BE)
#error config option DUK_USE_DOUBLE_LE conflicts with option DUK_USE_DOUBLE_BE (which is also defined)
#endif
#if defined(DUK_USE_DOUBLE_LE) && defined(DUK_USE_DOUBLE_ME)
#error config option DUK_USE_DOUBLE_LE conflicts with option DUK_USE_DOUBLE_ME (which is also defined)
#endif
#if defined(DUK_USE_DOUBLE_ME)
#error unsupported config option used (option has been removed): DUK_USE_DOUBLE_ME
#endif
#if defined(DUK_USE_DOUBLE_ME) && defined(DUK_USE_DOUBLE_LE)
#error config option DUK_USE_DOUBLE_ME conflicts with option DUK_USE_DOUBLE_LE (which is also defined)
#endif
#if defined(DUK_USE_DOUBLE_ME) && defined(DUK_USE_DOUBLE_BE)
#error config option DUK_USE_DOUBLE_ME conflicts with option DUK_USE_DOUBLE_BE (which is also defined)
#endif
#if defined(DUK_USE_DPRINT)
#error unsupported config option used (option has been removed): DUK_USE_DPRINT
#endif
#if defined(DUK_USE_DPRINT) && !defined(DUK_USE_DEBUG)
#error config option DUK_USE_DPRINT requires option DUK_USE_DEBUG (which is missing)
#endif
#if defined(DUK_USE_DPRINT_COLORS)
#error unsupported config option used (option has been removed): DUK_USE_DPRINT_COLORS
#endif
#if defined(DUK_USE_DPRINT_RDTSC)
#error unsupported config option used (option has been removed): DUK_USE_DPRINT_RDTSC
#endif
#if defined(DUK_USE_ES6_REGEXP_BRACES)
#error unsupported config option used (option has been removed): DUK_USE_ES6_REGEXP_BRACES
#endif
#if defined(DUK_USE_ESBC_MAX_BYTES) && !defined(DUK_USE_ESBC_LIMITS)
#error config option DUK_USE_ESBC_MAX_BYTES requires option DUK_USE_ESBC_LIMITS (which is missing)
#endif
#if defined(DUK_USE_ESBC_MAX_LINENUMBER) && !defined(DUK_USE_ESBC_LIMITS)
#error config option DUK_USE_ESBC_MAX_LINENUMBER requires option DUK_USE_ESBC_LIMITS (which is missing)
#endif
#if defined(DUK_USE_EXEC_TIMEOUT_CHECK) && !defined(DUK_USE_INTERRUPT_COUNTER)
#error config option DUK_USE_EXEC_TIMEOUT_CHECK requires option DUK_USE_INTERRUPT_COUNTER (which is missing)
#endif
#if defined(DUK_USE_EXTSTR_FREE) && !defined(DUK_USE_HSTRING_EXTDATA)
#error config option DUK_USE_EXTSTR_FREE requires option DUK_USE_HSTRING_EXTDATA (which is missing)
#endif
#if defined(DUK_USE_EXTSTR_INTERN_CHECK) && !defined(DUK_USE_HSTRING_EXTDATA)
#error config option DUK_USE_EXTSTR_INTERN_CHECK requires option DUK_USE_HSTRING_EXTDATA (which is missing)
#endif
#if defined(DUK_USE_FASTINT) && !defined(DUK_USE_64BIT_OPS)
#error config option DUK_USE_FASTINT requires option DUK_USE_64BIT_OPS (which is missing)
#endif
#if defined(DUK_USE_FILE_IO)
#error unsupported config option used (option has been removed): DUK_USE_FILE_IO
#endif
#if defined(DUK_USE_FULL_TVAL)
#error unsupported config option used (option has been removed): DUK_USE_FULL_TVAL
#endif
#if defined(DUK_USE_FUNCPTR_DEC16) && !defined(DUK_USE_FUNCPTR16)
#error config option DUK_USE_FUNCPTR_DEC16 requires option DUK_USE_FUNCPTR16 (which is missing)
#endif
#if defined(DUK_USE_FUNCPTR_ENC16) && !defined(DUK_USE_FUNCPTR16)
#error config option DUK_USE_FUNCPTR_ENC16 requires option DUK_USE_FUNCPTR16 (which is missing)
#endif
#if defined(DUK_USE_HASHBYTES_UNALIGNED_U32_ACCESS)
#error unsupported config option used (option has been removed): DUK_USE_HASHBYTES_UNALIGNED_U32_ACCESS
#endif
#if defined(DUK_USE_HEAPPTR16) && defined(DUK_USE_DEBUG)
#error config option DUK_USE_HEAPPTR16 conflicts with option DUK_USE_DEBUG (which is also defined)
#endif
#if defined(DUK_USE_HEAPPTR_DEC16) && !defined(DUK_USE_HEAPPTR16)
#error config option DUK_USE_HEAPPTR_DEC16 requires option DUK_USE_HEAPPTR16 (which is missing)
#endif
#if defined(DUK_USE_HEAPPTR_ENC16) && !defined(DUK_USE_HEAPPTR16)
#error config option DUK_USE_HEAPPTR_ENC16 requires option DUK_USE_HEAPPTR16 (which is missing)
#endif
#if defined(DUK_USE_INTEGER_BE)
#error unsupported config option used (option has been removed): DUK_USE_INTEGER_BE
#endif
#if defined(DUK_USE_INTEGER_BE) && defined(DUK_USE_INTEGER_LE)
#error config option DUK_USE_INTEGER_BE conflicts with option DUK_USE_INTEGER_LE (which is also defined)
#endif
#if defined(DUK_USE_INTEGER_BE) && defined(DUK_USE_INTEGER_ME)
#error config option DUK_USE_INTEGER_BE conflicts with option DUK_USE_INTEGER_ME (which is also defined)
#endif
#if defined(DUK_USE_INTEGER_LE)
#error unsupported config option used (option has been removed): DUK_USE_INTEGER_LE
#endif
#if defined(DUK_USE_INTEGER_LE) && defined(DUK_USE_INTEGER_BE)
#error config option DUK_USE_INTEGER_LE conflicts with option DUK_USE_INTEGER_BE (which is also defined)
#endif
#if defined(DUK_USE_INTEGER_LE) && defined(DUK_USE_INTEGER_ME)
#error config option DUK_USE_INTEGER_LE conflicts with option DUK_USE_INTEGER_ME (which is also defined)
#endif
#if defined(DUK_USE_INTEGER_ME)
#error unsupported config option used (option has been removed): DUK_USE_INTEGER_ME
#endif
#if defined(DUK_USE_INTEGER_ME) && defined(DUK_USE_INTEGER_LE)
#error config option DUK_USE_INTEGER_ME conflicts with option DUK_USE_INTEGER_LE (which is also defined)
#endif
#if defined(DUK_USE_INTEGER_ME) && defined(DUK_USE_INTEGER_BE)
#error config option DUK_USE_INTEGER_ME conflicts with option DUK_USE_INTEGER_BE (which is also defined)
#endif
#if defined(DUK_USE_MARKANDSWEEP_FINALIZER_TORTURE)
#error unsupported config option used (option has been removed): DUK_USE_MARKANDSWEEP_FINALIZER_TORTURE
#endif
#if defined(DUK_USE_MARK_AND_SWEEP)
#error unsupported config option used (option has been removed): DUK_USE_MARK_AND_SWEEP
#endif
#if defined(DUK_USE_MATH_FMAX)
#error unsupported config option used (option has been removed): DUK_USE_MATH_FMAX
#endif
#if defined(DUK_USE_MATH_FMIN)
#error unsupported config option used (option has been removed): DUK_USE_MATH_FMIN
#endif
#if defined(DUK_USE_MATH_ROUND)
#error unsupported config option used (option has been removed): DUK_USE_MATH_ROUND
#endif
#if defined(DUK_USE_MS_STRINGTABLE_RESIZE)
#error unsupported config option used (option has been removed): DUK_USE_MS_STRINGTABLE_RESIZE
#endif
#if defined(DUK_USE_NONSTD_ARRAY_CONCAT_TRAILER)
#error unsupported config option used (option has been removed): DUK_USE_NONSTD_ARRAY_CONCAT_TRAILER
#endif
#if defined(DUK_USE_NONSTD_ARRAY_MAP_TRAILER)
#error unsupported config option used (option has been removed): DUK_USE_NONSTD_ARRAY_MAP_TRAILER
#endif
#if defined(DUK_USE_NONSTD_REGEXP_DOLLAR_ESCAPE)
#error unsupported config option used (option has been removed): DUK_USE_NONSTD_REGEXP_DOLLAR_ESCAPE
#endif
#if defined(DUK_USE_NO_DOUBLE_ALIASING_SELFTEST)
#error unsupported config option used (option has been removed): DUK_USE_NO_DOUBLE_ALIASING_SELFTEST
#endif
#if defined(DUK_USE_OCTAL_SUPPORT)
#error unsupported config option used (option has been removed): DUK_USE_OCTAL_SUPPORT
#endif
#if defined(DUK_USE_PACKED_TVAL_POSSIBLE)
#error unsupported config option used (option has been removed): DUK_USE_PACKED_TVAL_POSSIBLE
#endif
#if defined(DUK_USE_PANIC_ABORT)
#error unsupported config option used (option has been removed): DUK_USE_PANIC_ABORT
#endif
#if defined(DUK_USE_PANIC_EXIT)
#error unsupported config option used (option has been removed): DUK_USE_PANIC_EXIT
#endif
#if defined(DUK_USE_PANIC_HANDLER)
#error unsupported config option used (option has been removed): DUK_USE_PANIC_HANDLER
#endif
#if defined(DUK_USE_PANIC_SEGFAULT)
#error unsupported config option used (option has been removed): DUK_USE_PANIC_SEGFAULT
#endif
#if defined(DUK_USE_POW_NETBSD_WORKAROUND)
#error unsupported config option used (option has been removed): DUK_USE_POW_NETBSD_WORKAROUND
#endif
#if defined(DUK_USE_RDTSC)
#error unsupported config option used (option has been removed): DUK_USE_RDTSC
#endif
#if defined(DUK_USE_REFZERO_FINALIZER_TORTURE)
#error unsupported config option used (option has been removed): DUK_USE_REFZERO_FINALIZER_TORTURE
#endif
#if defined(DUK_USE_ROM_GLOBAL_CLONE) && !defined(DUK_USE_ROM_STRINGS)
#error config option DUK_USE_ROM_GLOBAL_CLONE requires option DUK_USE_ROM_STRINGS (which is missing)
#endif
#if defined(DUK_USE_ROM_GLOBAL_CLONE) && !defined(DUK_USE_ROM_OBJECTS)
#error config option DUK_USE_ROM_GLOBAL_CLONE requires option DUK_USE_ROM_OBJECTS (which is missing)
#endif
#if defined(DUK_USE_ROM_GLOBAL_CLONE) && defined(DUK_USE_ROM_GLOBAL_INHERIT)
#error config option DUK_USE_ROM_GLOBAL_CLONE conflicts with option DUK_USE_ROM_GLOBAL_INHERIT (which is also defined)
#endif
#if defined(DUK_USE_ROM_GLOBAL_INHERIT) && !defined(DUK_USE_ROM_STRINGS)
#error config option DUK_USE_ROM_GLOBAL_INHERIT requires option DUK_USE_ROM_STRINGS (which is missing)
#endif
#if defined(DUK_USE_ROM_GLOBAL_INHERIT) && !defined(DUK_USE_ROM_OBJECTS)
#error config option DUK_USE_ROM_GLOBAL_INHERIT requires option DUK_USE_ROM_OBJECTS (which is missing)
#endif
#if defined(DUK_USE_ROM_GLOBAL_INHERIT) && defined(DUK_USE_ROM_GLOBAL_CLONE)
#error config option DUK_USE_ROM_GLOBAL_INHERIT conflicts with option DUK_USE_ROM_GLOBAL_CLONE (which is also defined)
#endif
#if defined(DUK_USE_ROM_OBJECTS) && !defined(DUK_USE_ROM_STRINGS)
#error config option DUK_USE_ROM_OBJECTS requires option DUK_USE_ROM_STRINGS (which is missing)
#endif
#if defined(DUK_USE_ROM_STRINGS) && !defined(DUK_USE_ROM_OBJECTS)
#error config option DUK_USE_ROM_STRINGS requires option DUK_USE_ROM_OBJECTS (which is missing)
#endif
#if defined(DUK_USE_SETJMP)
#error unsupported config option used (option has been removed): DUK_USE_SETJMP
#endif
#if defined(DUK_USE_SIGSETJMP)
#error unsupported config option used (option has been removed): DUK_USE_SIGSETJMP
#endif
#if defined(DUK_USE_STRTAB_CHAIN)
#error unsupported config option used (option has been removed): DUK_USE_STRTAB_CHAIN
#endif
#if defined(DUK_USE_STRTAB_CHAIN_SIZE)
#error unsupported config option used (option has been removed): DUK_USE_STRTAB_CHAIN_SIZE
#endif
#if defined(DUK_USE_STRTAB_CHAIN_SIZE) && !defined(DUK_USE_STRTAB_CHAIN)
#error config option DUK_USE_STRTAB_CHAIN_SIZE requires option DUK_USE_STRTAB_CHAIN (which is missing)
#endif
#if defined(DUK_USE_STRTAB_PROBE)
#error unsupported config option used (option has been removed): DUK_USE_STRTAB_PROBE
#endif
#if defined(DUK_USE_STRTAB_PTRCOMP) && !defined(DUK_USE_HEAPPTR16)
#error config option DUK_USE_STRTAB_PTRCOMP requires option DUK_USE_HEAPPTR16 (which is missing)
#endif
#if defined(DUK_USE_TAILCALL) && defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
#error config option DUK_USE_TAILCALL conflicts with option DUK_USE_NONSTD_FUNC_CALLER_PROPERTY (which is also defined)
#endif
#if defined(DUK_USE_UNALIGNED_ACCESSES_POSSIBLE)
#error unsupported config option used (option has been removed): DUK_USE_UNALIGNED_ACCESSES_POSSIBLE
#endif
#if defined(DUK_USE_UNDERSCORE_SETJMP)
#error unsupported config option used (option has been removed): DUK_USE_UNDERSCORE_SETJMP
#endif
#if defined(DUK_USE_USER_DECLARE)
#error unsupported config option used (option has been removed): DUK_USE_USER_DECLARE
#endif
#if defined(DUK_USE_USER_INITJS)
#error unsupported config option used (option has been removed): DUK_USE_USER_INITJS
#endif

#if defined(DUK_USE_CPP_EXCEPTIONS) && !defined(__cplusplus)
#error DUK_USE_CPP_EXCEPTIONS enabled but not compiling with a C++ compiler
#endif

/*
 *  Convert DUK_USE_BYTEORDER, from whatever source, into currently used
 *  internal defines.  If detection failed, #error out.
 */

#if defined(DUK_USE_BYTEORDER)
#if (DUK_USE_BYTEORDER == 1)
#define DUK_USE_INTEGER_LE
#define DUK_USE_DOUBLE_LE
#elif (DUK_USE_BYTEORDER == 2)
#define DUK_USE_INTEGER_LE  /* integer endianness is little on purpose */
#define DUK_USE_DOUBLE_ME
#elif (DUK_USE_BYTEORDER == 3)
#define DUK_USE_INTEGER_BE
#define DUK_USE_DOUBLE_BE
#else
#error unsupported: byte order invalid
#endif  /* byte order */
#else
#error unsupported: byte order detection failed
#endif  /* defined(DUK_USE_BYTEORDER) */


/*
 *  Avoid C++ name mangling
 */

#if defined(__cplusplus)
extern "C" {
#endif

/*
 *  Some defines forwarded from feature detection
 */

#undef DUK_API_VARIADIC_MACROS
#if defined(DUK_USE_VARIADIC_MACROS)
#define DUK_API_VARIADIC_MACROS
#endif

#define DUK_API_NORETURN(decl) DUK_NORETURN(decl)

/*
 *  Public API specific typedefs
 *
 *  Many types are wrapped by Duktape for portability to rare platforms
 *  where e.g. 'int' is a 16-bit type.  See practical typing discussion
 *  in Duktape web documentation.
 */

struct duk_thread_state;
struct duk_memory_functions;
struct duk_function_list_entry;
struct duk_number_list_entry;
struct duk_time_components;

/* duk_context is now defined in duk_config.h because it may also be
 * referenced there by prototypes.
 */
typedef struct duk_thread_state duk_thread_state;
typedef struct duk_memory_functions duk_memory_functions;
typedef struct duk_function_list_entry duk_function_list_entry;
typedef struct duk_number_list_entry duk_number_list_entry;
typedef struct duk_time_components duk_time_components;

typedef duk_ret_t (*duk_c_function)(duk_context *ctx);
typedef void *(*duk_alloc_function) (void *udata, duk_size_t size);
typedef void *(*duk_realloc_function) (void *udata, void *ptr, duk_size_t size);
typedef void (*duk_free_function) (void *udata, void *ptr);
typedef void (*duk_fatal_function) (void *udata, const char *msg);
typedef void (*duk_decode_char_function) (void *udata, duk_codepoint_t codepoint);
typedef duk_codepoint_t (*duk_map_char_function) (void *udata, duk_codepoint_t codepoint);
typedef duk_ret_t (*duk_safe_call_function) (duk_context *ctx, void *udata);
typedef duk_size_t (*duk_debug_read_function) (void *udata, char *buffer, duk_size_t length);
typedef duk_size_t (*duk_debug_write_function) (void *udata, const char *buffer, duk_size_t length);
typedef duk_size_t (*duk_debug_peek_function) (void *udata);
typedef void (*duk_debug_read_flush_function) (void *udata);
typedef void (*duk_debug_write_flush_function) (void *udata);
typedef duk_idx_t (*duk_debug_request_function) (duk_context *ctx, void *udata, duk_idx_t nvalues);
typedef void (*duk_debug_detached_function) (duk_context *ctx, void *udata);

struct duk_thread_state {
    /* XXX: Enough space to hold internal suspend/resume structure.
     * This is rather awkward and to be fixed when the internal
     * structure is visible for the public API header.
     */
    char data[128];
};

struct duk_memory_functions {
    duk_alloc_function alloc_func;
    duk_realloc_function realloc_func;
    duk_free_function free_func;
    void *udata;
};

struct duk_function_list_entry {
    const char *key;
    duk_c_function value;
    duk_idx_t nargs;
};

struct duk_number_list_entry {
    const char *key;
    duk_double_t value;
};

struct duk_time_components {
    duk_double_t year;          /* year, e.g. 2016, ECMAScript year range */
    duk_double_t month;         /* month: 1-12 */
    duk_double_t day;           /* day: 1-31 */
    duk_double_t hours;         /* hour: 0-59 */
    duk_double_t minutes;       /* minute: 0-59 */
    duk_double_t seconds;       /* second: 0-59 (in POSIX time no leap second) */
    duk_double_t milliseconds;  /* may contain sub-millisecond fractions */
    duk_double_t weekday;       /* weekday: 0-6, 0=Sunday, 1=Monday, ..., 6=Saturday */
};

/*
 *  Constants
 */

/* Duktape debug protocol version used by this build. */
#define DUK_DEBUG_PROTOCOL_VERSION        2

/* Used to represent invalid index; if caller uses this without checking,
 * this index will map to a non-existent stack entry.  Also used in some
 * API calls as a marker to denote "no value".
 */
#define DUK_INVALID_INDEX                 DUK_IDX_MIN

/* Indicates that a native function does not have a fixed number of args,
 * and the argument stack should not be capped/extended at all.
 */
#define DUK_VARARGS                       ((duk_int_t) (-1))

/* Number of value stack entries (in addition to actual call arguments)
 * guaranteed to be allocated on entry to a Duktape/C function.
 */
#define DUK_API_ENTRY_STACK               64U

/* Value types, used by e.g. duk_get_type() */
#define DUK_TYPE_MIN                      0U
#define DUK_TYPE_NONE                     0U    /* no value, e.g. invalid index */
#define DUK_TYPE_UNDEFINED                1U    /* ECMAScript undefined */
#define DUK_TYPE_NULL                     2U    /* ECMAScript null */
#define DUK_TYPE_BOOLEAN                  3U    /* ECMAScript boolean: 0 or 1 */
#define DUK_TYPE_NUMBER                   4U    /* ECMAScript number: double */
#define DUK_TYPE_STRING                   5U    /* ECMAScript string: CESU-8 / extended UTF-8 encoded */
#define DUK_TYPE_OBJECT                   6U    /* ECMAScript object: includes objects, arrays, functions, threads */
#define DUK_TYPE_BUFFER                   7U    /* fixed or dynamic, garbage collected byte buffer */
#define DUK_TYPE_POINTER                  8U    /* raw void pointer */
#define DUK_TYPE_LIGHTFUNC                9U    /* lightweight function pointer */
#define DUK_TYPE_MAX                      9U

/* Value mask types, used by e.g. duk_get_type_mask() */
#define DUK_TYPE_MASK_NONE                (1U << DUK_TYPE_NONE)
#define DUK_TYPE_MASK_UNDEFINED           (1U << DUK_TYPE_UNDEFINED)
#define DUK_TYPE_MASK_NULL                (1U << DUK_TYPE_NULL)
#define DUK_TYPE_MASK_BOOLEAN             (1U << DUK_TYPE_BOOLEAN)
#define DUK_TYPE_MASK_NUMBER              (1U << DUK_TYPE_NUMBER)
#define DUK_TYPE_MASK_STRING              (1U << DUK_TYPE_STRING)
#define DUK_TYPE_MASK_OBJECT              (1U << DUK_TYPE_OBJECT)
#define DUK_TYPE_MASK_BUFFER              (1U << DUK_TYPE_BUFFER)
#define DUK_TYPE_MASK_POINTER             (1U << DUK_TYPE_POINTER)
#define DUK_TYPE_MASK_LIGHTFUNC           (1U << DUK_TYPE_LIGHTFUNC)
#define DUK_TYPE_MASK_THROW               (1U << 10)  /* internal flag value: throw if mask doesn't match */
#define DUK_TYPE_MASK_PROMOTE             (1U << 11)  /* internal flag value: promote to object if mask matches */

/* Coercion hints */
#define DUK_HINT_NONE                     0    /* prefer number, unless input is a Date, in which
                                                * case prefer string (E5 Section 8.12.8)
                                                */
#define DUK_HINT_STRING                   1    /* prefer string */
#define DUK_HINT_NUMBER                   2    /* prefer number */

/* Enumeration flags for duk_enum() */
#define DUK_ENUM_INCLUDE_NONENUMERABLE    (1U << 0)    /* enumerate non-numerable properties in addition to enumerable */
#define DUK_ENUM_INCLUDE_HIDDEN           (1U << 1)    /* enumerate hidden symbols too (in Duktape 1.x called internal properties) */
#define DUK_ENUM_INCLUDE_SYMBOLS          (1U << 2)    /* enumerate symbols */
#define DUK_ENUM_EXCLUDE_STRINGS          (1U << 3)    /* exclude strings */
#define DUK_ENUM_OWN_PROPERTIES_ONLY      (1U << 4)    /* don't walk prototype chain, only check own properties */
#define DUK_ENUM_ARRAY_INDICES_ONLY       (1U << 5)    /* only enumerate array indices */
/* XXX: misleading name */
#define DUK_ENUM_SORT_ARRAY_INDICES       (1U << 6)    /* sort array indices (applied to full enumeration result, including inherited array indices); XXX: misleading name */
#define DUK_ENUM_NO_PROXY_BEHAVIOR        (1U << 7)    /* enumerate a proxy object itself without invoking proxy behavior */

/* Compilation flags for duk_compile() and duk_eval() */
/* DUK_COMPILE_xxx bits 0-2 are reserved for an internal 'nargs' argument.
 */
#define DUK_COMPILE_EVAL                  (1U << 3)    /* compile eval code (instead of global code) */
#define DUK_COMPILE_FUNCTION              (1U << 4)    /* compile function code (instead of global code) */
#define DUK_COMPILE_STRICT                (1U << 5)    /* use strict (outer) context for global, eval, or function code */
#define DUK_COMPILE_SHEBANG               (1U << 6)    /* allow shebang ('#! ...') comment on first line of source */
#define DUK_COMPILE_SAFE                  (1U << 7)    /* (internal) catch compilation errors */
#define DUK_COMPILE_NORESULT              (1U << 8)    /* (internal) omit eval result */
#define DUK_COMPILE_NOSOURCE              (1U << 9)    /* (internal) no source string on stack */
#define DUK_COMPILE_STRLEN                (1U << 10)   /* (internal) take strlen() of src_buffer (avoids double evaluation in macro) */
#define DUK_COMPILE_NOFILENAME            (1U << 11)   /* (internal) no filename on stack */
#define DUK_COMPILE_FUNCEXPR              (1U << 12)   /* (internal) source is a function expression (used for Function constructor) */

/* Flags for duk_def_prop() and its variants; base flags + a lot of convenience shorthands */
#define DUK_DEFPROP_WRITABLE              (1U << 0)    /* set writable (effective if DUK_DEFPROP_HAVE_WRITABLE set) */
#define DUK_DEFPROP_ENUMERABLE            (1U << 1)    /* set enumerable (effective if DUK_DEFPROP_HAVE_ENUMERABLE set) */
#define DUK_DEFPROP_CONFIGURABLE          (1U << 2)    /* set configurable (effective if DUK_DEFPROP_HAVE_CONFIGURABLE set) */
#define DUK_DEFPROP_HAVE_WRITABLE         (1U << 3)    /* set/clear writable */
#define DUK_DEFPROP_HAVE_ENUMERABLE       (1U << 4)    /* set/clear enumerable */
#define DUK_DEFPROP_HAVE_CONFIGURABLE     (1U << 5)    /* set/clear configurable */
#define DUK_DEFPROP_HAVE_VALUE            (1U << 6)    /* set value (given on value stack) */
#define DUK_DEFPROP_HAVE_GETTER           (1U << 7)    /* set getter (given on value stack) */
#define DUK_DEFPROP_HAVE_SETTER           (1U << 8)    /* set setter (given on value stack) */
#define DUK_DEFPROP_FORCE                 (1U << 9)    /* force change if possible, may still fail for e.g. virtual properties */
#define DUK_DEFPROP_SET_WRITABLE          (DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_WRITABLE)
#define DUK_DEFPROP_CLEAR_WRITABLE        DUK_DEFPROP_HAVE_WRITABLE
#define DUK_DEFPROP_SET_ENUMERABLE        (DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE)
#define DUK_DEFPROP_CLEAR_ENUMERABLE      DUK_DEFPROP_HAVE_ENUMERABLE
#define DUK_DEFPROP_SET_CONFIGURABLE      (DUK_DEFPROP_HAVE_CONFIGURABLE | DUK_DEFPROP_CONFIGURABLE)
#define DUK_DEFPROP_CLEAR_CONFIGURABLE    DUK_DEFPROP_HAVE_CONFIGURABLE
#define DUK_DEFPROP_W                     DUK_DEFPROP_WRITABLE
#define DUK_DEFPROP_E                     DUK_DEFPROP_ENUMERABLE
#define DUK_DEFPROP_C                     DUK_DEFPROP_CONFIGURABLE
#define DUK_DEFPROP_WE                    (DUK_DEFPROP_WRITABLE | DUK_DEFPROP_ENUMERABLE)
#define DUK_DEFPROP_WC                    (DUK_DEFPROP_WRITABLE | DUK_DEFPROP_CONFIGURABLE)
#define DUK_DEFPROP_EC                    (DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_CONFIGURABLE)
#define DUK_DEFPROP_WEC                   (DUK_DEFPROP_WRITABLE | DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_CONFIGURABLE)
#define DUK_DEFPROP_HAVE_W                DUK_DEFPROP_HAVE_WRITABLE
#define DUK_DEFPROP_HAVE_E                DUK_DEFPROP_HAVE_ENUMERABLE
#define DUK_DEFPROP_HAVE_C                DUK_DEFPROP_HAVE_CONFIGURABLE
#define DUK_DEFPROP_HAVE_WE               (DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_HAVE_ENUMERABLE)
#define DUK_DEFPROP_HAVE_WC               (DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_HAVE_CONFIGURABLE)
#define DUK_DEFPROP_HAVE_EC               (DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_HAVE_CONFIGURABLE)
#define DUK_DEFPROP_HAVE_WEC              (DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_HAVE_CONFIGURABLE)
#define DUK_DEFPROP_SET_W                 DUK_DEFPROP_SET_WRITABLE
#define DUK_DEFPROP_SET_E                 DUK_DEFPROP_SET_ENUMERABLE
#define DUK_DEFPROP_SET_C                 DUK_DEFPROP_SET_CONFIGURABLE
#define DUK_DEFPROP_SET_WE                (DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_ENUMERABLE)
#define DUK_DEFPROP_SET_WC                (DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE)
#define DUK_DEFPROP_SET_EC                (DUK_DEFPROP_SET_ENUMERABLE | DUK_DEFPROP_SET_CONFIGURABLE)
#define DUK_DEFPROP_SET_WEC               (DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_ENUMERABLE | DUK_DEFPROP_SET_CONFIGURABLE)
#define DUK_DEFPROP_CLEAR_W               DUK_DEFPROP_CLEAR_WRITABLE
#define DUK_DEFPROP_CLEAR_E               DUK_DEFPROP_CLEAR_ENUMERABLE
#define DUK_DEFPROP_CLEAR_C               DUK_DEFPROP_CLEAR_CONFIGURABLE
#define DUK_DEFPROP_CLEAR_WE              (DUK_DEFPROP_CLEAR_WRITABLE | DUK_DEFPROP_CLEAR_ENUMERABLE)
#define DUK_DEFPROP_CLEAR_WC              (DUK_DEFPROP_CLEAR_WRITABLE | DUK_DEFPROP_CLEAR_CONFIGURABLE)
#define DUK_DEFPROP_CLEAR_EC              (DUK_DEFPROP_CLEAR_ENUMERABLE | DUK_DEFPROP_CLEAR_CONFIGURABLE)
#define DUK_DEFPROP_CLEAR_WEC             (DUK_DEFPROP_CLEAR_WRITABLE | DUK_DEFPROP_CLEAR_ENUMERABLE | DUK_DEFPROP_CLEAR_CONFIGURABLE)
#define DUK_DEFPROP_ATTR_W                (DUK_DEFPROP_HAVE_WEC | DUK_DEFPROP_W)
#define DUK_DEFPROP_ATTR_E                (DUK_DEFPROP_HAVE_WEC | DUK_DEFPROP_E)
#define DUK_DEFPROP_ATTR_C                (DUK_DEFPROP_HAVE_WEC | DUK_DEFPROP_C)
#define DUK_DEFPROP_ATTR_WE               (DUK_DEFPROP_HAVE_WEC | DUK_DEFPROP_WE)
#define DUK_DEFPROP_ATTR_WC               (DUK_DEFPROP_HAVE_WEC | DUK_DEFPROP_WC)
#define DUK_DEFPROP_ATTR_EC               (DUK_DEFPROP_HAVE_WEC | DUK_DEFPROP_EC)
#define DUK_DEFPROP_ATTR_WEC              (DUK_DEFPROP_HAVE_WEC | DUK_DEFPROP_WEC)

/* Flags for duk_push_thread_raw() */
#define DUK_THREAD_NEW_GLOBAL_ENV         (1U << 0)    /* create a new global environment */

/* Flags for duk_gc() */
#define DUK_GC_COMPACT                    (1U << 0)    /* compact heap objects */

/* Error codes (must be 8 bits at most, see duk_error.h) */
#define DUK_ERR_NONE                      0    /* no error (e.g. from duk_get_error_code()) */
#define DUK_ERR_ERROR                     1    /* Error */
#define DUK_ERR_EVAL_ERROR                2    /* EvalError */
#define DUK_ERR_RANGE_ERROR               3    /* RangeError */
#define DUK_ERR_REFERENCE_ERROR           4    /* ReferenceError */
#define DUK_ERR_SYNTAX_ERROR              5    /* SyntaxError */
#define DUK_ERR_TYPE_ERROR                6    /* TypeError */
#define DUK_ERR_URI_ERROR                 7    /* URIError */

/* Return codes for C functions (shortcut for throwing an error) */
#define DUK_RET_ERROR                     (-DUK_ERR_ERROR)
#define DUK_RET_EVAL_ERROR                (-DUK_ERR_EVAL_ERROR)
#define DUK_RET_RANGE_ERROR               (-DUK_ERR_RANGE_ERROR)
#define DUK_RET_REFERENCE_ERROR           (-DUK_ERR_REFERENCE_ERROR)
#define DUK_RET_SYNTAX_ERROR              (-DUK_ERR_SYNTAX_ERROR)
#define DUK_RET_TYPE_ERROR                (-DUK_ERR_TYPE_ERROR)
#define DUK_RET_URI_ERROR                 (-DUK_ERR_URI_ERROR)

/* Return codes for protected calls (duk_safe_call(), duk_pcall()) */
#define DUK_EXEC_SUCCESS                  0
#define DUK_EXEC_ERROR                    1

/* Debug levels for DUK_USE_DEBUG_WRITE(). */
#define DUK_LEVEL_DEBUG                   0
#define DUK_LEVEL_DDEBUG                  1
#define DUK_LEVEL_DDDEBUG                 2

/*
 *  Macros to create Symbols as C statically constructed strings.
 *
 *  Call e.g. as DUK_HIDDEN_SYMBOL("myProperty") <=> ("\xFF" "myProperty").
 *
 *  Local symbols have a unique suffix, caller should take care to avoid
 *  conflicting with the Duktape internal representation by e.g. prepending
 *  a '!' character: DUK_LOCAL_SYMBOL("myLocal", "!123").
 *
 *  Note that these can only be used for string constants, not dynamically
 *  created strings.
 *
 *  You shouldn't normally use DUK_INTERNAL_SYMBOL() at all.  It is reserved
 *  for Duktape internal symbols only.  There are no versioning guarantees
 *  for internal symbols.
 */

#define DUK_HIDDEN_SYMBOL(x)     ("\xFF" x)
#define DUK_GLOBAL_SYMBOL(x)     ("\x80" x)
#define DUK_LOCAL_SYMBOL(x,uniq) ("\x81" x "\xff" uniq)
#define DUK_WELLKNOWN_SYMBOL(x)  ("\x81" x "\xff")
#define DUK_INTERNAL_SYMBOL(x)   ("\x82" x)

/*
 *  If no variadic macros, __FILE__ and __LINE__ are passed through globals
 *  which is ugly and not thread safe.
 */

#if !defined(DUK_API_VARIADIC_MACROS)
DUK_EXTERNAL_DECL const char *duk_api_global_filename;
DUK_EXTERNAL_DECL duk_int_t duk_api_global_line;
#endif

/*
 *  Context management
 */

DUK_EXTERNAL_DECL
duk_context *duk_create_heap(duk_alloc_function alloc_func,
                             duk_realloc_function realloc_func,
                             duk_free_function free_func,
                             void *heap_udata,
                             duk_fatal_function fatal_handler);
DUK_EXTERNAL_DECL void duk_destroy_heap(duk_context *ctx);

DUK_EXTERNAL_DECL void duk_suspend(duk_context *ctx, duk_thread_state *state);
DUK_EXTERNAL_DECL void duk_resume(duk_context *ctx, const duk_thread_state *state);

#define duk_create_heap_default() \
    duk_create_heap(NULL, NULL, NULL, NULL, NULL)

/*
 *  Memory management
 *
 *  Raw functions have no side effects (cannot trigger GC).
 */

DUK_EXTERNAL_DECL void *duk_alloc_raw(duk_context *ctx, duk_size_t size);
DUK_EXTERNAL_DECL void duk_free_raw(duk_context *ctx, void *ptr);
DUK_EXTERNAL_DECL void *duk_realloc_raw(duk_context *ctx, void *ptr, duk_size_t size);
DUK_EXTERNAL_DECL void *duk_alloc(duk_context *ctx, duk_size_t size);
DUK_EXTERNAL_DECL void duk_free(duk_context *ctx, void *ptr);
DUK_EXTERNAL_DECL void *duk_realloc(duk_context *ctx, void *ptr, duk_size_t size);
DUK_EXTERNAL_DECL void duk_get_memory_functions(duk_context *ctx, duk_memory_functions *out_funcs);
DUK_EXTERNAL_DECL void duk_gc(duk_context *ctx, duk_uint_t flags);

/*
 *  Error handling
 */

DUK_API_NORETURN(DUK_EXTERNAL_DECL void duk_throw_raw(duk_context *ctx));
#define duk_throw(ctx) \
    (duk_throw_raw((ctx)), (duk_ret_t) 0)
DUK_API_NORETURN(DUK_EXTERNAL_DECL void duk_fatal_raw(duk_context *ctx, const char *err_msg));
#define duk_fatal(ctx,err_msg) \
    (duk_fatal_raw((ctx), (err_msg)), (duk_ret_t) 0)
DUK_API_NORETURN(DUK_EXTERNAL_DECL void duk_error_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, ...));

#if defined(DUK_API_VARIADIC_MACROS)
#define duk_error(ctx,err_code,...)  \
    (duk_error_raw((ctx), (duk_errcode_t) (err_code), (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), __VA_ARGS__), (duk_ret_t) 0)
#define duk_generic_error(ctx,...)  \
    (duk_error_raw((ctx), (duk_errcode_t) DUK_ERR_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), __VA_ARGS__), (duk_ret_t) 0)
#define duk_eval_error(ctx,...)  \
    (duk_error_raw((ctx), (duk_errcode_t) DUK_ERR_EVAL_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), __VA_ARGS__), (duk_ret_t) 0)
#define duk_range_error(ctx,...)  \
    (duk_error_raw((ctx), (duk_errcode_t) DUK_ERR_RANGE_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), __VA_ARGS__), (duk_ret_t) 0)
#define duk_reference_error(ctx,...)  \
    (duk_error_raw((ctx), (duk_errcode_t) DUK_ERR_REFERENCE_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), __VA_ARGS__), (duk_ret_t) 0)
#define duk_syntax_error(ctx,...)  \
    (duk_error_raw((ctx), (duk_errcode_t) DUK_ERR_SYNTAX_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), __VA_ARGS__), (duk_ret_t) 0)
#define duk_type_error(ctx,...)  \
    (duk_error_raw((ctx), (duk_errcode_t) DUK_ERR_TYPE_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), __VA_ARGS__), (duk_ret_t) 0)
#define duk_uri_error(ctx,...)  \
    (duk_error_raw((ctx), (duk_errcode_t) DUK_ERR_URI_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), __VA_ARGS__), (duk_ret_t) 0)
#else  /* DUK_API_VARIADIC_MACROS */
/* For legacy compilers without variadic macros a macro hack is used to allow
 * variable arguments.  While the macro allows "return duk_error(...)", it
 * will fail with e.g. "(void) duk_error(...)".  The calls are noreturn but
 * with a return value to allow the "return duk_error(...)" idiom.  This may
 * cause some compiler warnings, but without noreturn the generated code is
 * often worse.  The same approach as with variadic macros (using
 * "(duk_error(...), 0)") won't work due to the macro hack structure.
 */
DUK_API_NORETURN(DUK_EXTERNAL_DECL duk_ret_t duk_error_stash(duk_context *ctx, duk_errcode_t err_code, const char *fmt, ...));
DUK_API_NORETURN(DUK_EXTERNAL_DECL duk_ret_t duk_generic_error_stash(duk_context *ctx, const char *fmt, ...));
DUK_API_NORETURN(DUK_EXTERNAL_DECL duk_ret_t duk_eval_error_stash(duk_context *ctx, const char *fmt, ...));
DUK_API_NORETURN(DUK_EXTERNAL_DECL duk_ret_t duk_range_error_stash(duk_context *ctx, const char *fmt, ...));
DUK_API_NORETURN(DUK_EXTERNAL_DECL duk_ret_t duk_reference_error_stash(duk_context *ctx, const char *fmt, ...));
DUK_API_NORETURN(DUK_EXTERNAL_DECL duk_ret_t duk_syntax_error_stash(duk_context *ctx, const char *fmt, ...));
DUK_API_NORETURN(DUK_EXTERNAL_DECL duk_ret_t duk_type_error_stash(duk_context *ctx, const char *fmt, ...));
DUK_API_NORETURN(DUK_EXTERNAL_DECL duk_ret_t duk_uri_error_stash(duk_context *ctx, const char *fmt, ...));
#define duk_error  \
    (duk_api_global_filename = (const char *) (DUK_FILE_MACRO), \
     duk_api_global_line = (duk_int_t) (DUK_LINE_MACRO), \
     duk_error_stash)  /* last value is func pointer, arguments follow in parens */
#define duk_generic_error  \
    (duk_api_global_filename = (const char *) (DUK_FILE_MACRO), \
     duk_api_global_line = (duk_int_t) (DUK_LINE_MACRO), \
     duk_generic_error_stash)
#define duk_eval_error  \
    (duk_api_global_filename = (const char *) (DUK_FILE_MACRO), \
     duk_api_global_line = (duk_int_t) (DUK_LINE_MACRO), \
     duk_eval_error_stash)
#define duk_range_error  \
    (duk_api_global_filename = (const char *) (DUK_FILE_MACRO), \
     duk_api_global_line = (duk_int_t) (DUK_LINE_MACRO), \
     duk_range_error_stash)
#define duk_reference_error  \
    (duk_api_global_filename = (const char *) (DUK_FILE_MACRO), \
     duk_api_global_line = (duk_int_t) (DUK_LINE_MACRO), \
     duk_reference_error_stash)
#define duk_syntax_error  \
    (duk_api_global_filename = (const char *) (DUK_FILE_MACRO), \
     duk_api_global_line = (duk_int_t) (DUK_LINE_MACRO), \
     duk_syntax_error_stash)
#define duk_type_error  \
    (duk_api_global_filename = (const char *) (DUK_FILE_MACRO), \
     duk_api_global_line = (duk_int_t) (DUK_LINE_MACRO), \
     duk_type_error_stash)
#define duk_uri_error  \
    (duk_api_global_filename = (const char *) (DUK_FILE_MACRO), \
     duk_api_global_line = (duk_int_t) (DUK_LINE_MACRO), \
     duk_uri_error_stash)
#endif  /* DUK_API_VARIADIC_MACROS */

DUK_API_NORETURN(DUK_EXTERNAL_DECL void duk_error_va_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, va_list ap));

#define duk_error_va(ctx,err_code,fmt,ap)  \
    (duk_error_va_raw((ctx), (duk_errcode_t) (err_code), (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), (fmt), (ap)), (duk_ret_t) 0)
#define duk_generic_error_va(ctx,fmt,ap)  \
    (duk_error_va_raw((ctx), (duk_errcode_t) DUK_ERR_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), (fmt), (ap)), (duk_ret_t) 0)
#define duk_eval_error_va(ctx,fmt,ap)  \
    (duk_error_va_raw((ctx), (duk_errcode_t) DUK_ERR_EVAL_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), (fmt), (ap)), (duk_ret_t) 0)
#define duk_range_error_va(ctx,fmt,ap)  \
    (duk_error_va_raw((ctx), (duk_errcode_t) DUK_ERR_RANGE_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), (fmt), (ap)), (duk_ret_t) 0)
#define duk_reference_error_va(ctx,fmt,ap)  \
    (duk_error_va_raw((ctx), (duk_errcode_t) DUK_ERR_REFERENCE_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), (fmt), (ap)), (duk_ret_t) 0)
#define duk_syntax_error_va(ctx,fmt,ap)  \
    (duk_error_va_raw((ctx), (duk_errcode_t) DUK_ERR_SYNTAX_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), (fmt), (ap)), (duk_ret_t) 0)
#define duk_type_error_va(ctx,fmt,ap)  \
    (duk_error_va_raw((ctx), (duk_errcode_t) DUK_ERR_TYPE_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), (fmt), (ap)), (duk_ret_t) 0)
#define duk_uri_error_va(ctx,fmt,ap)  \
    (duk_error_va_raw((ctx), (duk_errcode_t) DUK_ERR_URI_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), (fmt), (ap)), (duk_ret_t) 0)

/*
 *  Other state related functions
 */

DUK_EXTERNAL_DECL duk_bool_t duk_is_strict_call(duk_context *ctx);
DUK_EXTERNAL_DECL duk_bool_t duk_is_constructor_call(duk_context *ctx);

/*
 *  Stack management
 */

DUK_EXTERNAL_DECL duk_idx_t duk_normalize_index(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_idx_t duk_require_normalize_index(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_bool_t duk_is_valid_index(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL void duk_require_valid_index(duk_context *ctx, duk_idx_t idx);

DUK_EXTERNAL_DECL duk_idx_t duk_get_top(duk_context *ctx);
DUK_EXTERNAL_DECL void duk_set_top(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_idx_t duk_get_top_index(duk_context *ctx);
DUK_EXTERNAL_DECL duk_idx_t duk_require_top_index(duk_context *ctx);

/* Although extra/top could be an unsigned type here, using a signed type
 * makes the API more robust to calling code calculation errors or corner
 * cases (where caller might occasionally come up with negative values).
 * Negative values are treated as zero, which is better than casting them
 * to a large unsigned number.  (This principle is used elsewhere in the
 * API too.)
 */
DUK_EXTERNAL_DECL duk_bool_t duk_check_stack(duk_context *ctx, duk_idx_t extra);
DUK_EXTERNAL_DECL void duk_require_stack(duk_context *ctx, duk_idx_t extra);
DUK_EXTERNAL_DECL duk_bool_t duk_check_stack_top(duk_context *ctx, duk_idx_t top);
DUK_EXTERNAL_DECL void duk_require_stack_top(duk_context *ctx, duk_idx_t top);

/*
 *  Stack manipulation (other than push/pop)
 */

DUK_EXTERNAL_DECL void duk_swap(duk_context *ctx, duk_idx_t idx1, duk_idx_t idx2);
DUK_EXTERNAL_DECL void duk_swap_top(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL void duk_dup(duk_context *ctx, duk_idx_t from_idx);
DUK_EXTERNAL_DECL void duk_dup_top(duk_context *ctx);
DUK_EXTERNAL_DECL void duk_insert(duk_context *ctx, duk_idx_t to_idx);
DUK_EXTERNAL_DECL void duk_pull(duk_context *ctx, duk_idx_t from_idx);
DUK_EXTERNAL_DECL void duk_replace(duk_context *ctx, duk_idx_t to_idx);
DUK_EXTERNAL_DECL void duk_copy(duk_context *ctx, duk_idx_t from_idx, duk_idx_t to_idx);
DUK_EXTERNAL_DECL void duk_remove(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL void duk_xcopymove_raw(duk_context *to_ctx, duk_context *from_ctx, duk_idx_t count, duk_bool_t is_copy);

#define duk_xmove_top(to_ctx,from_ctx,count) \
    duk_xcopymove_raw((to_ctx), (from_ctx), (count), 0 /*is_copy*/)
#define duk_xcopy_top(to_ctx,from_ctx,count) \
    duk_xcopymove_raw((to_ctx), (from_ctx), (count), 1 /*is_copy*/)

/*
 *  Push operations
 *
 *  Push functions return the absolute (relative to bottom of frame)
 *  position of the pushed value for convenience.
 *
 *  Note: duk_dup() is technically a push.
 */

DUK_EXTERNAL_DECL void duk_push_undefined(duk_context *ctx);
DUK_EXTERNAL_DECL void duk_push_null(duk_context *ctx);
DUK_EXTERNAL_DECL void duk_push_boolean(duk_context *ctx, duk_bool_t val);
DUK_EXTERNAL_DECL void duk_push_true(duk_context *ctx);
DUK_EXTERNAL_DECL void duk_push_false(duk_context *ctx);
DUK_EXTERNAL_DECL void duk_push_number(duk_context *ctx, duk_double_t val);
DUK_EXTERNAL_DECL void duk_push_nan(duk_context *ctx);
DUK_EXTERNAL_DECL void duk_push_int(duk_context *ctx, duk_int_t val);
DUK_EXTERNAL_DECL void duk_push_uint(duk_context *ctx, duk_uint_t val);
DUK_EXTERNAL_DECL const char *duk_push_string(duk_context *ctx, const char *str);
DUK_EXTERNAL_DECL const char *duk_push_lstring(duk_context *ctx, const char *str, duk_size_t len);
DUK_EXTERNAL_DECL void duk_push_pointer(duk_context *ctx, void *p);
DUK_EXTERNAL_DECL const char *duk_push_sprintf(duk_context *ctx, const char *fmt, ...);
DUK_EXTERNAL_DECL const char *duk_push_vsprintf(duk_context *ctx, const char *fmt, va_list ap);

/* duk_push_literal() may evaluate its argument (a C string literal) more than
 * once on purpose.  When speed is preferred, sizeof() avoids an unnecessary
 * strlen() at runtime.  Sizeof("foo") == 4, so subtract 1.  The argument
 * must be non-NULL and should not contain internal NUL characters as the
 * behavior will then depend on config options.
 */
#if defined(DUK_USE_PREFER_SIZE)
#define duk_push_literal(ctx,cstring)  duk_push_string((ctx), (cstring))
#else
DUK_EXTERNAL_DECL const char *duk_push_literal_raw(duk_context *ctx, const char *str, duk_size_t len);
#define duk_push_literal(ctx,cstring)  duk_push_literal_raw((ctx), (cstring), sizeof((cstring)) - 1U)
#endif

DUK_EXTERNAL_DECL void duk_push_this(duk_context *ctx);
DUK_EXTERNAL_DECL void duk_push_new_target(duk_context *ctx);
DUK_EXTERNAL_DECL void duk_push_current_function(duk_context *ctx);
DUK_EXTERNAL_DECL void duk_push_current_thread(duk_context *ctx);
DUK_EXTERNAL_DECL void duk_push_global_object(duk_context *ctx);
DUK_EXTERNAL_DECL void duk_push_heap_stash(duk_context *ctx);
DUK_EXTERNAL_DECL void duk_push_global_stash(duk_context *ctx);
DUK_EXTERNAL_DECL void duk_push_thread_stash(duk_context *ctx, duk_context *target_ctx);

DUK_EXTERNAL_DECL duk_idx_t duk_push_object(duk_context *ctx);
DUK_EXTERNAL_DECL duk_idx_t duk_push_bare_object(duk_context *ctx);
DUK_EXTERNAL_DECL duk_idx_t duk_push_array(duk_context *ctx);
DUK_EXTERNAL_DECL duk_idx_t duk_push_bare_array(duk_context *ctx);
DUK_EXTERNAL_DECL duk_idx_t duk_push_c_function(duk_context *ctx, duk_c_function func, duk_idx_t nargs);
DUK_EXTERNAL_DECL duk_idx_t duk_push_c_lightfunc(duk_context *ctx, duk_c_function func, duk_idx_t nargs, duk_idx_t length, duk_int_t magic);
DUK_EXTERNAL_DECL duk_idx_t duk_push_thread_raw(duk_context *ctx, duk_uint_t flags);
DUK_EXTERNAL_DECL duk_idx_t duk_push_proxy(duk_context *ctx, duk_uint_t proxy_flags);

#define duk_push_thread(ctx) \
    duk_push_thread_raw((ctx), 0 /*flags*/)

#define duk_push_thread_new_globalenv(ctx) \
    duk_push_thread_raw((ctx), DUK_THREAD_NEW_GLOBAL_ENV /*flags*/)

DUK_EXTERNAL_DECL duk_idx_t duk_push_error_object_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, ...);

#if defined(DUK_API_VARIADIC_MACROS)
#define duk_push_error_object(ctx,err_code,...)  \
    duk_push_error_object_raw((ctx), (err_code), (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), __VA_ARGS__)
#else
DUK_EXTERNAL_DECL duk_idx_t duk_push_error_object_stash(duk_context *ctx, duk_errcode_t err_code, const char *fmt, ...);
/* Note: parentheses are required so that the comma expression works in assignments. */
#define duk_push_error_object  \
    (duk_api_global_filename = (const char *) (DUK_FILE_MACRO), \
     duk_api_global_line = (duk_int_t) (DUK_LINE_MACRO), \
     duk_push_error_object_stash)  /* last value is func pointer, arguments follow in parens */
#endif

DUK_EXTERNAL_DECL duk_idx_t duk_push_error_object_va_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, va_list ap);
#define duk_push_error_object_va(ctx,err_code,fmt,ap)  \
    duk_push_error_object_va_raw((ctx), (err_code), (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), (fmt), (ap))

#define DUK_BUF_FLAG_DYNAMIC   (1 << 0)    /* internal flag: dynamic buffer */
#define DUK_BUF_FLAG_EXTERNAL  (1 << 1)    /* internal flag: external buffer */
#define DUK_BUF_FLAG_NOZERO    (1 << 2)    /* internal flag: don't zero allocated buffer */

DUK_EXTERNAL_DECL void *duk_push_buffer_raw(duk_context *ctx, duk_size_t size, duk_small_uint_t flags);

#define duk_push_buffer(ctx,size,dynamic) \
    duk_push_buffer_raw((ctx), (size), (dynamic) ? DUK_BUF_FLAG_DYNAMIC : 0)
#define duk_push_fixed_buffer(ctx,size) \
    duk_push_buffer_raw((ctx), (size), 0 /*flags*/)
#define duk_push_dynamic_buffer(ctx,size) \
    duk_push_buffer_raw((ctx), (size), DUK_BUF_FLAG_DYNAMIC /*flags*/)
#define duk_push_external_buffer(ctx) \
    ((void) duk_push_buffer_raw((ctx), 0, DUK_BUF_FLAG_DYNAMIC | DUK_BUF_FLAG_EXTERNAL))

#define DUK_BUFOBJ_ARRAYBUFFER         0
#define DUK_BUFOBJ_NODEJS_BUFFER       1
#define DUK_BUFOBJ_DATAVIEW            2
#define DUK_BUFOBJ_INT8ARRAY           3
#define DUK_BUFOBJ_UINT8ARRAY          4
#define DUK_BUFOBJ_UINT8CLAMPEDARRAY   5
#define DUK_BUFOBJ_INT16ARRAY          6
#define DUK_BUFOBJ_UINT16ARRAY         7
#define DUK_BUFOBJ_INT32ARRAY          8
#define DUK_BUFOBJ_UINT32ARRAY         9
#define DUK_BUFOBJ_FLOAT32ARRAY        10
#define DUK_BUFOBJ_FLOAT64ARRAY        11

DUK_EXTERNAL_DECL void duk_push_buffer_object(duk_context *ctx, duk_idx_t idx_buffer, duk_size_t byte_offset, duk_size_t byte_length, duk_uint_t flags);

DUK_EXTERNAL_DECL duk_idx_t duk_push_heapptr(duk_context *ctx, void *ptr);

/*
 *  Pop operations
 */

DUK_EXTERNAL_DECL void duk_pop(duk_context *ctx);
DUK_EXTERNAL_DECL void duk_pop_n(duk_context *ctx, duk_idx_t count);
DUK_EXTERNAL_DECL void duk_pop_2(duk_context *ctx);
DUK_EXTERNAL_DECL void duk_pop_3(duk_context *ctx);

/*
 *  Type checks
 *
 *  duk_is_none(), which would indicate whether index it outside of stack,
 *  is not needed; duk_is_valid_index() gives the same information.
 */

DUK_EXTERNAL_DECL duk_int_t duk_get_type(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_bool_t duk_check_type(duk_context *ctx, duk_idx_t idx, duk_int_t type);
DUK_EXTERNAL_DECL duk_uint_t duk_get_type_mask(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_bool_t duk_check_type_mask(duk_context *ctx, duk_idx_t idx, duk_uint_t mask);

DUK_EXTERNAL_DECL duk_bool_t duk_is_undefined(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_bool_t duk_is_null(duk_context *ctx, duk_idx_t idx);
#define duk_is_null_or_undefined(ctx, idx) \
    ((duk_get_type_mask((ctx), (idx)) & (DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_UNDEFINED)) ? 1 : 0)

DUK_EXTERNAL_DECL duk_bool_t duk_is_boolean(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_bool_t duk_is_number(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_bool_t duk_is_nan(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_bool_t duk_is_string(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_bool_t duk_is_object(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_bool_t duk_is_buffer(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_bool_t duk_is_buffer_data(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_bool_t duk_is_pointer(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_bool_t duk_is_lightfunc(duk_context *ctx, duk_idx_t idx);

DUK_EXTERNAL_DECL duk_bool_t duk_is_symbol(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_bool_t duk_is_array(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_bool_t duk_is_function(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_bool_t duk_is_c_function(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_bool_t duk_is_ecmascript_function(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_bool_t duk_is_bound_function(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_bool_t duk_is_thread(duk_context *ctx, duk_idx_t idx);

#define duk_is_callable(ctx,idx) \
    duk_is_function((ctx), (idx))
DUK_EXTERNAL_DECL duk_bool_t duk_is_constructable(duk_context *ctx, duk_idx_t idx);

DUK_EXTERNAL_DECL duk_bool_t duk_is_dynamic_buffer(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_bool_t duk_is_fixed_buffer(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_bool_t duk_is_external_buffer(duk_context *ctx, duk_idx_t idx);

/* Buffers and lightfuncs are not considered primitive because they mimic
 * objects and e.g. duk_to_primitive() will coerce them instead of returning
 * them as is.  Symbols are represented as strings internally.
 */
#define duk_is_primitive(ctx,idx) \
    duk_check_type_mask((ctx), (idx), DUK_TYPE_MASK_UNDEFINED | \
                                      DUK_TYPE_MASK_NULL | \
                                      DUK_TYPE_MASK_BOOLEAN | \
                                      DUK_TYPE_MASK_NUMBER | \
                                      DUK_TYPE_MASK_STRING | \
                                      DUK_TYPE_MASK_POINTER)

/* Symbols are object coercible, covered by DUK_TYPE_MASK_STRING. */
#define duk_is_object_coercible(ctx,idx) \
    duk_check_type_mask((ctx), (idx), DUK_TYPE_MASK_BOOLEAN | \
                                      DUK_TYPE_MASK_NUMBER | \
                                      DUK_TYPE_MASK_STRING | \
                                      DUK_TYPE_MASK_OBJECT | \
                                      DUK_TYPE_MASK_BUFFER | \
                                      DUK_TYPE_MASK_POINTER | \
                                      DUK_TYPE_MASK_LIGHTFUNC)

DUK_EXTERNAL_DECL duk_errcode_t duk_get_error_code(duk_context *ctx, duk_idx_t idx);
#define duk_is_error(ctx,idx) \
    (duk_get_error_code((ctx), (idx)) != 0)
#define duk_is_eval_error(ctx,idx) \
    (duk_get_error_code((ctx), (idx)) == DUK_ERR_EVAL_ERROR)
#define duk_is_range_error(ctx,idx) \
    (duk_get_error_code((ctx), (idx)) == DUK_ERR_RANGE_ERROR)
#define duk_is_reference_error(ctx,idx) \
    (duk_get_error_code((ctx), (idx)) == DUK_ERR_REFERENCE_ERROR)
#define duk_is_syntax_error(ctx,idx) \
    (duk_get_error_code((ctx), (idx)) == DUK_ERR_SYNTAX_ERROR)
#define duk_is_type_error(ctx,idx) \
    (duk_get_error_code((ctx), (idx)) == DUK_ERR_TYPE_ERROR)
#define duk_is_uri_error(ctx,idx) \
    (duk_get_error_code((ctx), (idx)) == DUK_ERR_URI_ERROR)

/*
 *  Get operations: no coercion, returns default value for invalid
 *  indices and invalid value types.
 *
 *  duk_get_undefined() and duk_get_null() would be pointless and
 *  are not included.
 */

DUK_EXTERNAL_DECL duk_bool_t duk_get_boolean(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_double_t duk_get_number(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_int_t duk_get_int(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_uint_t duk_get_uint(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL const char *duk_get_string(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL const char *duk_get_lstring(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len);
DUK_EXTERNAL_DECL void *duk_get_buffer(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size);
DUK_EXTERNAL_DECL void *duk_get_buffer_data(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size);
DUK_EXTERNAL_DECL void *duk_get_pointer(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_c_function duk_get_c_function(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_context *duk_get_context(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL void *duk_get_heapptr(duk_context *ctx, duk_idx_t idx);

/*
 *  Get-with-explicit default operations: like get operations but with an
 *  explicit default value.
 */

DUK_EXTERNAL_DECL duk_bool_t duk_get_boolean_default(duk_context *ctx, duk_idx_t idx, duk_bool_t def_value);
DUK_EXTERNAL_DECL duk_double_t duk_get_number_default(duk_context *ctx, duk_idx_t idx, duk_double_t def_value);
DUK_EXTERNAL_DECL duk_int_t duk_get_int_default(duk_context *ctx, duk_idx_t idx, duk_int_t def_value);
DUK_EXTERNAL_DECL duk_uint_t duk_get_uint_default(duk_context *ctx, duk_idx_t idx, duk_uint_t def_value);
DUK_EXTERNAL_DECL const char *duk_get_string_default(duk_context *ctx, duk_idx_t idx, const char *def_value);
DUK_EXTERNAL_DECL const char *duk_get_lstring_default(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len, const char *def_ptr, duk_size_t def_len);
DUK_EXTERNAL_DECL void *duk_get_buffer_default(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_len);
DUK_EXTERNAL_DECL void *duk_get_buffer_data_default(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_len);
DUK_EXTERNAL_DECL void *duk_get_pointer_default(duk_context *ctx, duk_idx_t idx, void *def_value);
DUK_EXTERNAL_DECL duk_c_function duk_get_c_function_default(duk_context *ctx, duk_idx_t idx, duk_c_function def_value);
DUK_EXTERNAL_DECL duk_context *duk_get_context_default(duk_context *ctx, duk_idx_t idx, duk_context *def_value);
DUK_EXTERNAL_DECL void *duk_get_heapptr_default(duk_context *ctx, duk_idx_t idx, void *def_value);

/*
 *  Opt operations: like require operations but with an explicit default value
 *  when value is undefined or index is invalid, null and non-matching types
 *  cause a TypeError.
 */

DUK_EXTERNAL_DECL duk_bool_t duk_opt_boolean(duk_context *ctx, duk_idx_t idx, duk_bool_t def_value);
DUK_EXTERNAL_DECL duk_double_t duk_opt_number(duk_context *ctx, duk_idx_t idx, duk_double_t def_value);
DUK_EXTERNAL_DECL duk_int_t duk_opt_int(duk_context *ctx, duk_idx_t idx, duk_int_t def_value);
DUK_EXTERNAL_DECL duk_uint_t duk_opt_uint(duk_context *ctx, duk_idx_t idx, duk_uint_t def_value);
DUK_EXTERNAL_DECL const char *duk_opt_string(duk_context *ctx, duk_idx_t idx, const char *def_ptr);
DUK_EXTERNAL_DECL const char *duk_opt_lstring(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len, const char *def_ptr, duk_size_t def_len);
DUK_EXTERNAL_DECL void *duk_opt_buffer(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size);
DUK_EXTERNAL_DECL void *duk_opt_buffer_data(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size);
DUK_EXTERNAL_DECL void *duk_opt_pointer(duk_context *ctx, duk_idx_t idx, void *def_value);
DUK_EXTERNAL_DECL duk_c_function duk_opt_c_function(duk_context *ctx, duk_idx_t idx, duk_c_function def_value);
DUK_EXTERNAL_DECL duk_context *duk_opt_context(duk_context *ctx, duk_idx_t idx, duk_context *def_value);
DUK_EXTERNAL_DECL void *duk_opt_heapptr(duk_context *ctx, duk_idx_t idx, void *def_value);

/*
 *  Require operations: no coercion, throw error if index or type
 *  is incorrect.  No defaulting.
 */

#define duk_require_type_mask(ctx,idx,mask) \
    ((void) duk_check_type_mask((ctx), (idx), (mask) | DUK_TYPE_MASK_THROW))

DUK_EXTERNAL_DECL void duk_require_undefined(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL void duk_require_null(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_bool_t duk_require_boolean(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_double_t duk_require_number(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_int_t duk_require_int(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_uint_t duk_require_uint(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL const char *duk_require_string(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL const char *duk_require_lstring(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len);
DUK_EXTERNAL_DECL void duk_require_object(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL void *duk_require_buffer(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size);
DUK_EXTERNAL_DECL void *duk_require_buffer_data(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size);
DUK_EXTERNAL_DECL void *duk_require_pointer(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_c_function duk_require_c_function(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_context *duk_require_context(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL void duk_require_function(duk_context *ctx, duk_idx_t idx);
#define duk_require_callable(ctx,idx) \
    duk_require_function((ctx), (idx))
DUK_EXTERNAL_DECL void duk_require_constructor_call(duk_context *ctx);
DUK_EXTERNAL_DECL void duk_require_constructable(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL void *duk_require_heapptr(duk_context *ctx, duk_idx_t idx);

/* Symbols are object coercible and covered by DUK_TYPE_MASK_STRING. */
#define duk_require_object_coercible(ctx,idx) \
    ((void) duk_check_type_mask((ctx), (idx), DUK_TYPE_MASK_BOOLEAN | \
                                                DUK_TYPE_MASK_NUMBER | \
                                                DUK_TYPE_MASK_STRING | \
                                                DUK_TYPE_MASK_OBJECT | \
                                                DUK_TYPE_MASK_BUFFER | \
                                                DUK_TYPE_MASK_POINTER | \
                                                DUK_TYPE_MASK_LIGHTFUNC | \
                                                DUK_TYPE_MASK_THROW))

/*
 *  Coercion operations: in-place coercion, return coerced value where
 *  applicable.  If index is invalid, throw error.  Some coercions may
 *  throw an expected error (e.g. from a toString() or valueOf() call)
 *  or an internal error (e.g. from out of memory).
 */

DUK_EXTERNAL_DECL void duk_to_undefined(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL void duk_to_null(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_bool_t duk_to_boolean(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_double_t duk_to_number(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_int_t duk_to_int(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_uint_t duk_to_uint(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_int32_t duk_to_int32(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_uint32_t duk_to_uint32(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_uint16_t duk_to_uint16(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL const char *duk_to_string(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL const char *duk_to_lstring(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len);
DUK_EXTERNAL_DECL void *duk_to_buffer_raw(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, duk_uint_t flags);
DUK_EXTERNAL_DECL void *duk_to_pointer(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL void duk_to_object(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL void duk_to_primitive(duk_context *ctx, duk_idx_t idx, duk_int_t hint);

#define DUK_BUF_MODE_FIXED      0   /* internal: request fixed buffer result */
#define DUK_BUF_MODE_DYNAMIC    1   /* internal: request dynamic buffer result */
#define DUK_BUF_MODE_DONTCARE   2   /* internal: don't care about fixed/dynamic nature */

#define duk_to_buffer(ctx,idx,out_size) \
    duk_to_buffer_raw((ctx), (idx), (out_size), DUK_BUF_MODE_DONTCARE)
#define duk_to_fixed_buffer(ctx,idx,out_size) \
    duk_to_buffer_raw((ctx), (idx), (out_size), DUK_BUF_MODE_FIXED)
#define duk_to_dynamic_buffer(ctx,idx,out_size) \
    duk_to_buffer_raw((ctx), (idx), (out_size), DUK_BUF_MODE_DYNAMIC)

/* safe variants of a few coercion operations */
DUK_EXTERNAL_DECL const char *duk_safe_to_lstring(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len);
DUK_EXTERNAL_DECL const char *duk_to_stacktrace(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL const char *duk_safe_to_stacktrace(duk_context *ctx, duk_idx_t idx);
#define duk_safe_to_string(ctx,idx) \
    duk_safe_to_lstring((ctx), (idx), NULL)

/*
 *  Value length
 */

DUK_EXTERNAL_DECL duk_size_t duk_get_length(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL void duk_set_length(duk_context *ctx, duk_idx_t idx, duk_size_t len);
#if 0
/* duk_require_length()? */
/* duk_opt_length()? */
#endif

/*
 *  Misc conversion
 */

DUK_EXTERNAL_DECL const char *duk_base64_encode(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL void duk_base64_decode(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL const char *duk_hex_encode(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL void duk_hex_decode(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL const char *duk_json_encode(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL void duk_json_decode(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL void duk_cbor_encode(duk_context *ctx, duk_idx_t idx, duk_uint_t encode_flags);
DUK_EXTERNAL_DECL void duk_cbor_decode(duk_context *ctx, duk_idx_t idx, duk_uint_t decode_flags);

DUK_EXTERNAL_DECL const char *duk_buffer_to_string(duk_context *ctx, duk_idx_t idx);

/*
 *  Buffer
 */

DUK_EXTERNAL_DECL void *duk_resize_buffer(duk_context *ctx, duk_idx_t idx, duk_size_t new_size);
DUK_EXTERNAL_DECL void *duk_steal_buffer(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size);
DUK_EXTERNAL_DECL void duk_config_buffer(duk_context *ctx, duk_idx_t idx, void *ptr, duk_size_t len);

/*
 *  Property access
 *
 *  The basic function assumes key is on stack.  The _(l)string variant takes
 *  a C string as a property name; the _literal variant takes a C literal.
 *  The _index variant takes an array index as a property name (e.g. 123 is
 *  equivalent to the key "123").  The _heapptr variant takes a raw, borrowed
 *  heap pointer.
 */

DUK_EXTERNAL_DECL duk_bool_t duk_get_prop(duk_context *ctx, duk_idx_t obj_idx);
DUK_EXTERNAL_DECL duk_bool_t duk_get_prop_string(duk_context *ctx, duk_idx_t obj_idx, const char *key);
DUK_EXTERNAL_DECL duk_bool_t duk_get_prop_lstring(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len);
#if defined(DUK_USE_PREFER_SIZE)
#define duk_get_prop_literal(ctx,obj_idx,key)  duk_get_prop_string((ctx), (obj_idx), (key))
#else
DUK_EXTERNAL_DECL duk_bool_t duk_get_prop_literal_raw(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len);
#define duk_get_prop_literal(ctx,obj_idx,key)  duk_get_prop_literal_raw((ctx), (obj_idx), (key), sizeof((key)) - 1U)
#endif
DUK_EXTERNAL_DECL duk_bool_t duk_get_prop_index(duk_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx);
DUK_EXTERNAL_DECL duk_bool_t duk_get_prop_heapptr(duk_context *ctx, duk_idx_t obj_idx, void *ptr);
DUK_EXTERNAL_DECL duk_bool_t duk_put_prop(duk_context *ctx, duk_idx_t obj_idx);
DUK_EXTERNAL_DECL duk_bool_t duk_put_prop_string(duk_context *ctx, duk_idx_t obj_idx, const char *key);
DUK_EXTERNAL_DECL duk_bool_t duk_put_prop_lstring(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len);
#if defined(DUK_USE_PREFER_SIZE)
#define duk_put_prop_literal(ctx,obj_idx,key)  duk_put_prop_string((ctx), (obj_idx), (key))
#else
DUK_EXTERNAL_DECL duk_bool_t duk_put_prop_literal_raw(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len);
#define duk_put_prop_literal(ctx,obj_idx,key)  duk_put_prop_literal_raw((ctx), (obj_idx), (key), sizeof((key)) - 1U)
#endif
DUK_EXTERNAL_DECL duk_bool_t duk_put_prop_index(duk_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx);
DUK_EXTERNAL_DECL duk_bool_t duk_put_prop_heapptr(duk_context *ctx, duk_idx_t obj_idx, void *ptr);
DUK_EXTERNAL_DECL duk_bool_t duk_del_prop(duk_context *ctx, duk_idx_t obj_idx);
DUK_EXTERNAL_DECL duk_bool_t duk_del_prop_string(duk_context *ctx, duk_idx_t obj_idx, const char *key);
DUK_EXTERNAL_DECL duk_bool_t duk_del_prop_lstring(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len);
#if defined(DUK_USE_PREFER_SIZE)
#define duk_del_prop_literal(ctx,obj_idx,key)  duk_del_prop_string((ctx), (obj_idx), (key))
#else
DUK_EXTERNAL_DECL duk_bool_t duk_del_prop_literal_raw(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len);
#define duk_del_prop_literal(ctx,obj_idx,key)  duk_del_prop_literal_raw((ctx), (obj_idx), (key), sizeof((key)) - 1U)
#endif
DUK_EXTERNAL_DECL duk_bool_t duk_del_prop_index(duk_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx);
DUK_EXTERNAL_DECL duk_bool_t duk_del_prop_heapptr(duk_context *ctx, duk_idx_t obj_idx, void *ptr);
DUK_EXTERNAL_DECL duk_bool_t duk_has_prop(duk_context *ctx, duk_idx_t obj_idx);
DUK_EXTERNAL_DECL duk_bool_t duk_has_prop_string(duk_context *ctx, duk_idx_t obj_idx, const char *key);
DUK_EXTERNAL_DECL duk_bool_t duk_has_prop_lstring(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len);
#if defined(DUK_USE_PREFER_SIZE)
#define duk_has_prop_literal(ctx,obj_idx,key)  duk_has_prop_string((ctx), (obj_idx), (key))
#else
DUK_EXTERNAL_DECL duk_bool_t duk_has_prop_literal_raw(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len);
#define duk_has_prop_literal(ctx,obj_idx,key)  duk_has_prop_literal_raw((ctx), (obj_idx), (key), sizeof((key)) - 1U)
#endif
DUK_EXTERNAL_DECL duk_bool_t duk_has_prop_index(duk_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx);
DUK_EXTERNAL_DECL duk_bool_t duk_has_prop_heapptr(duk_context *ctx, duk_idx_t obj_idx, void *ptr);

DUK_EXTERNAL_DECL void duk_get_prop_desc(duk_context *ctx, duk_idx_t obj_idx, duk_uint_t flags);
DUK_EXTERNAL_DECL void duk_def_prop(duk_context *ctx, duk_idx_t obj_idx, duk_uint_t flags);

DUK_EXTERNAL_DECL duk_bool_t duk_get_global_string(duk_context *ctx, const char *key);
DUK_EXTERNAL_DECL duk_bool_t duk_get_global_lstring(duk_context *ctx, const char *key, duk_size_t key_len);
#if defined(DUK_USE_PREFER_SIZE)
#define duk_get_global_literal(ctx,key)  duk_get_global_string((ctx), (key))
#else
DUK_EXTERNAL_DECL duk_bool_t duk_get_global_literal_raw(duk_context *ctx, const char *key, duk_size_t key_len);
#define duk_get_global_literal(ctx,key)  duk_get_global_literal_raw((ctx), (key), sizeof((key)) - 1U)
#endif
DUK_EXTERNAL_DECL duk_bool_t duk_get_global_heapptr(duk_context *ctx, void *ptr);
DUK_EXTERNAL_DECL duk_bool_t duk_put_global_string(duk_context *ctx, const char *key);
DUK_EXTERNAL_DECL duk_bool_t duk_put_global_lstring(duk_context *ctx, const char *key, duk_size_t key_len);
#if defined(DUK_USE_PREFER_SIZE)
#define duk_put_global_literal(ctx,key)  duk_put_global_string((ctx), (key))
#else
DUK_EXTERNAL_DECL duk_bool_t duk_put_global_literal_raw(duk_context *ctx, const char *key, duk_size_t key_len);
#define duk_put_global_literal(ctx,key)  duk_put_global_literal_raw((ctx), (key), sizeof((key)) - 1U)
#endif
DUK_EXTERNAL_DECL duk_bool_t duk_put_global_heapptr(duk_context *ctx, void *ptr);

/*
 *  Inspection
 */

DUK_EXTERNAL_DECL void duk_inspect_value(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL void duk_inspect_callstack_entry(duk_context *ctx, duk_int_t level);

/*
 *  Object prototype
 */

DUK_EXTERNAL_DECL void duk_get_prototype(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL void duk_set_prototype(duk_context *ctx, duk_idx_t idx);

/*
 *  Object finalizer
 */

DUK_EXTERNAL_DECL void duk_get_finalizer(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL void duk_set_finalizer(duk_context *ctx, duk_idx_t idx);

/*
 *  Global object
 */

DUK_EXTERNAL_DECL void duk_set_global_object(duk_context *ctx);

/*
 *  Duktape/C function magic value
 */

DUK_EXTERNAL_DECL duk_int_t duk_get_magic(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL void duk_set_magic(duk_context *ctx, duk_idx_t idx, duk_int_t magic);
DUK_EXTERNAL_DECL duk_int_t duk_get_current_magic(duk_context *ctx);

/*
 *  Module helpers: put multiple function or constant properties
 */

DUK_EXTERNAL_DECL void duk_put_function_list(duk_context *ctx, duk_idx_t obj_idx, const duk_function_list_entry *funcs);
DUK_EXTERNAL_DECL void duk_put_number_list(duk_context *ctx, duk_idx_t obj_idx, const duk_number_list_entry *numbers);

/*
 *  Object operations
 */

DUK_EXTERNAL_DECL void duk_compact(duk_context *ctx, duk_idx_t obj_idx);
DUK_EXTERNAL_DECL void duk_enum(duk_context *ctx, duk_idx_t obj_idx, duk_uint_t enum_flags);
DUK_EXTERNAL_DECL duk_bool_t duk_next(duk_context *ctx, duk_idx_t enum_idx, duk_bool_t get_value);
DUK_EXTERNAL_DECL void duk_seal(duk_context *ctx, duk_idx_t obj_idx);
DUK_EXTERNAL_DECL void duk_freeze(duk_context *ctx, duk_idx_t obj_idx);

/*
 *  String manipulation
 */

DUK_EXTERNAL_DECL void duk_concat(duk_context *ctx, duk_idx_t count);
DUK_EXTERNAL_DECL void duk_join(duk_context *ctx, duk_idx_t count);
DUK_EXTERNAL_DECL void duk_decode_string(duk_context *ctx, duk_idx_t idx, duk_decode_char_function callback, void *udata);
DUK_EXTERNAL_DECL void duk_map_string(duk_context *ctx, duk_idx_t idx, duk_map_char_function callback, void *udata);
DUK_EXTERNAL_DECL void duk_substring(duk_context *ctx, duk_idx_t idx, duk_size_t start_char_offset, duk_size_t end_char_offset);
DUK_EXTERNAL_DECL void duk_trim(duk_context *ctx, duk_idx_t idx);
DUK_EXTERNAL_DECL duk_codepoint_t duk_char_code_at(duk_context *ctx, duk_idx_t idx, duk_size_t char_offset);

/*
 *  ECMAScript operators
 */

DUK_EXTERNAL_DECL duk_bool_t duk_equals(duk_context *ctx, duk_idx_t idx1, duk_idx_t idx2);
DUK_EXTERNAL_DECL duk_bool_t duk_strict_equals(duk_context *ctx, duk_idx_t idx1, duk_idx_t idx2);
DUK_EXTERNAL_DECL duk_bool_t duk_samevalue(duk_context *ctx, duk_idx_t idx1, duk_idx_t idx2);
DUK_EXTERNAL_DECL duk_bool_t duk_instanceof(duk_context *ctx, duk_idx_t idx1, duk_idx_t idx2);

/*
 *  Random
 */

DUK_EXTERNAL_DECL duk_double_t duk_random(duk_context *ctx);

/*
 *  Function (method) calls
 */

DUK_EXTERNAL_DECL void duk_call(duk_context *ctx, duk_idx_t nargs);
DUK_EXTERNAL_DECL void duk_call_method(duk_context *ctx, duk_idx_t nargs);
DUK_EXTERNAL_DECL void duk_call_prop(duk_context *ctx, duk_idx_t obj_idx, duk_idx_t nargs);
DUK_EXTERNAL_DECL duk_int_t duk_pcall(duk_context *ctx, duk_idx_t nargs);
DUK_EXTERNAL_DECL duk_int_t duk_pcall_method(duk_context *ctx, duk_idx_t nargs);
DUK_EXTERNAL_DECL duk_int_t duk_pcall_prop(duk_context *ctx, duk_idx_t obj_idx, duk_idx_t nargs);
DUK_EXTERNAL_DECL void duk_new(duk_context *ctx, duk_idx_t nargs);
DUK_EXTERNAL_DECL duk_int_t duk_pnew(duk_context *ctx, duk_idx_t nargs);
DUK_EXTERNAL_DECL duk_int_t duk_safe_call(duk_context *ctx, duk_safe_call_function func, void *udata, duk_idx_t nargs, duk_idx_t nrets);

/*
 *  Thread management
 */

/* There are currently no native functions to yield/resume, due to the internal
 * limitations on coroutine handling.  These will be added later.
 */

/*
 *  Compilation and evaluation
 */

DUK_EXTERNAL_DECL duk_int_t duk_eval_raw(duk_context *ctx, const char *src_buffer, duk_size_t src_length, duk_uint_t flags);
DUK_EXTERNAL_DECL duk_int_t duk_compile_raw(duk_context *ctx, const char *src_buffer, duk_size_t src_length, duk_uint_t flags);

/* plain */
#define duk_eval(ctx)  \
    ((void) duk_eval_raw((ctx), NULL, 0, 1 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_NOFILENAME))

#define duk_eval_noresult(ctx)  \
    ((void) duk_eval_raw((ctx), NULL, 0, 1 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_NORESULT | DUK_COMPILE_NOFILENAME))

#define duk_peval(ctx)  \
    (duk_eval_raw((ctx), NULL, 0, 1 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_SAFE | DUK_COMPILE_NOFILENAME))

#define duk_peval_noresult(ctx)  \
    (duk_eval_raw((ctx), NULL, 0, 1 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_SAFE | DUK_COMPILE_NORESULT | DUK_COMPILE_NOFILENAME))

#define duk_compile(ctx,flags)  \
    ((void) duk_compile_raw((ctx), NULL, 0, 2 /*args*/ | (flags)))

#define duk_pcompile(ctx,flags)  \
    (duk_compile_raw((ctx), NULL, 0, 2 /*args*/ | (flags) | DUK_COMPILE_SAFE))

/* string */
#define duk_eval_string(ctx,src)  \
    ((void) duk_eval_raw((ctx), (src), 0, 0 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_NOSOURCE | DUK_COMPILE_STRLEN | DUK_COMPILE_NOFILENAME))

#define duk_eval_string_noresult(ctx,src)  \
    ((void) duk_eval_raw((ctx), (src), 0, 0 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_NOSOURCE | DUK_COMPILE_STRLEN | DUK_COMPILE_NORESULT | DUK_COMPILE_NOFILENAME))

#define duk_peval_string(ctx,src)  \
    (duk_eval_raw((ctx), (src), 0, 0 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_SAFE | DUK_COMPILE_NOSOURCE | DUK_COMPILE_STRLEN | DUK_COMPILE_NOFILENAME))

#define duk_peval_string_noresult(ctx,src)  \
    (duk_eval_raw((ctx), (src), 0, 0 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_SAFE | DUK_COMPILE_NOSOURCE | DUK_COMPILE_STRLEN | DUK_COMPILE_NORESULT | DUK_COMPILE_NOFILENAME))

#define duk_compile_string(ctx,flags,src)  \
    ((void) duk_compile_raw((ctx), (src), 0, 0 /*args*/ | (flags) | DUK_COMPILE_NOSOURCE | DUK_COMPILE_STRLEN | DUK_COMPILE_NOFILENAME))

#define duk_compile_string_filename(ctx,flags,src)  \
    ((void) duk_compile_raw((ctx), (src), 0, 1 /*args*/ | (flags) | DUK_COMPILE_NOSOURCE | DUK_COMPILE_STRLEN))

#define duk_pcompile_string(ctx,flags,src)  \
    (duk_compile_raw((ctx), (src), 0, 0 /*args*/ | (flags) | DUK_COMPILE_SAFE | DUK_COMPILE_NOSOURCE | DUK_COMPILE_STRLEN | DUK_COMPILE_NOFILENAME))

#define duk_pcompile_string_filename(ctx,flags,src)  \
    (duk_compile_raw((ctx), (src), 0, 1 /*args*/ | (flags) | DUK_COMPILE_SAFE | DUK_COMPILE_NOSOURCE | DUK_COMPILE_STRLEN))

/* lstring */
#define duk_eval_lstring(ctx,buf,len)  \
    ((void) duk_eval_raw((ctx), buf, len, 0 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_NOSOURCE | DUK_COMPILE_NOFILENAME))

#define duk_eval_lstring_noresult(ctx,buf,len)  \
    ((void) duk_eval_raw((ctx), buf, len, 0 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_NOSOURCE | DUK_COMPILE_NORESULT | DUK_COMPILE_NOFILENAME))

#define duk_peval_lstring(ctx,buf,len)  \
    (duk_eval_raw((ctx), buf, len, 0 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_NOSOURCE | DUK_COMPILE_SAFE | DUK_COMPILE_NOFILENAME))

#define duk_peval_lstring_noresult(ctx,buf,len)  \
    (duk_eval_raw((ctx), buf, len, 0 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_SAFE | DUK_COMPILE_NOSOURCE | DUK_COMPILE_NORESULT | DUK_COMPILE_NOFILENAME))

#define duk_compile_lstring(ctx,flags,buf,len)  \
    ((void) duk_compile_raw((ctx), buf, len, 0 /*args*/ | (flags) | DUK_COMPILE_NOSOURCE | DUK_COMPILE_NOFILENAME))

#define duk_compile_lstring_filename(ctx,flags,buf,len)  \
    ((void) duk_compile_raw((ctx), buf, len, 1 /*args*/ | (flags) | DUK_COMPILE_NOSOURCE))

#define duk_pcompile_lstring(ctx,flags,buf,len)  \
    (duk_compile_raw((ctx), buf, len, 0 /*args*/ | (flags) | DUK_COMPILE_SAFE | DUK_COMPILE_NOSOURCE | DUK_COMPILE_NOFILENAME))

#define duk_pcompile_lstring_filename(ctx,flags,buf,len)  \
    (duk_compile_raw((ctx), buf, len, 1 /*args*/ | (flags) | DUK_COMPILE_SAFE | DUK_COMPILE_NOSOURCE))

/*
 *  Bytecode load/dump
 */

DUK_EXTERNAL_DECL void duk_dump_function(duk_context *ctx);
DUK_EXTERNAL_DECL void duk_load_function(duk_context *ctx);

/*
 *  Debugging
 */

DUK_EXTERNAL_DECL void duk_push_context_dump(duk_context *ctx);

/*
 *  Debugger (debug protocol)
 */

DUK_EXTERNAL_DECL void duk_debugger_attach(duk_context *ctx,
                                           duk_debug_read_function read_cb,
                                           duk_debug_write_function write_cb,
                                           duk_debug_peek_function peek_cb,
                                           duk_debug_read_flush_function read_flush_cb,
                                           duk_debug_write_flush_function write_flush_cb,
                                           duk_debug_request_function request_cb,
                                           duk_debug_detached_function detached_cb,
                                           void *udata);
DUK_EXTERNAL_DECL void duk_debugger_detach(duk_context *ctx);
DUK_EXTERNAL_DECL void duk_debugger_cooperate(duk_context *ctx);
DUK_EXTERNAL_DECL duk_bool_t duk_debugger_notify(duk_context *ctx, duk_idx_t nvalues);
DUK_EXTERNAL_DECL void duk_debugger_pause(duk_context *ctx);

/*
 *  Time handling
 */

DUK_EXTERNAL_DECL duk_double_t duk_get_now(duk_context *ctx);
DUK_EXTERNAL_DECL void duk_time_to_components(duk_context *ctx, duk_double_t timeval, duk_time_components *comp);
DUK_EXTERNAL_DECL duk_double_t duk_components_to_time(duk_context *ctx, duk_time_components *comp);

/*
 *  Date provider related constants
 *
 *  NOTE: These are "semi public" - you should only use these if you write
 *  your own platform specific Date provider, see doc/datetime.rst.
 */

/* Millisecond count constants. */
#define DUK_DATE_MSEC_SECOND          1000L
#define DUK_DATE_MSEC_MINUTE          (60L * 1000L)
#define DUK_DATE_MSEC_HOUR            (60L * 60L * 1000L)
#define DUK_DATE_MSEC_DAY             (24L * 60L * 60L * 1000L)

/* ECMAScript date range is 100 million days from Epoch:
 * > 100e6 * 24 * 60 * 60 * 1000  // 100M days in millisecs
 * 8640000000000000
 * (= 8.64e15)
 */
#define DUK_DATE_MSEC_100M_DAYS         (8.64e15)
#define DUK_DATE_MSEC_100M_DAYS_LEEWAY  (8.64e15 + 24 * 3600e3)

/* ECMAScript year range:
 * > new Date(100e6 * 24 * 3600e3).toISOString()
 * '+275760-09-13T00:00:00.000Z'
 * > new Date(-100e6 * 24 * 3600e3).toISOString()
 * '-271821-04-20T00:00:00.000Z'
 */
#define DUK_DATE_MIN_ECMA_YEAR     (-271821L)
#define DUK_DATE_MAX_ECMA_YEAR     275760L

/* Part indices for internal breakdowns.  Part order from DUK_DATE_IDX_YEAR
 * to DUK_DATE_IDX_MILLISECOND matches argument ordering of ECMAScript API
 * calls (like Date constructor call).  Some functions in duk_bi_date.c
 * depend on the specific ordering, so change with care.  16 bits are not
 * enough for all parts (year, specifically).
 *
 * Must be in-sync with genbuiltins.py.
 */
#define DUK_DATE_IDX_YEAR           0  /* year */
#define DUK_DATE_IDX_MONTH          1  /* month: 0 to 11 */
#define DUK_DATE_IDX_DAY            2  /* day within month: 0 to 30 */
#define DUK_DATE_IDX_HOUR           3
#define DUK_DATE_IDX_MINUTE         4
#define DUK_DATE_IDX_SECOND         5
#define DUK_DATE_IDX_MILLISECOND    6
#define DUK_DATE_IDX_WEEKDAY        7  /* weekday: 0 to 6, 0=sunday, 1=monday, etc */
#define DUK_DATE_IDX_NUM_PARTS      8

/* Internal API call flags, used for various functions in duk_bi_date.c.
 * Certain flags are used by only certain functions, but since the flags
 * don't overlap, a single flags value can be passed around to multiple
 * functions.
 *
 * The unused top bits of the flags field are also used to pass values
 * to helpers (duk__get_part_helper() and duk__set_part_helper()).
 *
 * Must be in-sync with genbuiltins.py.
 */

/* NOTE: when writing a Date provider you only need a few specific
 * flags from here, the rest are internal.  Avoid using anything you
 * don't need.
 */

#define DUK_DATE_FLAG_NAN_TO_ZERO          (1 << 0)  /* timeval breakdown: internal time value NaN -> zero */
#define DUK_DATE_FLAG_NAN_TO_RANGE_ERROR   (1 << 1)  /* timeval breakdown: internal time value NaN -> RangeError (toISOString) */
#define DUK_DATE_FLAG_ONEBASED             (1 << 2)  /* timeval breakdown: convert month and day-of-month parts to one-based (default is zero-based) */
#define DUK_DATE_FLAG_EQUIVYEAR            (1 << 3)  /* timeval breakdown: replace year with equivalent year in the [1971,2037] range for DST calculations */
#define DUK_DATE_FLAG_LOCALTIME            (1 << 4)  /* convert time value to local time */
#define DUK_DATE_FLAG_SUB1900              (1 << 5)  /* getter: subtract 1900 from year when getting year part */
#define DUK_DATE_FLAG_TOSTRING_DATE        (1 << 6)  /* include date part in string conversion result */
#define DUK_DATE_FLAG_TOSTRING_TIME        (1 << 7)  /* include time part in string conversion result */
#define DUK_DATE_FLAG_TOSTRING_LOCALE      (1 << 8)  /* use locale specific formatting if available */
#define DUK_DATE_FLAG_TIMESETTER           (1 << 9)  /* setter: call is a time setter (affects hour, min, sec, ms); otherwise date setter (affects year, month, day-in-month) */
#define DUK_DATE_FLAG_YEAR_FIXUP           (1 << 10) /* setter: perform 2-digit year fixup (00...99 -> 1900...1999) */
#define DUK_DATE_FLAG_SEP_T                (1 << 11) /* string conversion: use 'T' instead of ' ' as a separator */
#define DUK_DATE_FLAG_VALUE_SHIFT          12        /* additional values begin at bit 12 */

/*
 *  ROM pointer compression
 */

/* Support array for ROM pointer compression.  Only declared when ROM
 * pointer compression is active.
 */
#if defined(DUK_USE_ROM_OBJECTS) && defined(DUK_USE_HEAPPTR16)
DUK_EXTERNAL_DECL const void * const duk_rom_compressed_pointers[];
#endif

/*
 *  C++ name mangling
 */

#if defined(__cplusplus)
/* end 'extern "C"' wrapper */
}
#endif


/*
 *  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 endian (e.g. ARM FPA)     DUK_USE_DOUBLE_ME
 *
 *  Legacy ARM (FPA) is a special case: ARM double values are in mixed
 *  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.
 *  See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0056d/Bcfhgcgd.html.
 *
 *  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
#define DUK_DBL_IDX_UC0    0
#define DUK_DBL_IDX_UC1    1
#define DUK_DBL_IDX_UC2    2
#define DUK_DBL_IDX_UC3    3
#define DUK_DBL_IDX_UC4    4
#define DUK_DBL_IDX_UC5    5
#define DUK_DBL_IDX_UC6    6
#define DUK_DBL_IDX_UC7    7
#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_ME)
#if defined(DUK_USE_64BIT_OPS)
#define DUK_DBL_IDX_ULL0   0  /* not directly applicable, byte order differs from a double */
#endif
#define DUK_DBL_IDX_UI0    0
#define DUK_DBL_IDX_UI1    1
#define DUK_DBL_IDX_US0    1
#define DUK_DBL_IDX_US1    0
#define DUK_DBL_IDX_US2    3
#define DUK_DBL_IDX_US3    2
#define DUK_DBL_IDX_UC0    3
#define DUK_DBL_IDX_UC1    2
#define DUK_DBL_IDX_UC2    1
#define DUK_DBL_IDX_UC3    0
#define DUK_DBL_IDX_UC4    7
#define DUK_DBL_IDX_UC5    6
#define DUK_DBL_IDX_UC6    5
#define DUK_DBL_IDX_UC7    4
#define DUK_DBL_IDX_VP0    DUK_DBL_IDX_UI0  /* packed tval */
#define DUK_DBL_IDX_VP1    DUK_DBL_IDX_UI1  /* packed tval */
#else
#error internal error
#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 { \
        /* Check must be full. */ \
        if (DUK__DBLUNION_IS_NAN_FULL((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)
#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))
#if 0
#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)
#define DUK_DBLUNION_GET_SIGNBIT(u) (((u)->ull[DUK_DBL_IDX_ULL0] >> 63U))
#else
#define DUK_DBLUNION_HAS_SIGNBIT(u) (((u)->ui[DUK_DBL_IDX_UI0] & 0x80000000UL) != 0)
#define DUK_DBLUNION_GET_SIGNBIT(u) (((u)->ui[DUK_DBL_IDX_UI0] >> 31U))
#endif

#endif  /* DUK_DBLUNION_H_INCLUDED */
/* #include duk_fltunion.h */
//#line 1 "duk_fltunion.h"
/*
 *  Union to access IEEE float memory representation.
 */

#if !defined(DUK_FLTUNION_H_INCLUDED)
#define DUK_FLTUNION_H_INCLUDED

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

union duk_float_union {
    float f;
    duk_uint32_t ui[1];
    duk_uint16_t us[2];
    duk_uint8_t uc[4];
};

typedef union duk_float_union duk_float_union;

#if defined(DUK_USE_DOUBLE_LE) || defined(DUK_USE_DOUBLE_ME)
#define DUK_FLT_IDX_UI0    0
#define DUK_FLT_IDX_US0    1
#define DUK_FLT_IDX_US1    0
#define DUK_FLT_IDX_UC0    3
#define DUK_FLT_IDX_UC1    2
#define DUK_FLT_IDX_UC2    1
#define DUK_FLT_IDX_UC3    0
#elif defined(DUK_USE_DOUBLE_BE)
#define DUK_FLT_IDX_UI0    0
#define DUK_FLT_IDX_US0    0
#define DUK_FLT_IDX_US1    1
#define DUK_FLT_IDX_UC0    0
#define DUK_FLT_IDX_UC1    1
#define DUK_FLT_IDX_UC2    2
#define DUK_FLT_IDX_UC3    3
#else
#error internal error
#endif

#endif  /* DUK_FLTUNION_H_INCLUDED */
/* #include duk_replacements.h */
//#line 1 "duk_replacements.h"
#if !defined(DUK_REPLACEMENTS_H_INCLUDED)
#define DUK_REPLACEMENTS_H_INCLUDED

#if !defined(DUK_SINGLE_FILE)
#if defined(DUK_USE_COMPUTED_INFINITY)
DUK_INTERNAL_DECL double duk_computed_infinity;
#endif
#if defined(DUK_USE_COMPUTED_NAN)
DUK_INTERNAL_DECL double duk_computed_nan;
#endif
#endif  /* !DUK_SINGLE_FILE */

#if defined(DUK_USE_REPL_FPCLASSIFY)
DUK_INTERNAL_DECL int duk_repl_fpclassify(double x);
#endif
#if defined(DUK_USE_REPL_SIGNBIT)
DUK_INTERNAL_DECL int duk_repl_signbit(double x);
#endif
#if defined(DUK_USE_REPL_ISFINITE)
DUK_INTERNAL_DECL int duk_repl_isfinite(double x);
#endif
#if defined(DUK_USE_REPL_ISNAN)
DUK_INTERNAL_DECL int duk_repl_isnan(double x);
#endif
#if defined(DUK_USE_REPL_ISINF)
DUK_INTERNAL_DECL int duk_repl_isinf(double x);
#endif

#endif  /* DUK_REPLACEMENTS_H_INCLUDED */
/* #include duk_jmpbuf.h */
//#line 1 "duk_jmpbuf.h"
/*
 *  Wrapper for jmp_buf.
 *
 *  This is used because jmp_buf is an array type for backward compatibility.
 *  Wrapping jmp_buf in a struct makes pointer references, sizeof, etc,
 *  behave more intuitively.
 *
 *  http://en.wikipedia.org/wiki/Setjmp.h#Member_types
 */

#if !defined(DUK_JMPBUF_H_INCLUDED)
#define DUK_JMPBUF_H_INCLUDED

#if defined(DUK_USE_CPP_EXCEPTIONS)
struct duk_jmpbuf {
    duk_small_int_t dummy;  /* unused */
};
#else
struct duk_jmpbuf {
    DUK_JMPBUF_TYPE jb;
};
#endif

#endif  /* DUK_JMPBUF_H_INCLUDED */
/* #include duk_exception.h */
//#line 1 "duk_exception.h"
/*
 *  Exceptions for Duktape internal throws when C++ exceptions are used
 *  for long control transfers.
 */

#if !defined(DUK_EXCEPTION_H_INCLUDED)
#define DUK_EXCEPTION_H_INCLUDED

#if defined(DUK_USE_CPP_EXCEPTIONS)
/* Internal exception used as a setjmp-longjmp replacement.  User code should
 * NEVER see or catch this exception, so it doesn't inherit from any base
 * class which should minimize the chance of user code accidentally catching
 * the exception.
 */
class duk_internal_exception {
    /* intentionally empty */
};

/* Fatal error, thrown as a specific C++ exception with C++ exceptions
 * enabled.  It is unsafe to continue; doing so may cause crashes or memory
 * leaks.  This is intended to be either uncaught, or caught by user code
 * aware of the "unsafe to continue" semantics.
 */
class duk_fatal_exception : public virtual std::runtime_error {
 public:
    duk_fatal_exception(const char *message) : std::runtime_error(message) {}
};
#endif

#endif  /* DUK_EXCEPTION_H_INCLUDED */
/* #include duk_forwdecl.h */
//#line 1 "duk_forwdecl.h"
/*
 *  Forward declarations for all Duktape structures.
 */

#if !defined(DUK_FORWDECL_H_INCLUDED)
#define DUK_FORWDECL_H_INCLUDED

/*
 *  Forward declarations
 */

#if defined(DUK_USE_CPP_EXCEPTIONS)
class duk_internal_exception;
#else
struct duk_jmpbuf;
#endif

/* duk_tval intentionally skipped */
struct duk_heaphdr;
struct duk_heaphdr_string;
struct duk_harray;
struct duk_hstring;
struct duk_hstring_external;
struct duk_hobject;
struct duk_hcompfunc;
struct duk_hnatfunc;
struct duk_hboundfunc;
struct duk_hthread;
struct duk_hbufobj;
struct duk_hdecenv;
struct duk_hobjenv;
struct duk_hproxy;
struct duk_hbuffer;
struct duk_hbuffer_fixed;
struct duk_hbuffer_dynamic;
struct duk_hbuffer_external;

struct duk_propaccessor;
union duk_propvalue;
struct duk_propdesc;

struct duk_heap;
struct duk_breakpoint;

struct duk_activation;
struct duk_catcher;
struct duk_ljstate;
struct duk_strcache_entry;
struct duk_litcache_entry;
struct duk_strtab_entry;

#if defined(DUK_USE_DEBUG)
struct duk_fixedbuffer;
#endif

struct duk_bitdecoder_ctx;
struct duk_bitencoder_ctx;
struct duk_bufwriter_ctx;

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

struct duk_compiler_instr;
struct duk_compiler_func;
struct duk_compiler_ctx;

struct duk_re_matcher_ctx;
struct duk_re_compiler_ctx;

#if defined(DUK_USE_CPP_EXCEPTIONS)
/* no typedef */
#else
typedef struct duk_jmpbuf duk_jmpbuf;
#endif

/* duk_tval intentionally skipped */
typedef struct duk_heaphdr duk_heaphdr;
typedef struct duk_heaphdr_string duk_heaphdr_string;
typedef struct duk_harray duk_harray;
typedef struct duk_hstring duk_hstring;
typedef struct duk_hstring_external duk_hstring_external;
typedef struct duk_hobject duk_hobject;
typedef struct duk_hcompfunc duk_hcompfunc;
typedef struct duk_hnatfunc duk_hnatfunc;
typedef struct duk_hboundfunc duk_hboundfunc;
typedef struct duk_hthread duk_hthread;
typedef struct duk_hbufobj duk_hbufobj;
typedef struct duk_hdecenv duk_hdecenv;
typedef struct duk_hobjenv duk_hobjenv;
typedef struct duk_hproxy duk_hproxy;
typedef struct duk_hbuffer duk_hbuffer;
typedef struct duk_hbuffer_fixed duk_hbuffer_fixed;
typedef struct duk_hbuffer_dynamic duk_hbuffer_dynamic;
typedef struct duk_hbuffer_external duk_hbuffer_external;

typedef struct duk_propaccessor duk_propaccessor;
typedef union duk_propvalue duk_propvalue;
typedef struct duk_propdesc duk_propdesc;

typedef struct duk_heap duk_heap;
typedef struct duk_breakpoint duk_breakpoint;

typedef struct duk_activation duk_activation;
typedef struct duk_catcher duk_catcher;
typedef struct duk_ljstate duk_ljstate;
typedef struct duk_strcache_entry duk_strcache_entry;
typedef struct duk_litcache_entry duk_litcache_entry;
typedef struct duk_strtab_entry duk_strtab_entry;

#if defined(DUK_USE_DEBUG)
typedef struct duk_fixedbuffer duk_fixedbuffer;
#endif

typedef struct duk_bitdecoder_ctx duk_bitdecoder_ctx;
typedef struct duk_bitencoder_ctx duk_bitencoder_ctx;
typedef struct duk_bufwriter_ctx duk_bufwriter_ctx;

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) | \
                                      ((duk_uint64_t) (duk_uint32_t) (fp)); \
    } while (0)
#endif
#else  /* DUK_USE_64BIT_OPS */
#define DUK__TVAL_SET_LIGHTFUNC(tv,fp,flags)  do { \
        duk_tval *duk__tv; \
        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])
#if defined(DUK_USE_FASTINT)
#define DUK_TVAL_GET_DOUBLE(tv)              ((tv)->d)
#define DUK_TVAL_GET_FASTINT(tv)             DUK__TVAL_GET_FASTINT((tv))
#define DUK_TVAL_GET_FASTINT_U32(tv)         DUK__TVAL_GET_FASTINT_U32((tv))
#define DUK_TVAL_GET_FASTINT_I32(tv)         DUK__TVAL_GET_FASTINT_I32((tv))
#define DUK_TVAL_GET_NUMBER(tv)              duk_tval_get_number_packed((tv))
#else
#define DUK_TVAL_GET_NUMBER(tv)              ((tv)->d)
#define DUK_TVAL_GET_DOUBLE(tv)              ((tv)->d)
#endif
#define DUK_TVAL_GET_LIGHTFUNC(tv,out_fp,out_flags)  do { \
        (out_flags) = (tv)->ui[DUK_DBL_IDX_UI0] & 0xffffUL; \
        (out_fp) = (duk_c_function) (tv)->ui[DUK_DBL_IDX_UI1]; \
    } while (0)
#define DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv)   ((duk_c_function) ((tv)->ui[DUK_DBL_IDX_UI1]))
#define DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv)     (((duk_small_uint_t) (tv)->ui[DUK_DBL_IDX_UI0]) & 0xffffUL)
#define DUK_TVAL_GET_STRING(tv)              ((duk_hstring *) (tv)->vp[DUK_DBL_IDX_VP1])
#define DUK_TVAL_GET_OBJECT(tv)              ((duk_hobject *) (tv)->vp[DUK_DBL_IDX_VP1])
#define DUK_TVAL_GET_BUFFER(tv)              ((duk_hbuffer *) (tv)->vp[DUK_DBL_IDX_VP1])
#define DUK_TVAL_GET_POINTER(tv)             ((void *) (tv)->vp[DUK_DBL_IDX_VP1])
#define DUK_TVAL_GET_HEAPHDR(tv)             ((duk_heaphdr *) (tv)->vp[DUK_DBL_IDX_VP1])

/* decoding */
#define DUK_TVAL_GET_TAG(tv)                 ((duk_small_uint_t) (tv)->us[DUK_DBL_IDX_US0])

#define DUK_TVAL_IS_UNDEFINED(tv)            (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_UNDEFINED)
#define DUK_TVAL_IS_UNUSED(tv)               (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_UNUSED)
#define DUK_TVAL_IS_NULL(tv)                 (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_NULL)
#define DUK_TVAL_IS_BOOLEAN(tv)              (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_BOOLEAN)
#define DUK_TVAL_IS_BOOLEAN_TRUE(tv)         ((tv)->ui[DUK_DBL_IDX_UI0] == DUK_XTAG_BOOLEAN_TRUE)
#define DUK_TVAL_IS_BOOLEAN_FALSE(tv)        ((tv)->ui[DUK_DBL_IDX_UI0] == DUK_XTAG_BOOLEAN_FALSE)
#define DUK_TVAL_IS_LIGHTFUNC(tv)            (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_LIGHTFUNC)
#define DUK_TVAL_IS_STRING(tv)               (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_STRING)
#define DUK_TVAL_IS_OBJECT(tv)               (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_OBJECT)
#define DUK_TVAL_IS_BUFFER(tv)               (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_BUFFER)
#define DUK_TVAL_IS_POINTER(tv)              (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_POINTER)
#if defined(DUK_USE_FASTINT)
/* 0xfff0 is -Infinity */
#define DUK_TVAL_IS_DOUBLE(tv)               (DUK_TVAL_GET_TAG((tv)) <= 0xfff0UL)
#define DUK_TVAL_IS_FASTINT(tv)              (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_FASTINT)
#define DUK_TVAL_IS_NUMBER(tv)               (DUK_TVAL_GET_TAG((tv)) <= 0xfff1UL)
#else
#define DUK_TVAL_IS_NUMBER(tv)               (DUK_TVAL_GET_TAG((tv)) <= 0xfff0UL)
#define DUK_TVAL_IS_DOUBLE(tv)               DUK_TVAL_IS_NUMBER((tv))
#endif

/* This is performance critical because it appears in every DECREF. */
#define DUK_TVAL_IS_HEAP_ALLOCATED(tv)       (DUK_TVAL_GET_TAG((tv)) >= DUK_TAG_STRING)

#if defined(DUK_USE_FASTINT)
DUK_INTERNAL_DECL duk_double_t duk_tval_get_number_packed(duk_tval *tv);
#endif

#else  /* DUK_USE_PACKED_TVAL */
/* ======================================================================== */

/*
 *  Portable 12-byte representation
 */

/* Note: not initializing all bytes is normally not an issue: Duktape won't
 * read or use the uninitialized bytes so valgrind won't issue warnings.
 * In some special cases a harmless valgrind warning may be issued though.
 * For example, the DumpHeap debugger command writes out a compiled function's
 * 'data' area as is, including any uninitialized bytes, which causes a
 * valgrind warning.
 */

typedef struct duk_tval_struct duk_tval;

struct duk_tval_struct {
    duk_small_uint_t t;
    duk_small_uint_t v_extra;
    union {
        duk_double_t d;
        duk_small_int_t i;
#if defined(DUK_USE_FASTINT)
        duk_int64_t fi;  /* if present, forces 16-byte duk_tval */
#endif
        void *voidptr;
        duk_hstring *hstring;
        duk_hobject *hobject;
        duk_hcompfunc *hcompfunc;
        duk_hnatfunc *hnatfunc;
        duk_hthread *hthread;
        duk_hbuffer *hbuffer;
        duk_heaphdr *heaphdr;
        duk_c_function lightfunc;
    } v;
};

typedef struct {
    duk_small_uint_t t;
    duk_small_uint_t v_extra;
    /* The rest of the fields don't matter except for debug dumps and such
     * for which a partial initializer may trigger out-ot-bounds memory
     * reads.  Include a double field which is usually as large or larger
     * than pointers (not always however).
     */
    duk_double_t d;
} duk_tval_unused;

#define DUK_TVAL_UNUSED_INITIALIZER() \
    { DUK_TAG_UNUSED, 0, 0.0 }

#define DUK_TAG_MIN                   0
#define DUK_TAG_NUMBER                0  /* DUK_TAG_NUMBER only defined for non-packed duk_tval */
#if defined(DUK_USE_FASTINT)
#define DUK_TAG_FASTINT               1
#endif
#define DUK_TAG_UNDEFINED             2
#define DUK_TAG_NULL                  3
#define DUK_TAG_BOOLEAN               4
#define DUK_TAG_POINTER               5
#define DUK_TAG_LIGHTFUNC             6
#define DUK_TAG_UNUSED                7  /* marker; not actual tagged type */
#define DUK_TAG_STRING                8  /* first heap allocated, match bit boundary */
#define DUK_TAG_OBJECT                9
#define DUK_TAG_BUFFER                10
#define DUK_TAG_MAX                   10

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

/* DUK_TAG_NUMBER is intentionally first, as it is the default clause in code
 * to support the 8-byte representation.  Further, it is a non-heap-allocated
 * type so it should come before DUK_TAG_STRING.  Finally, it should not break
 * the tag value ranges covered by case-clauses in a switch-case.
 */

/* setters */
#define DUK_TVAL_SET_UNDEFINED(tv)  do { \
        duk_tval *duk__tv; \
        duk__tv = (tv); \
        duk__tv->t = DUK_TAG_UNDEFINED; \
    } while (0)

#define DUK_TVAL_SET_UNUSED(tv)  do { \
        duk_tval *duk__tv; \
        duk__tv = (tv); \
        duk__tv->t = DUK_TAG_UNUSED; \
    } while (0)

#define DUK_TVAL_SET_NULL(tv)  do { \
        duk_tval *duk__tv; \
        duk__tv = (tv); \
        duk__tv->t = DUK_TAG_NULL; \
    } while (0)

#define DUK_TVAL_SET_BOOLEAN(tv,val)  do { \
        duk_tval *duk__tv; \
        duk__tv = (tv); \
        duk__tv->t = DUK_TAG_BOOLEAN; \
        duk__tv->v.i = (duk_small_int_t) (val); \
    } while (0)

#if defined(DUK_USE_FASTINT)
#define DUK_TVAL_SET_DOUBLE(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_I48(tv,val)  do { \
        duk_tval *duk__tv; \
        duk__tv = (tv); \
        duk__tv->t = DUK_TAG_FASTINT; \
        duk__tv->v.fi = (val); \
    } while (0)
#define DUK_TVAL_SET_U32(tv,val)  do { \
        duk_tval *duk__tv; \
        duk__tv = (tv); \
        duk__tv->t = DUK_TAG_FASTINT; \
        duk__tv->v.fi = (duk_int64_t) (val); \
    } while (0)
#define DUK_TVAL_SET_I32(tv,val)  do { \
        duk_tval *duk__tv; \
        duk__tv = (tv); \
        duk__tv->t = DUK_TAG_FASTINT; \
        duk__tv->v.fi = (duk_int64_t) (val); \
    } while (0)
#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,val) \
    DUK_TVAL_SET_DOUBLE((tv), (val))
#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) \
    DUK_TVAL_SET_NUMBER((tv), (d))
#define DUK_TVAL_SET_I48(tv,val) \
    DUK_TVAL_SET_NUMBER((tv), (duk_double_t) (val))  /* XXX: fast int-to-double */
#define DUK_TVAL_SET_U32(tv,val) \
    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)
#define DUK_TVAL_IS_LIGHTFUNC(tv)          ((tv)->t == DUK_TAG_LIGHTFUNC)
#define DUK_TVAL_IS_STRING(tv)             ((tv)->t == DUK_TAG_STRING)
#define DUK_TVAL_IS_OBJECT(tv)             ((tv)->t == DUK_TAG_OBJECT)
#define DUK_TVAL_IS_BUFFER(tv)             ((tv)->t == DUK_TAG_BUFFER)

/* This is performance critical because it's needed for every DECREF.
 * Take advantage of the fact that the first heap allocated tag is 8,
 * so that bit 3 is set for all heap allocated tags (and never set for
 * non-heap-allocated tags).
 */
#if 0
#define DUK_TVAL_IS_HEAP_ALLOCATED(tv)     ((tv)->t >= DUK_TAG_STRING)
#endif
#define DUK_TVAL_IS_HEAP_ALLOCATED(tv)     ((tv)->t & 0x08)

#if defined(DUK_USE_FASTINT)
#if 0
DUK_INTERNAL_DECL duk_double_t duk_tval_get_number_unpacked(duk_tval *tv);
#endif
DUK_INTERNAL_DECL duk_double_t duk_tval_get_number_unpacked_fastint(duk_tval *tv);
#endif

#endif  /* DUK_USE_PACKED_TVAL */

/*
 *  Convenience (independent of representation)
 */

#define DUK_TVAL_SET_BOOLEAN_TRUE(tv)        DUK_TVAL_SET_BOOLEAN((tv), 1)
#define DUK_TVAL_SET_BOOLEAN_FALSE(tv)       DUK_TVAL_SET_BOOLEAN((tv), 0)

#define DUK_TVAL_STRING_IS_SYMBOL(tv) \
    DUK_HSTRING_HAS_SYMBOL(DUK_TVAL_GET_STRING((tv)))

/* Lightfunc flags packing and unpacking. */
/* Sign extend: 0x0000##00 -> 0x##000000 -> sign extend to 0xssssss##.
 * Avoid signed shifts due to portability limitations.
 */
#define DUK_LFUNC_FLAGS_GET_MAGIC(lf_flags) \
    ((duk_int32_t) (duk_int8_t) (((duk_uint16_t) (lf_flags)) >> 8))
#define DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags) \
    (((lf_flags) >> 4) & 0x0fU)
#define DUK_LFUNC_FLAGS_GET_NARGS(lf_flags) \
    ((lf_flags) & 0x0fU)
#define DUK_LFUNC_FLAGS_PACK(magic,length,nargs) \
    ((((duk_small_uint_t) (magic)) & 0xffU) << 8) | ((length) << 4) | (nargs)

#define DUK_LFUNC_NARGS_VARARGS             0x0f   /* varargs marker */
#define DUK_LFUNC_NARGS_MIN                 0x00
#define DUK_LFUNC_NARGS_MAX                 0x0e   /* max, excl. varargs marker */
#define DUK_LFUNC_LENGTH_MIN                0x00
#define DUK_LFUNC_LENGTH_MAX                0x0f
#define DUK_LFUNC_MAGIC_MIN                 (-0x80)
#define DUK_LFUNC_MAGIC_MAX                 0x7f

/* fastint constants etc */
#if defined(DUK_USE_FASTINT)
#define DUK_FASTINT_MIN           (DUK_I64_CONSTANT(-0x800000000000))
#define DUK_FASTINT_MAX           (DUK_I64_CONSTANT(0x7fffffffffff))
#define DUK_FASTINT_BITS          48

DUK_INTERNAL_DECL void duk_tval_set_number_chkfast_fast(duk_tval *tv, duk_double_t x);
DUK_INTERNAL_DECL void duk_tval_set_number_chkfast_slow(duk_tval *tv, duk_double_t x);
#endif

#if defined(DUK_USE_ASSERTIONS)
DUK_INTERNAL_DECL void duk_tval_assert_valid(duk_tval *tv);
#define DUK_TVAL_ASSERT_VALID(tv)  do { duk_tval_assert_valid((tv)); } while (0)
#else
#define DUK_TVAL_ASSERT_VALID(tv)  do {} while (0)
#endif

#endif  /* DUK_TVAL_H_INCLUDED */
/* #include duk_builtins.h */
//#line 1 "duk_builtins.h"
/*
 *  Automatically generated by genbuiltins.py, do not edit!
 */

#if !defined(DUK_BUILTINS_H_INCLUDED)
#define DUK_BUILTINS_H_INCLUDED

#if defined(DUK_USE_ROM_STRINGS)
#error ROM support not enabled, rerun configure.py with --rom-support
#else  /* DUK_USE_ROM_STRINGS */
#define DUK_STRIDX_UC_UNDEFINED                                       0                              /* 'Undefined' */
#define DUK_HEAP_STRING_UC_UNDEFINED(heap)                            DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_UNDEFINED)
#define DUK_HTHREAD_STRING_UC_UNDEFINED(thr)                          DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_UNDEFINED)
#define DUK_STRIDX_UC_NULL                                            1                              /* 'Null' */
#define DUK_HEAP_STRING_UC_NULL(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_NULL)
#define DUK_HTHREAD_STRING_UC_NULL(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_NULL)
#define DUK_STRIDX_UC_SYMBOL                                          2                              /* 'Symbol' */
#define DUK_HEAP_STRING_UC_SYMBOL(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_SYMBOL)
#define DUK_HTHREAD_STRING_UC_SYMBOL(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_SYMBOL)
#define DUK_STRIDX_UC_ARGUMENTS                                       3                              /* 'Arguments' */
#define DUK_HEAP_STRING_UC_ARGUMENTS(heap)                            DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_ARGUMENTS)
#define DUK_HTHREAD_STRING_UC_ARGUMENTS(thr)                          DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_ARGUMENTS)
#define DUK_STRIDX_UC_OBJECT                                          4                              /* 'Object' */
#define DUK_HEAP_STRING_UC_OBJECT(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_OBJECT)
#define DUK_HTHREAD_STRING_UC_OBJECT(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_OBJECT)
#define DUK_STRIDX_UC_FUNCTION                                        5                              /* 'Function' */
#define DUK_HEAP_STRING_UC_FUNCTION(heap)                             DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_FUNCTION)
#define DUK_HTHREAD_STRING_UC_FUNCTION(thr)                           DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_FUNCTION)
#define DUK_STRIDX_UC_ARRAY                                           6                              /* 'Array' */
#define DUK_HEAP_STRING_UC_ARRAY(heap)                                DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_ARRAY)
#define DUK_HTHREAD_STRING_UC_ARRAY(thr)                              DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_ARRAY)
#define DUK_STRIDX_UC_STRING                                          7                              /* 'String' */
#define DUK_HEAP_STRING_UC_STRING(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_STRING)
#define DUK_HTHREAD_STRING_UC_STRING(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_STRING)
#define DUK_STRIDX_UC_BOOLEAN                                         8                              /* 'Boolean' */
#define DUK_HEAP_STRING_UC_BOOLEAN(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_BOOLEAN)
#define DUK_HTHREAD_STRING_UC_BOOLEAN(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_BOOLEAN)
#define DUK_STRIDX_UC_NUMBER                                          9                              /* 'Number' */
#define DUK_HEAP_STRING_UC_NUMBER(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_NUMBER)
#define DUK_HTHREAD_STRING_UC_NUMBER(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_NUMBER)
#define DUK_STRIDX_UC_DATE                                            10                             /* 'Date' */
#define DUK_HEAP_STRING_UC_DATE(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_DATE)
#define DUK_HTHREAD_STRING_UC_DATE(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_DATE)
#define DUK_STRIDX_REG_EXP                                            11                             /* 'RegExp' */
#define DUK_HEAP_STRING_REG_EXP(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_REG_EXP)
#define DUK_HTHREAD_STRING_REG_EXP(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_REG_EXP)
#define DUK_STRIDX_UC_ERROR                                           12                             /* 'Error' */
#define DUK_HEAP_STRING_UC_ERROR(heap)                                DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_ERROR)
#define DUK_HTHREAD_STRING_UC_ERROR(thr)                              DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_ERROR)
#define DUK_STRIDX_MATH                                               13                             /* 'Math' */
#define DUK_HEAP_STRING_MATH(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_MATH)
#define DUK_HTHREAD_STRING_MATH(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_MATH)
#define DUK_STRIDX_JSON                                               14                             /* 'JSON' */
#define DUK_HEAP_STRING_JSON(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON)
#define DUK_HTHREAD_STRING_JSON(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON)
#define DUK_STRIDX_EMPTY_STRING                                       15                             /* '' */
#define DUK_HEAP_STRING_EMPTY_STRING(heap)                            DUK_HEAP_GET_STRING((heap),DUK_STRIDX_EMPTY_STRING)
#define DUK_HTHREAD_STRING_EMPTY_STRING(thr)                          DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_EMPTY_STRING)
#define DUK_STRIDX_ARRAY_BUFFER                                       16                             /* 'ArrayBuffer' */
#define DUK_HEAP_STRING_ARRAY_BUFFER(heap)                            DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ARRAY_BUFFER)
#define DUK_HTHREAD_STRING_ARRAY_BUFFER(thr)                          DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ARRAY_BUFFER)
#define DUK_STRIDX_DATA_VIEW                                          17                             /* 'DataView' */
#define DUK_HEAP_STRING_DATA_VIEW(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DATA_VIEW)
#define DUK_HTHREAD_STRING_DATA_VIEW(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DATA_VIEW)
#define DUK_STRIDX_INT8_ARRAY                                         18                             /* 'Int8Array' */
#define DUK_HEAP_STRING_INT8_ARRAY(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT8_ARRAY)
#define DUK_HTHREAD_STRING_INT8_ARRAY(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT8_ARRAY)
#define DUK_STRIDX_UINT8_ARRAY                                        19                             /* 'Uint8Array' */
#define DUK_HEAP_STRING_UINT8_ARRAY(heap)                             DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UINT8_ARRAY)
#define DUK_HTHREAD_STRING_UINT8_ARRAY(thr)                           DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UINT8_ARRAY)
#define DUK_STRIDX_UINT8_CLAMPED_ARRAY                                20                             /* 'Uint8ClampedArray' */
#define DUK_HEAP_STRING_UINT8_CLAMPED_ARRAY(heap)                     DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UINT8_CLAMPED_ARRAY)
#define DUK_HTHREAD_STRING_UINT8_CLAMPED_ARRAY(thr)                   DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UINT8_CLAMPED_ARRAY)
#define DUK_STRIDX_INT16_ARRAY                                        21                             /* 'Int16Array' */
#define DUK_HEAP_STRING_INT16_ARRAY(heap)                             DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT16_ARRAY)
#define DUK_HTHREAD_STRING_INT16_ARRAY(thr)                           DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT16_ARRAY)
#define DUK_STRIDX_UINT16_ARRAY                                       22                             /* 'Uint16Array' */
#define DUK_HEAP_STRING_UINT16_ARRAY(heap)                            DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UINT16_ARRAY)
#define DUK_HTHREAD_STRING_UINT16_ARRAY(thr)                          DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UINT16_ARRAY)
#define DUK_STRIDX_INT32_ARRAY                                        23                             /* 'Int32Array' */
#define DUK_HEAP_STRING_INT32_ARRAY(heap)                             DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT32_ARRAY)
#define DUK_HTHREAD_STRING_INT32_ARRAY(thr)                           DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT32_ARRAY)
#define DUK_STRIDX_UINT32_ARRAY                                       24                             /* 'Uint32Array' */
#define DUK_HEAP_STRING_UINT32_ARRAY(heap)                            DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UINT32_ARRAY)
#define DUK_HTHREAD_STRING_UINT32_ARRAY(thr)                          DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UINT32_ARRAY)
#define DUK_STRIDX_FLOAT32_ARRAY                                      25                             /* 'Float32Array' */
#define DUK_HEAP_STRING_FLOAT32_ARRAY(heap)                           DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FLOAT32_ARRAY)
#define DUK_HTHREAD_STRING_FLOAT32_ARRAY(thr)                         DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FLOAT32_ARRAY)
#define DUK_STRIDX_FLOAT64_ARRAY                                      26                             /* 'Float64Array' */
#define DUK_HEAP_STRING_FLOAT64_ARRAY(heap)                           DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FLOAT64_ARRAY)
#define DUK_HTHREAD_STRING_FLOAT64_ARRAY(thr)                         DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FLOAT64_ARRAY)
#define DUK_STRIDX_GLOBAL                                             27                             /* 'global' */
#define DUK_HEAP_STRING_GLOBAL(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_GLOBAL)
#define DUK_HTHREAD_STRING_GLOBAL(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_GLOBAL)
#define DUK_STRIDX_OBJ_ENV                                            28                             /* 'ObjEnv' */
#define DUK_HEAP_STRING_OBJ_ENV(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_OBJ_ENV)
#define DUK_HTHREAD_STRING_OBJ_ENV(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_OBJ_ENV)
#define DUK_STRIDX_DEC_ENV                                            29                             /* 'DecEnv' */
#define DUK_HEAP_STRING_DEC_ENV(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DEC_ENV)
#define DUK_HTHREAD_STRING_DEC_ENV(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DEC_ENV)
#define DUK_STRIDX_UC_BUFFER                                          30                             /* 'Buffer' */
#define DUK_HEAP_STRING_UC_BUFFER(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_BUFFER)
#define DUK_HTHREAD_STRING_UC_BUFFER(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_BUFFER)
#define DUK_STRIDX_UC_POINTER                                         31                             /* 'Pointer' */
#define DUK_HEAP_STRING_UC_POINTER(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_POINTER)
#define DUK_HTHREAD_STRING_UC_POINTER(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_POINTER)
#define DUK_STRIDX_UC_THREAD                                          32                             /* 'Thread' */
#define DUK_HEAP_STRING_UC_THREAD(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_THREAD)
#define DUK_HTHREAD_STRING_UC_THREAD(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_THREAD)
#define DUK_STRIDX_EVAL                                               33                             /* 'eval' */
#define DUK_HEAP_STRING_EVAL(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_EVAL)
#define DUK_HTHREAD_STRING_EVAL(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_EVAL)
#define DUK_STRIDX_VALUE                                              34                             /* 'value' */
#define DUK_HEAP_STRING_VALUE(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_VALUE)
#define DUK_HTHREAD_STRING_VALUE(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_VALUE)
#define DUK_STRIDX_WRITABLE                                           35                             /* 'writable' */
#define DUK_HEAP_STRING_WRITABLE(heap)                                DUK_HEAP_GET_STRING((heap),DUK_STRIDX_WRITABLE)
#define DUK_HTHREAD_STRING_WRITABLE(thr)                              DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_WRITABLE)
#define DUK_STRIDX_CONFIGURABLE                                       36                             /* 'configurable' */
#define DUK_HEAP_STRING_CONFIGURABLE(heap)                            DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CONFIGURABLE)
#define DUK_HTHREAD_STRING_CONFIGURABLE(thr)                          DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CONFIGURABLE)
#define DUK_STRIDX_ENUMERABLE                                         37                             /* 'enumerable' */
#define DUK_HEAP_STRING_ENUMERABLE(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ENUMERABLE)
#define DUK_HTHREAD_STRING_ENUMERABLE(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ENUMERABLE)
#define DUK_STRIDX_JOIN                                               38                             /* 'join' */
#define DUK_HEAP_STRING_JOIN(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JOIN)
#define DUK_HTHREAD_STRING_JOIN(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JOIN)
#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' */
#define DUK_HEAP_STRING_WELLKNOWN_SYMBOL_IS_CONCAT_SPREADABLE(heap)   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_WELLKNOWN_SYMBOL_IS_CONCAT_SPREADABLE)
#define DUK_HTHREAD_STRING_WELLKNOWN_SYMBOL_IS_CONCAT_SPREADABLE(thr)  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_WELLKNOWN_SYMBOL_IS_CONCAT_SPREADABLE)
#define DUK_STRIDX_SET_PROTOTYPE_OF                                   80                             /* 'setPrototypeOf' */
#define DUK_HEAP_STRING_SET_PROTOTYPE_OF(heap)                        DUK_HEAP_GET_STRING((heap),DUK_STRIDX_SET_PROTOTYPE_OF)
#define DUK_HTHREAD_STRING_SET_PROTOTYPE_OF(thr)                      DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_SET_PROTOTYPE_OF)
#define DUK_STRIDX___PROTO__                                          81                             /* '__proto__' */
#define DUK_HEAP_STRING___PROTO__(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX___PROTO__)
#define DUK_HTHREAD_STRING___PROTO__(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX___PROTO__)
#define DUK_STRIDX_TO_STRING                                          82                             /* 'toString' */
#define DUK_HEAP_STRING_TO_STRING(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TO_STRING)
#define DUK_HTHREAD_STRING_TO_STRING(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TO_STRING)
#define DUK_STRIDX_TO_JSON                                            83                             /* 'toJSON' */
#define DUK_HEAP_STRING_TO_JSON(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TO_JSON)
#define DUK_HTHREAD_STRING_TO_JSON(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TO_JSON)
#define DUK_STRIDX_TYPE                                               84                             /* 'type' */
#define DUK_HEAP_STRING_TYPE(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TYPE)
#define DUK_HTHREAD_STRING_TYPE(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TYPE)
#define DUK_STRIDX_DATA                                               85                             /* 'data' */
#define DUK_HEAP_STRING_DATA(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DATA)
#define DUK_HTHREAD_STRING_DATA(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DATA)
#define DUK_STRIDX_LC_BUFFER                                          86                             /* 'buffer' */
#define DUK_HEAP_STRING_LC_BUFFER(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_BUFFER)
#define DUK_HTHREAD_STRING_LC_BUFFER(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_BUFFER)
#define DUK_STRIDX_LENGTH                                             87                             /* 'length' */
#define DUK_HEAP_STRING_LENGTH(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LENGTH)
#define DUK_HTHREAD_STRING_LENGTH(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LENGTH)
#define DUK_STRIDX_SET                                                88                             /* 'set' */
#define DUK_HEAP_STRING_SET(heap)                                     DUK_HEAP_GET_STRING((heap),DUK_STRIDX_SET)
#define DUK_HTHREAD_STRING_SET(thr)                                   DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_SET)
#define DUK_STRIDX_STACK                                              89                             /* 'stack' */
#define DUK_HEAP_STRING_STACK(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_STACK)
#define DUK_HTHREAD_STRING_STACK(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_STACK)
#define DUK_STRIDX_PC                                                 90                             /* 'pc' */
#define DUK_HEAP_STRING_PC(heap)                                      DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PC)
#define DUK_HTHREAD_STRING_PC(thr)                                    DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PC)
#define DUK_STRIDX_LINE_NUMBER                                        91                             /* 'lineNumber' */
#define DUK_HEAP_STRING_LINE_NUMBER(heap)                             DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LINE_NUMBER)
#define DUK_HTHREAD_STRING_LINE_NUMBER(thr)                           DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LINE_NUMBER)
#define DUK_STRIDX_INT_TRACEDATA                                      92                             /* '\x82Tracedata' */
#define DUK_HEAP_STRING_INT_TRACEDATA(heap)                           DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_TRACEDATA)
#define DUK_HTHREAD_STRING_INT_TRACEDATA(thr)                         DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_TRACEDATA)
#define DUK_STRIDX_NAME                                               93                             /* 'name' */
#define DUK_HEAP_STRING_NAME(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_NAME)
#define DUK_HTHREAD_STRING_NAME(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_NAME)
#define DUK_STRIDX_FILE_NAME                                          94                             /* 'fileName' */
#define DUK_HEAP_STRING_FILE_NAME(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FILE_NAME)
#define DUK_HTHREAD_STRING_FILE_NAME(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FILE_NAME)
#define DUK_STRIDX_LC_POINTER                                         95                             /* 'pointer' */
#define DUK_HEAP_STRING_LC_POINTER(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_POINTER)
#define DUK_HTHREAD_STRING_LC_POINTER(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_POINTER)
#define DUK_STRIDX_INT_TARGET                                         96                             /* '\x82Target' */
#define DUK_HEAP_STRING_INT_TARGET(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_TARGET)
#define DUK_HTHREAD_STRING_INT_TARGET(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_TARGET)
#define DUK_STRIDX_INT_NEXT                                           97                             /* '\x82Next' */
#define DUK_HEAP_STRING_INT_NEXT(heap)                                DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_NEXT)
#define DUK_HTHREAD_STRING_INT_NEXT(thr)                              DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_NEXT)
#define DUK_STRIDX_INT_BYTECODE                                       98                             /* '\x82Bytecode' */
#define DUK_HEAP_STRING_INT_BYTECODE(heap)                            DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_BYTECODE)
#define DUK_HTHREAD_STRING_INT_BYTECODE(thr)                          DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_BYTECODE)
#define DUK_STRIDX_INT_FORMALS                                        99                             /* '\x82Formals' */
#define DUK_HEAP_STRING_INT_FORMALS(heap)                             DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_FORMALS)
#define DUK_HTHREAD_STRING_INT_FORMALS(thr)                           DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_FORMALS)
#define DUK_STRIDX_INT_VARMAP                                         100                            /* '\x82Varmap' */
#define DUK_HEAP_STRING_INT_VARMAP(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_VARMAP)
#define DUK_HTHREAD_STRING_INT_VARMAP(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_VARMAP)
#define DUK_STRIDX_INT_SOURCE                                         101                            /* '\x82Source' */
#define DUK_HEAP_STRING_INT_SOURCE(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_SOURCE)
#define DUK_HTHREAD_STRING_INT_SOURCE(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_SOURCE)
#define DUK_STRIDX_INT_PC2LINE                                        102                            /* '\x82Pc2line' */
#define DUK_HEAP_STRING_INT_PC2LINE(heap)                             DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_PC2LINE)
#define DUK_HTHREAD_STRING_INT_PC2LINE(thr)                           DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_PC2LINE)
#define DUK_STRIDX_INT_MAP                                            103                            /* '\x82Map' */
#define DUK_HEAP_STRING_INT_MAP(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_MAP)
#define DUK_HTHREAD_STRING_INT_MAP(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_MAP)
#define DUK_STRIDX_INT_VARENV                                         104                            /* '\x82Varenv' */
#define DUK_HEAP_STRING_INT_VARENV(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_VARENV)
#define DUK_HTHREAD_STRING_INT_VARENV(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_VARENV)
#define DUK_STRIDX_INT_FINALIZER                                      105                            /* '\x82Finalizer' */
#define DUK_HEAP_STRING_INT_FINALIZER(heap)                           DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_FINALIZER)
#define DUK_HTHREAD_STRING_INT_FINALIZER(thr)                         DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_FINALIZER)
#define DUK_STRIDX_INT_VALUE                                          106                            /* '\x82Value' */
#define DUK_HEAP_STRING_INT_VALUE(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_VALUE)
#define DUK_HTHREAD_STRING_INT_VALUE(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_VALUE)
#define DUK_STRIDX_COMPILE                                            107                            /* 'compile' */
#define DUK_HEAP_STRING_COMPILE(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_COMPILE)
#define DUK_HTHREAD_STRING_COMPILE(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_COMPILE)
#define DUK_STRIDX_INPUT                                              108                            /* 'input' */
#define DUK_HEAP_STRING_INPUT(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INPUT)
#define DUK_HTHREAD_STRING_INPUT(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INPUT)
#define DUK_STRIDX_ERR_CREATE                                         109                            /* 'errCreate' */
#define DUK_HEAP_STRING_ERR_CREATE(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ERR_CREATE)
#define DUK_HTHREAD_STRING_ERR_CREATE(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ERR_CREATE)
#define DUK_STRIDX_ERR_THROW                                          110                            /* 'errThrow' */
#define DUK_HEAP_STRING_ERR_THROW(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ERR_THROW)
#define DUK_HTHREAD_STRING_ERR_THROW(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ERR_THROW)
#define DUK_STRIDX_ENV                                                111                            /* 'env' */
#define DUK_HEAP_STRING_ENV(heap)                                     DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ENV)
#define DUK_HTHREAD_STRING_ENV(thr)                                   DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ENV)
#define DUK_STRIDX_HEX                                                112                            /* 'hex' */
#define DUK_HEAP_STRING_HEX(heap)                                     DUK_HEAP_GET_STRING((heap),DUK_STRIDX_HEX)
#define DUK_HTHREAD_STRING_HEX(thr)                                   DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_HEX)
#define DUK_STRIDX_BASE64                                             113                            /* 'base64' */
#define DUK_HEAP_STRING_BASE64(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_BASE64)
#define DUK_HTHREAD_STRING_BASE64(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_BASE64)
#define DUK_STRIDX_JX                                                 114                            /* 'jx' */
#define DUK_HEAP_STRING_JX(heap)                                      DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JX)
#define DUK_HTHREAD_STRING_JX(thr)                                    DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JX)
#define DUK_STRIDX_JC                                                 115                            /* 'jc' */
#define DUK_HEAP_STRING_JC(heap)                                      DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JC)
#define DUK_HTHREAD_STRING_JC(thr)                                    DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JC)
#define DUK_STRIDX_JSON_EXT_UNDEFINED                                 116                            /* '{"_undef":true}' */
#define DUK_HEAP_STRING_JSON_EXT_UNDEFINED(heap)                      DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_UNDEFINED)
#define DUK_HTHREAD_STRING_JSON_EXT_UNDEFINED(thr)                    DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_UNDEFINED)
#define DUK_STRIDX_JSON_EXT_NAN                                       117                            /* '{"_nan":true}' */
#define DUK_HEAP_STRING_JSON_EXT_NAN(heap)                            DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_NAN)
#define DUK_HTHREAD_STRING_JSON_EXT_NAN(thr)                          DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_NAN)
#define DUK_STRIDX_JSON_EXT_POSINF                                    118                            /* '{"_inf":true}' */
#define DUK_HEAP_STRING_JSON_EXT_POSINF(heap)                         DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_POSINF)
#define DUK_HTHREAD_STRING_JSON_EXT_POSINF(thr)                       DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_POSINF)
#define DUK_STRIDX_JSON_EXT_NEGINF                                    119                            /* '{"_ninf":true}' */
#define DUK_HEAP_STRING_JSON_EXT_NEGINF(heap)                         DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_NEGINF)
#define DUK_HTHREAD_STRING_JSON_EXT_NEGINF(thr)                       DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_NEGINF)
#define DUK_STRIDX_JSON_EXT_FUNCTION1                                 120                            /* '{"_func":true}' */
#define DUK_HEAP_STRING_JSON_EXT_FUNCTION1(heap)                      DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_FUNCTION1)
#define DUK_HTHREAD_STRING_JSON_EXT_FUNCTION1(thr)                    DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_FUNCTION1)
#define DUK_STRIDX_JSON_EXT_FUNCTION2                                 121                            /* '{_func:true}' */
#define DUK_HEAP_STRING_JSON_EXT_FUNCTION2(heap)                      DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_FUNCTION2)
#define DUK_HTHREAD_STRING_JSON_EXT_FUNCTION2(thr)                    DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_FUNCTION2)
#define DUK_STRIDX_BREAK                                              122                            /* 'break' */
#define DUK_HEAP_STRING_BREAK(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_BREAK)
#define DUK_HTHREAD_STRING_BREAK(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_BREAK)
#define DUK_STRIDX_CASE                                               123                            /* 'case' */
#define DUK_HEAP_STRING_CASE(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CASE)
#define DUK_HTHREAD_STRING_CASE(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CASE)
#define DUK_STRIDX_CATCH                                              124                            /* 'catch' */
#define DUK_HEAP_STRING_CATCH(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CATCH)
#define DUK_HTHREAD_STRING_CATCH(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CATCH)
#define DUK_STRIDX_CONTINUE                                           125                            /* 'continue' */
#define DUK_HEAP_STRING_CONTINUE(heap)                                DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CONTINUE)
#define DUK_HTHREAD_STRING_CONTINUE(thr)                              DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CONTINUE)
#define DUK_STRIDX_DEBUGGER                                           126                            /* 'debugger' */
#define DUK_HEAP_STRING_DEBUGGER(heap)                                DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DEBUGGER)
#define DUK_HTHREAD_STRING_DEBUGGER(thr)                              DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DEBUGGER)
#define DUK_STRIDX_DEFAULT                                            127                            /* 'default' */
#define DUK_HEAP_STRING_DEFAULT(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DEFAULT)
#define DUK_HTHREAD_STRING_DEFAULT(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DEFAULT)
#define DUK_STRIDX_DELETE                                             128                            /* 'delete' */
#define DUK_HEAP_STRING_DELETE(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DELETE)
#define DUK_HTHREAD_STRING_DELETE(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DELETE)
#define DUK_STRIDX_DO                                                 129                            /* 'do' */
#define DUK_HEAP_STRING_DO(heap)                                      DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DO)
#define DUK_HTHREAD_STRING_DO(thr)                                    DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DO)
#define DUK_STRIDX_ELSE                                               130                            /* 'else' */
#define DUK_HEAP_STRING_ELSE(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ELSE)
#define DUK_HTHREAD_STRING_ELSE(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ELSE)
#define DUK_STRIDX_FINALLY                                            131                            /* 'finally' */
#define DUK_HEAP_STRING_FINALLY(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FINALLY)
#define DUK_HTHREAD_STRING_FINALLY(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FINALLY)
#define DUK_STRIDX_FOR                                                132                            /* 'for' */
#define DUK_HEAP_STRING_FOR(heap)                                     DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FOR)
#define DUK_HTHREAD_STRING_FOR(thr)                                   DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FOR)
#define DUK_STRIDX_LC_FUNCTION                                        133                            /* 'function' */
#define DUK_HEAP_STRING_LC_FUNCTION(heap)                             DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_FUNCTION)
#define DUK_HTHREAD_STRING_LC_FUNCTION(thr)                           DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_FUNCTION)
#define DUK_STRIDX_IF                                                 134                            /* 'if' */
#define DUK_HEAP_STRING_IF(heap)                                      DUK_HEAP_GET_STRING((heap),DUK_STRIDX_IF)
#define DUK_HTHREAD_STRING_IF(thr)                                    DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_IF)
#define DUK_STRIDX_IN                                                 135                            /* 'in' */
#define DUK_HEAP_STRING_IN(heap)                                      DUK_HEAP_GET_STRING((heap),DUK_STRIDX_IN)
#define DUK_HTHREAD_STRING_IN(thr)                                    DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_IN)
#define DUK_STRIDX_INSTANCEOF                                         136                            /* 'instanceof' */
#define DUK_HEAP_STRING_INSTANCEOF(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INSTANCEOF)
#define DUK_HTHREAD_STRING_INSTANCEOF(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INSTANCEOF)
#define DUK_STRIDX_NEW                                                137                            /* 'new' */
#define DUK_HEAP_STRING_NEW(heap)                                     DUK_HEAP_GET_STRING((heap),DUK_STRIDX_NEW)
#define DUK_HTHREAD_STRING_NEW(thr)                                   DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_NEW)
#define DUK_STRIDX_RETURN                                             138                            /* 'return' */
#define DUK_HEAP_STRING_RETURN(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_RETURN)
#define DUK_HTHREAD_STRING_RETURN(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_RETURN)
#define DUK_STRIDX_SWITCH                                             139                            /* 'switch' */
#define DUK_HEAP_STRING_SWITCH(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_SWITCH)
#define DUK_HTHREAD_STRING_SWITCH(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_SWITCH)
#define DUK_STRIDX_THIS                                               140                            /* 'this' */
#define DUK_HEAP_STRING_THIS(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_THIS)
#define DUK_HTHREAD_STRING_THIS(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_THIS)
#define DUK_STRIDX_THROW                                              141                            /* 'throw' */
#define DUK_HEAP_STRING_THROW(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_THROW)
#define DUK_HTHREAD_STRING_THROW(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_THROW)
#define DUK_STRIDX_TRY                                                142                            /* 'try' */
#define DUK_HEAP_STRING_TRY(heap)                                     DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TRY)
#define DUK_HTHREAD_STRING_TRY(thr)                                   DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TRY)
#define DUK_STRIDX_TYPEOF                                             143                            /* 'typeof' */
#define DUK_HEAP_STRING_TYPEOF(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TYPEOF)
#define DUK_HTHREAD_STRING_TYPEOF(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TYPEOF)
#define DUK_STRIDX_VAR                                                144                            /* 'var' */
#define DUK_HEAP_STRING_VAR(heap)                                     DUK_HEAP_GET_STRING((heap),DUK_STRIDX_VAR)
#define DUK_HTHREAD_STRING_VAR(thr)                                   DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_VAR)
#define DUK_STRIDX_CONST                                              145                            /* 'const' */
#define DUK_HEAP_STRING_CONST(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CONST)
#define DUK_HTHREAD_STRING_CONST(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CONST)
#define DUK_STRIDX_VOID                                               146                            /* 'void' */
#define DUK_HEAP_STRING_VOID(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_VOID)
#define DUK_HTHREAD_STRING_VOID(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_VOID)
#define DUK_STRIDX_WHILE                                              147                            /* 'while' */
#define DUK_HEAP_STRING_WHILE(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_WHILE)
#define DUK_HTHREAD_STRING_WHILE(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_WHILE)
#define DUK_STRIDX_WITH                                               148                            /* 'with' */
#define DUK_HEAP_STRING_WITH(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_WITH)
#define DUK_HTHREAD_STRING_WITH(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_WITH)
#define DUK_STRIDX_CLASS                                              149                            /* 'class' */
#define DUK_HEAP_STRING_CLASS(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CLASS)
#define DUK_HTHREAD_STRING_CLASS(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CLASS)
#define DUK_STRIDX_ENUM                                               150                            /* 'enum' */
#define DUK_HEAP_STRING_ENUM(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ENUM)
#define DUK_HTHREAD_STRING_ENUM(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ENUM)
#define DUK_STRIDX_EXPORT                                             151                            /* 'export' */
#define DUK_HEAP_STRING_EXPORT(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_EXPORT)
#define DUK_HTHREAD_STRING_EXPORT(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_EXPORT)
#define DUK_STRIDX_EXTENDS                                            152                            /* 'extends' */
#define DUK_HEAP_STRING_EXTENDS(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_EXTENDS)
#define DUK_HTHREAD_STRING_EXTENDS(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_EXTENDS)
#define DUK_STRIDX_IMPORT                                             153                            /* 'import' */
#define DUK_HEAP_STRING_IMPORT(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_IMPORT)
#define DUK_HTHREAD_STRING_IMPORT(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_IMPORT)
#define DUK_STRIDX_SUPER                                              154                            /* 'super' */
#define DUK_HEAP_STRING_SUPER(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_SUPER)
#define DUK_HTHREAD_STRING_SUPER(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_SUPER)
#define DUK_STRIDX_LC_NULL                                            155                            /* 'null' */
#define DUK_HEAP_STRING_LC_NULL(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_NULL)
#define DUK_HTHREAD_STRING_LC_NULL(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_NULL)
#define DUK_STRIDX_TRUE                                               156                            /* 'true' */
#define DUK_HEAP_STRING_TRUE(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TRUE)
#define DUK_HTHREAD_STRING_TRUE(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TRUE)
#define DUK_STRIDX_FALSE                                              157                            /* 'false' */
#define DUK_HEAP_STRING_FALSE(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FALSE)
#define DUK_HTHREAD_STRING_FALSE(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FALSE)
#define DUK_STRIDX_IMPLEMENTS                                         158                            /* 'implements' */
#define DUK_HEAP_STRING_IMPLEMENTS(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_IMPLEMENTS)
#define DUK_HTHREAD_STRING_IMPLEMENTS(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_IMPLEMENTS)
#define DUK_STRIDX_INTERFACE                                          159                            /* 'interface' */
#define DUK_HEAP_STRING_INTERFACE(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INTERFACE)
#define DUK_HTHREAD_STRING_INTERFACE(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INTERFACE)
#define DUK_STRIDX_LET                                                160                            /* 'let' */
#define DUK_HEAP_STRING_LET(heap)                                     DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LET)
#define DUK_HTHREAD_STRING_LET(thr)                                   DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LET)
#define DUK_STRIDX_PACKAGE                                            161                            /* 'package' */
#define DUK_HEAP_STRING_PACKAGE(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PACKAGE)
#define DUK_HTHREAD_STRING_PACKAGE(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PACKAGE)
#define DUK_STRIDX_PRIVATE                                            162                            /* 'private' */
#define DUK_HEAP_STRING_PRIVATE(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PRIVATE)
#define DUK_HTHREAD_STRING_PRIVATE(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PRIVATE)
#define DUK_STRIDX_PROTECTED                                          163                            /* 'protected' */
#define DUK_HEAP_STRING_PROTECTED(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PROTECTED)
#define DUK_HTHREAD_STRING_PROTECTED(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PROTECTED)
#define DUK_STRIDX_PUBLIC                                             164                            /* 'public' */
#define DUK_HEAP_STRING_PUBLIC(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PUBLIC)
#define DUK_HTHREAD_STRING_PUBLIC(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PUBLIC)
#define DUK_STRIDX_STATIC                                             165                            /* 'static' */
#define DUK_HEAP_STRING_STATIC(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_STATIC)
#define DUK_HTHREAD_STRING_STATIC(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_STATIC)
#define DUK_STRIDX_YIELD                                              166                            /* 'yield' */
#define DUK_HEAP_STRING_YIELD(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_YIELD)
#define DUK_HTHREAD_STRING_YIELD(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_YIELD)

#define DUK_HEAP_NUM_STRINGS                                          167
#define DUK_STRIDX_START_RESERVED                                     122
#define DUK_STRIDX_START_STRICT_RESERVED                              158
#define DUK_STRIDX_END_RESERVED                                       167                            /* exclusive endpoint */

/* To convert a heap stridx to a token number, subtract
 * DUK_STRIDX_START_RESERVED and add DUK_TOK_START_RESERVED.
 */
#if !defined(DUK_SINGLE_FILE)
DUK_INTERNAL_DECL const duk_uint8_t duk_strings_data[972];
#endif  /* !DUK_SINGLE_FILE */
#define DUK_STRDATA_MAX_STRLEN                                        27
#define DUK_STRDATA_DATA_LENGTH                                       972
#endif  /* DUK_USE_ROM_STRINGS */

#if defined(DUK_USE_ROM_OBJECTS)
#error RAM support not enabled, rerun configure.py with --ram-support
#else  /* DUK_USE_ROM_OBJECTS */
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_function_constructor(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_array_constructor(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_string_constructor(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_boolean_constructor(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_number_constructor(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_date_constructor(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_regexp_constructor(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_error_constructor_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_type_error_thrower(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_parse_int(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_parse_float(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_thread_constructor(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_pointer_constructor(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_proxy_constructor(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_symbol_constructor_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_arraybuffer_constructor(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_dataview_constructor(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_constructor(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_textencoder_constructor(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_textdecoder_constructor(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_eval(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_is_nan(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_is_finite(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_decode_uri(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_decode_uri_component(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_encode_uri(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_encode_uri_component(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_escape(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_unescape(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_getprototype_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_setprototype_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_get_own_property_descriptor(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_keys_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_assign(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_create(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_define_property(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_define_properties(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_seal_freeze_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_prevent_extensions(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_is_sealed_frozen_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_is_extensible(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_is(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_to_string(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_to_locale_string(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_value_of(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_has_own_property(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_is_prototype_of(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_property_is_enumerable(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_defineaccessor(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_lookupaccessor(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype_to_string(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype_apply(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype_call(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype_bind(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype_hasinstance(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_native_function_length(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_native_function_name(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_array_constructor_is_array(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_to_string(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_join_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_concat(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_pop(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_push(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_reverse(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_shift(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_slice(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_sort(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_splice(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_unshift(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_indexof_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_iter_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_reduce_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_string_constructor_from_char_code(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_string_constructor_from_code_point(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_to_string(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_char_at(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_char_code_at(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_concat(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_indexof_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_locale_compare(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_match(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_replace(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_search(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_slice(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_split(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_substring(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_caseconv_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_trim(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_repeat(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_startswith_endswith(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_includes(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_substr(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_boolean_prototype_tostring_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_number_check_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_number_prototype_to_string(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_number_prototype_to_locale_string(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_number_prototype_value_of(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_number_prototype_to_fixed(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_number_prototype_to_exponential(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_number_prototype_to_precision(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_date_constructor_parse(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_date_constructor_utc(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_date_constructor_now(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_date_prototype_tostring_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_date_prototype_to_json(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_date_prototype_value_of(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_date_prototype_get_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_date_prototype_get_timezone_offset(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_date_prototype_set_time(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_date_prototype_set_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_date_prototype_toprimitive(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_regexp_prototype_exec(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_regexp_prototype_test(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_regexp_prototype_tostring(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_regexp_prototype_flags(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_regexp_prototype_shared_getter(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_error_prototype_stack_getter(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_error_prototype_stack_setter(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_error_prototype_filename_getter(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_error_prototype_filename_setter(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_error_prototype_linenumber_getter(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_error_prototype_linenumber_setter(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_error_prototype_to_string(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_onearg_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_twoarg_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_clz32(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_hypot(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_imul(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_max(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_min(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_random(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_sign(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_json_object_parse(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_json_object_stringify(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_duktape_object_info(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_duktape_object_act(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_duktape_object_gc(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_duktape_object_fin(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_duktape_object_enc(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_duktape_object_dec(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_duktape_object_compact(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_thread_yield(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_thread_resume(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_thread_current(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_pointer_prototype_tostring_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_apply(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_construct(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_object_delete_property(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_object_get(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_object_has(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_object_set(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_symbol_key_for(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_symbol_tostring_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_symbol_toprimitive(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_arraybuffer_isview(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_typedarray_bytelength_getter(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_buffer_slice_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_typedarray_byteoffset_getter(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_typedarray_buffer_getter(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_typedarray_set(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_uint8array_allocplain(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_uint8array_plainof(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_concat(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_is_encoding(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_is_buffer(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_byte_length(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_buffer_compare_shared(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_tostring(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_tojson(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_fill(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_copy(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_write(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_cbor_encode(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_cbor_decode(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_textencoder_prototype_encoding_getter(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_textencoder_prototype_encode(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_textdecoder_prototype_shared_getter(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_textdecoder_prototype_decode(duk_context *ctx);
DUK_INTERNAL_DECL duk_ret_t duk_bi_performance_now(duk_context *ctx);
#if !defined(DUK_SINGLE_FILE)
DUK_INTERNAL_DECL const duk_c_function duk_bi_native_functions[185];
#endif  /* !DUK_SINGLE_FILE */
#define DUK_BIDX_GLOBAL                                               0
#define DUK_BIDX_GLOBAL_ENV                                           1
#define DUK_BIDX_OBJECT_CONSTRUCTOR                                   2
#define DUK_BIDX_OBJECT_PROTOTYPE                                     3
#define DUK_BIDX_FUNCTION_CONSTRUCTOR                                 4
#define DUK_BIDX_FUNCTION_PROTOTYPE                                   5
#define DUK_BIDX_NATIVE_FUNCTION_PROTOTYPE                            6
#define DUK_BIDX_ARRAY_CONSTRUCTOR                                    7
#define DUK_BIDX_ARRAY_PROTOTYPE                                      8
#define DUK_BIDX_STRING_CONSTRUCTOR                                   9
#define DUK_BIDX_STRING_PROTOTYPE                                     10
#define DUK_BIDX_BOOLEAN_CONSTRUCTOR                                  11
#define DUK_BIDX_BOOLEAN_PROTOTYPE                                    12
#define DUK_BIDX_NUMBER_CONSTRUCTOR                                   13
#define DUK_BIDX_NUMBER_PROTOTYPE                                     14
#define DUK_BIDX_DATE_CONSTRUCTOR                                     15
#define DUK_BIDX_DATE_PROTOTYPE                                       16
#define DUK_BIDX_REGEXP_CONSTRUCTOR                                   17
#define DUK_BIDX_REGEXP_PROTOTYPE                                     18
#define DUK_BIDX_ERROR_CONSTRUCTOR                                    19
#define DUK_BIDX_ERROR_PROTOTYPE                                      20
#define DUK_BIDX_EVAL_ERROR_CONSTRUCTOR                               21
#define DUK_BIDX_EVAL_ERROR_PROTOTYPE                                 22
#define DUK_BIDX_RANGE_ERROR_CONSTRUCTOR                              23
#define DUK_BIDX_RANGE_ERROR_PROTOTYPE                                24
#define DUK_BIDX_REFERENCE_ERROR_CONSTRUCTOR                          25
#define DUK_BIDX_REFERENCE_ERROR_PROTOTYPE                            26
#define DUK_BIDX_SYNTAX_ERROR_CONSTRUCTOR                             27
#define DUK_BIDX_SYNTAX_ERROR_PROTOTYPE                               28
#define DUK_BIDX_TYPE_ERROR_CONSTRUCTOR                               29
#define DUK_BIDX_TYPE_ERROR_PROTOTYPE                                 30
#define DUK_BIDX_URI_ERROR_CONSTRUCTOR                                31
#define DUK_BIDX_URI_ERROR_PROTOTYPE                                  32
#define DUK_BIDX_TYPE_ERROR_THROWER                                   33
#define DUK_BIDX_DUKTAPE                                              34
#define DUK_BIDX_THREAD_PROTOTYPE                                     35
#define DUK_BIDX_POINTER_PROTOTYPE                                    36
#define DUK_BIDX_DOUBLE_ERROR                                         37
#define DUK_BIDX_SYMBOL_PROTOTYPE                                     38
#define DUK_BIDX_ARRAYBUFFER_PROTOTYPE                                39
#define DUK_BIDX_DATAVIEW_PROTOTYPE                                   40
#define DUK_BIDX_INT8ARRAY_PROTOTYPE                                  41
#define DUK_BIDX_UINT8ARRAY_PROTOTYPE                                 42
#define DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE                          43
#define DUK_BIDX_INT16ARRAY_PROTOTYPE                                 44
#define DUK_BIDX_UINT16ARRAY_PROTOTYPE                                45
#define DUK_BIDX_INT32ARRAY_PROTOTYPE                                 46
#define DUK_BIDX_UINT32ARRAY_PROTOTYPE                                47
#define DUK_BIDX_FLOAT32ARRAY_PROTOTYPE                               48
#define DUK_BIDX_FLOAT64ARRAY_PROTOTYPE                               49
#define DUK_BIDX_NODEJS_BUFFER_PROTOTYPE                              50
#define DUK_NUM_BUILTINS                                              51
#define DUK_NUM_BIDX_BUILTINS                                         51
#define DUK_NUM_ALL_BUILTINS                                          80
#if defined(DUK_USE_DOUBLE_LE)
#if !defined(DUK_SINGLE_FILE)
DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[4281];
#endif  /* !DUK_SINGLE_FILE */
#define DUK_BUILTINS_DATA_LENGTH                                      4281
#elif defined(DUK_USE_DOUBLE_BE)
#if !defined(DUK_SINGLE_FILE)
DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[4281];
#endif  /* !DUK_SINGLE_FILE */
#define DUK_BUILTINS_DATA_LENGTH                                      4281
#elif defined(DUK_USE_DOUBLE_ME)
#if !defined(DUK_SINGLE_FILE)
DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[4281];
#endif  /* !DUK_SINGLE_FILE */
#define DUK_BUILTINS_DATA_LENGTH                                      4281
#else
#error invalid endianness defines
#endif
#endif  /* DUK_USE_ROM_OBJECTS */
#endif  /* DUK_BUILTINS_H_INCLUDED */
//#line 45 "duk_internal.h"

/* #include duk_util.h */
//#line 1 "duk_util.h"
/*
 *  Utilities
 */

#if !defined(DUK_UTIL_H_INCLUDED)
#define DUK_UTIL_H_INCLUDED

#if defined(DUK_USE_GET_RANDOM_DOUBLE)
#define DUK_UTIL_GET_RANDOM_DOUBLE(thr) DUK_USE_GET_RANDOM_DOUBLE((thr)->heap_udata)
#else
#define DUK_UTIL_GET_RANDOM_DOUBLE(thr) duk_util_tinyrandom_get_double(thr)
#endif

/*
 *  Some useful constants
 */

#define DUK_DOUBLE_2TO32     4294967296.0
#define DUK_DOUBLE_2TO31     2147483648.0
#define DUK_DOUBLE_LOG2E     1.4426950408889634
#define DUK_DOUBLE_LOG10E    0.4342944819032518

/*
 *  Endian conversion
 */

#if defined(DUK_USE_INTEGER_LE)
#define DUK_HTON32(x) DUK_BSWAP32((x))
#define DUK_NTOH32(x) DUK_BSWAP32((x))
#define DUK_HTON16(x) DUK_BSWAP16((x))
#define DUK_NTOH16(x) DUK_BSWAP16((x))
#elif defined(DUK_USE_INTEGER_BE)
#define DUK_HTON32(x) (x)
#define DUK_NTOH32(x) (x)
#define DUK_HTON16(x) (x)
#define DUK_NTOH16(x) (x)
#else
#error internal error, endianness defines broken
#endif

/*
 *  Bitstream decoder
 */

struct duk_bitdecoder_ctx {
    const duk_uint8_t *data;
    duk_size_t offset;
    duk_size_t length;
    duk_uint32_t currval;
    duk_small_int_t currbits;
};

#define DUK_BD_BITPACKED_STRING_MAXLEN 256

/*
 *  Bitstream encoder
 */

struct duk_bitencoder_ctx {
    duk_uint8_t *data;
    duk_size_t offset;
    duk_size_t length;
    duk_uint32_t currval;
    duk_small_int_t currbits;
    duk_small_int_t truncated;
};

/*
 *  Raw write/read macros for big endian, unaligned basic values.
 *  Caller ensures there's enough space.  The INC macro variants
 *  update the pointer argument automatically.
 */

#define DUK_RAW_WRITE_U8(ptr,val)  do { \
        *(ptr) = (duk_uint8_t) (val); \
    } while (0)
#define DUK_RAW_WRITE_U16_BE(ptr,val) duk_raw_write_u16_be((ptr), (duk_uint16_t) (val))
#define DUK_RAW_WRITE_U32_BE(ptr,val) duk_raw_write_u32_be((ptr), (duk_uint32_t) (val))
#define DUK_RAW_WRITE_FLOAT_BE(ptr,val) duk_raw_write_float_be((ptr), (duk_float_t) (val))
#define DUK_RAW_WRITE_DOUBLE_BE(ptr,val) duk_raw_write_double_be((ptr), (duk_double_t) (val))
#define DUK_RAW_WRITE_XUTF8(ptr,val) duk_raw_write_xutf8((ptr), (duk_ucodepoint_t) (val))

#define DUK_RAW_WRITEINC_U8(ptr,val)  do { \
        *(ptr)++ = (duk_uint8_t) (val); \
    } while (0)
#define DUK_RAW_WRITEINC_U16_BE(ptr,val) duk_raw_writeinc_u16_be(&(ptr), (duk_uint16_t) (val))
#define DUK_RAW_WRITEINC_U32_BE(ptr,val) duk_raw_writeinc_u32_be(&(ptr), (duk_uint32_t) (val))
#define DUK_RAW_WRITEINC_FLOAT_BE(ptr,val) duk_raw_writeinc_float_be(&(ptr), (duk_float_t) (val))
#define DUK_RAW_WRITEINC_DOUBLE_BE(ptr,val) duk_raw_writeinc_double_be(&(ptr), (duk_double_t) (val))
#define DUK_RAW_WRITEINC_XUTF8(ptr,val) duk_raw_writeinc_xutf8(&(ptr), (duk_ucodepoint_t) (val))
#define DUK_RAW_WRITEINC_CESU8(ptr,val) duk_raw_writeinc_cesu8(&(ptr), (duk_ucodepoint_t) (val))

#define DUK_RAW_READ_U8(ptr) ((duk_uint8_t) (*(ptr)))
#define DUK_RAW_READ_U16_BE(ptr) duk_raw_read_u16_be((ptr));
#define DUK_RAW_READ_U32_BE(ptr) duk_raw_read_u32_be((ptr));
#define DUK_RAW_READ_DOUBLE_BE(ptr) duk_raw_read_double_be((ptr));

#define DUK_RAW_READINC_U8(ptr) ((duk_uint8_t) (*(ptr)++))
#define DUK_RAW_READINC_U16_BE(ptr) duk_raw_readinc_u16_be(&(ptr));
#define DUK_RAW_READINC_U32_BE(ptr) duk_raw_readinc_u32_be(&(ptr));
#define DUK_RAW_READINC_DOUBLE_BE(ptr) duk_raw_readinc_double_be(&(ptr));

/*
 *  Double and float byte order operations.
 */

DUK_INTERNAL_DECL void duk_dblunion_host_to_little(duk_double_union *u);
DUK_INTERNAL_DECL void duk_dblunion_little_to_host(duk_double_union *u);
DUK_INTERNAL_DECL void duk_dblunion_host_to_big(duk_double_union *u);
DUK_INTERNAL_DECL void duk_dblunion_big_to_host(duk_double_union *u);
DUK_INTERNAL_DECL void duk_fltunion_host_to_big(duk_float_union *u);
DUK_INTERNAL_DECL void duk_fltunion_big_to_host(duk_float_union *u);

/*
 *  Buffer writer (dynamic buffer only)
 *
 *  Helper for writing to a dynamic buffer with a concept of a "slack" area
 *  to reduce resizes.  You can ensure there is enough space beforehand and
 *  then write for a while without further checks, relying on a stable data
 *  pointer.  Slack handling is automatic so call sites only indicate how
 *  much data they need right now.
 *
 *  There are several ways to write using bufwriter.  The best approach
 *  depends mainly on how much performance matters over code footprint.
 *  The key issues are (1) ensuring there is space and (2) keeping the
 *  pointers consistent.  Fast code should ensure space for multiple writes
 *  with one ensure call.  Fastest inner loop code can temporarily borrow
 *  the 'p' pointer but must write it back eventually.
 *
 *  Be careful to ensure all macro arguments (other than static pointers like
 *  'thr' and 'bw_ctx') are evaluated exactly once, using temporaries if
 *  necessary (if that's not possible, there should be a note near the macro).
 *  Buffer write arguments often contain arithmetic etc so this is
 *  particularly important here.
 */

/* XXX: Migrate bufwriter and other read/write helpers to its own header? */

struct duk_bufwriter_ctx {
    duk_uint8_t *p;
    duk_uint8_t *p_base;
    duk_uint8_t *p_limit;
    duk_hbuffer_dynamic *buf;
};

#if defined(DUK_USE_PREFER_SIZE)
#define DUK_BW_SLACK_ADD           64
#define DUK_BW_SLACK_SHIFT         4    /* 2^4 -> 1/16 = 6.25% slack */
#else
#define DUK_BW_SLACK_ADD           64
#define DUK_BW_SLACK_SHIFT         2    /* 2^2 -> 1/4 = 25% slack */
#endif

/* Initialization and finalization (compaction), converting to other types. */

#define DUK_BW_INIT_PUSHBUF(thr,bw_ctx,sz) do { \
        duk_bw_init_pushbuf((thr), (bw_ctx), (sz)); \
    } while (0)
#define DUK_BW_INIT_WITHBUF(thr,bw_ctx,buf) do { \
        duk_bw_init((thr), (bw_ctx), (buf)); \
    } while (0)
#define DUK_BW_COMPACT(thr,bw_ctx) do { \
        /* Make underlying buffer compact to match DUK_BW_GET_SIZE(). */ \
        duk_bw_compact((thr), (bw_ctx)); \
    } while (0)
#define DUK_BW_PUSH_AS_STRING(thr,bw_ctx) do { \
        duk_push_lstring((thr), \
                         (const char *) (bw_ctx)->p_base, \
                         (duk_size_t) ((bw_ctx)->p - (bw_ctx)->p_base)); \
    } while (0)

/* Pointers may be NULL for a while when 'buf' size is zero and before any
 * ENSURE calls have been made.  Once an ENSURE has been made, the pointers
 * are required to be non-NULL so that it's always valid to use memcpy() and
 * memmove(), even for zero size.
 */
#if defined(DUK_USE_ASSERTIONS)
DUK_INTERNAL_DECL void duk_bw_assert_valid(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx);
#define DUK_BW_ASSERT_VALID_EXPR(thr,bw_ctx)  (duk_bw_assert_valid((thr), (bw_ctx)))
#define DUK_BW_ASSERT_VALID(thr,bw_ctx)  do { duk_bw_assert_valid((thr), (bw_ctx)); } while (0)
#else
#define DUK_BW_ASSERT_VALID_EXPR(thr,bw_ctx)  DUK_ASSERT_EXPR(1)
#define DUK_BW_ASSERT_VALID(thr,bw_ctx)  do {} while (0)
#endif

/* Working with the pointer and current size. */

#define DUK_BW_GET_PTR(thr,bw_ctx) \
    ((bw_ctx)->p)
#define DUK_BW_SET_PTR(thr,bw_ctx,ptr) do { \
        (bw_ctx)->p = (ptr); \
    } while (0)
#define DUK_BW_ADD_PTR(thr,bw_ctx,delta) do { \
        (bw_ctx)->p += (delta); \
    } while (0)
#define DUK_BW_GET_BASEPTR(thr,bw_ctx) \
    ((bw_ctx)->p_base)
#define DUK_BW_GET_LIMITPTR(thr,bw_ctx) \
    ((bw_ctx)->p_limit)
#define DUK_BW_GET_SIZE(thr,bw_ctx) \
    ((duk_size_t) ((bw_ctx)->p - (bw_ctx)->p_base))
#define DUK_BW_SET_SIZE(thr,bw_ctx,sz) do { \
        DUK_ASSERT((duk_size_t) (sz) <= (duk_size_t) ((bw_ctx)->p - (bw_ctx)->p_base)); \
        (bw_ctx)->p = (bw_ctx)->p_base + (sz); \
    } while (0)
#define DUK_BW_RESET_SIZE(thr,bw_ctx) do { \
        /* Reset to zero size, keep current limit. */ \
        (bw_ctx)->p = (bw_ctx)->p_base; \
    } while (0)
#define DUK_BW_GET_BUFFER(thr,bw_ctx) \
    ((bw_ctx)->buf)

/* Ensuring (reserving) space. */

#define DUK_BW_ENSURE(thr,bw_ctx,sz) do { \
        duk_size_t duk__sz, duk__space; \
        DUK_BW_ASSERT_VALID((thr), (bw_ctx)); \
        duk__sz = (sz); \
        duk__space = (duk_size_t) ((bw_ctx)->p_limit - (bw_ctx)->p); \
        if (duk__space < duk__sz) { \
            (void) duk_bw_resize((thr), (bw_ctx), duk__sz); \
        } \
    } while (0)
/* NOTE: Multiple evaluation of 'ptr' in this macro. */
/* XXX: Rework to use an always-inline function? */
#define DUK_BW_ENSURE_RAW(thr,bw_ctx,sz,ptr) \
    (((duk_size_t) ((bw_ctx)->p_limit - (ptr)) >= (sz)) ? \
     (ptr) : \
     ((bw_ctx)->p = (ptr), duk_bw_resize((thr),(bw_ctx),(sz))))
#define DUK_BW_ENSURE_GETPTR(thr,bw_ctx,sz) \
    DUK_BW_ENSURE_RAW((thr), (bw_ctx), (sz), (bw_ctx)->p)
#define DUK_BW_ASSERT_SPACE_EXPR(thr,bw_ctx,sz) \
    (DUK_BW_ASSERT_VALID_EXPR((thr), (bw_ctx)), \
     DUK_ASSERT_EXPR((duk_size_t) ((bw_ctx)->p_limit - (bw_ctx)->p) >= (duk_size_t) (sz)))
#define DUK_BW_ASSERT_SPACE(thr,bw_ctx,sz) do { \
        DUK_BW_ASSERT_SPACE_EXPR((thr), (bw_ctx), (sz)); \
    } while (0)

/* Miscellaneous. */

#define DUK_BW_SETPTR_AND_COMPACT(thr,bw_ctx,ptr) do { \
        (bw_ctx)->p = (ptr); \
        duk_bw_compact((thr), (bw_ctx)); \
    } while (0)

/* Fast write calls which assume you control the slack beforehand.
 * Multibyte write variants exist and use a temporary write pointer
 * because byte writes alias with anything: with a stored pointer
 * explicit pointer load/stores get generated (e.g. gcc -Os).
 */

#define DUK_BW_WRITE_RAW_U8(thr,bw_ctx,val) do { \
        DUK_BW_ASSERT_SPACE((thr), (bw_ctx), 1); \
        *(bw_ctx)->p++ = (duk_uint8_t) (val); \
    } while (0)
#define DUK_BW_WRITE_RAW_U8_2(thr,bw_ctx,val1,val2) do { \
        duk_uint8_t *duk__p; \
        DUK_BW_ASSERT_SPACE((thr), (bw_ctx), 2); \
        duk__p = (bw_ctx)->p; \
        *duk__p++ = (duk_uint8_t) (val1); \
        *duk__p++ = (duk_uint8_t) (val2); \
        (bw_ctx)->p = duk__p; \
    } while (0)
#define DUK_BW_WRITE_RAW_U8_3(thr,bw_ctx,val1,val2,val3) do { \
        duk_uint8_t *duk__p; \
        DUK_BW_ASSERT_SPACE((thr), (bw_ctx), 3); \
        duk__p = (bw_ctx)->p; \
        *duk__p++ = (duk_uint8_t) (val1); \
        *duk__p++ = (duk_uint8_t) (val2); \
        *duk__p++ = (duk_uint8_t) (val3); \
        (bw_ctx)->p = duk__p; \
    } while (0)
#define DUK_BW_WRITE_RAW_U8_4(thr,bw_ctx,val1,val2,val3,val4) do { \
        duk_uint8_t *duk__p; \
        DUK_BW_ASSERT_SPACE((thr), (bw_ctx), 4); \
        duk__p = (bw_ctx)->p; \
        *duk__p++ = (duk_uint8_t) (val1); \
        *duk__p++ = (duk_uint8_t) (val2); \
        *duk__p++ = (duk_uint8_t) (val3); \
        *duk__p++ = (duk_uint8_t) (val4); \
        (bw_ctx)->p = duk__p; \
    } while (0)
#define DUK_BW_WRITE_RAW_U8_5(thr,bw_ctx,val1,val2,val3,val4,val5) do { \
        duk_uint8_t *duk__p; \
        DUK_BW_ASSERT_SPACE((thr), (bw_ctx), 5); \
        duk__p = (bw_ctx)->p; \
        *duk__p++ = (duk_uint8_t) (val1); \
        *duk__p++ = (duk_uint8_t) (val2); \
        *duk__p++ = (duk_uint8_t) (val3); \
        *duk__p++ = (duk_uint8_t) (val4); \
        *duk__p++ = (duk_uint8_t) (val5); \
        (bw_ctx)->p = duk__p; \
    } while (0)
#define DUK_BW_WRITE_RAW_U8_6(thr,bw_ctx,val1,val2,val3,val4,val5,val6) do { \
        duk_uint8_t *duk__p; \
        DUK_BW_ASSERT_SPACE((thr), (bw_ctx), 6); \
        duk__p = (bw_ctx)->p; \
        *duk__p++ = (duk_uint8_t) (val1); \
        *duk__p++ = (duk_uint8_t) (val2); \
        *duk__p++ = (duk_uint8_t) (val3); \
        *duk__p++ = (duk_uint8_t) (val4); \
        *duk__p++ = (duk_uint8_t) (val5); \
        *duk__p++ = (duk_uint8_t) (val6); \
        (bw_ctx)->p = duk__p; \
    } while (0)
#define DUK_BW_WRITE_RAW_XUTF8(thr,bw_ctx,cp) do { \
        duk_ucodepoint_t duk__cp; \
        duk_small_int_t duk__enc_len; \
        duk__cp = (duk_ucodepoint_t) (cp); \
        DUK_BW_ASSERT_SPACE((thr), (bw_ctx), duk_unicode_get_xutf8_length(duk__cp)); \
        duk__enc_len = duk_unicode_encode_xutf8(duk__cp, (bw_ctx)->p); \
        (bw_ctx)->p += duk__enc_len; \
    } while (0)
#define DUK_BW_WRITE_RAW_CESU8(thr,bw_ctx,cp) do { \
        duk_ucodepoint_t duk__cp; \
        duk_small_int_t duk__enc_len; \
        duk__cp = (duk_ucodepoint_t) (cp); \
        DUK_BW_ASSERT_SPACE((thr), (bw_ctx), duk_unicode_get_cesu8_length(duk__cp)); \
        duk__enc_len = duk_unicode_encode_cesu8(duk__cp, (bw_ctx)->p); \
        (bw_ctx)->p += duk__enc_len; \
    } while (0)
/* XXX: add temporary duk__p pointer here too; sharing */
/* XXX: avoid unsafe variants */
#define DUK_BW_WRITE_RAW_BYTES(thr,bw_ctx,valptr,valsz) do { \
        const void *duk__valptr; \
        duk_size_t duk__valsz; \
        duk__valptr = (const void *) (valptr); \
        duk__valsz = (duk_size_t) (valsz); \
        duk_memcpy_unsafe((void *) ((bw_ctx)->p), duk__valptr, duk__valsz); \
        (bw_ctx)->p += duk__valsz; \
    } while (0)
#define DUK_BW_WRITE_RAW_CSTRING(thr,bw_ctx,val) do { \
        const duk_uint8_t *duk__val; \
        duk_size_t duk__val_len; \
        duk__val = (const duk_uint8_t *) (val); \
        duk__val_len = DUK_STRLEN((const char *) duk__val); \
        duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) duk__val, duk__val_len); \
        (bw_ctx)->p += duk__val_len; \
    } while (0)
#define DUK_BW_WRITE_RAW_HSTRING(thr,bw_ctx,val) do { \
        duk_size_t duk__val_len; \
        duk__val_len = DUK_HSTRING_GET_BYTELEN((val)); \
        duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HSTRING_GET_DATA((val)), duk__val_len); \
        (bw_ctx)->p += duk__val_len; \
    } while (0)
#define DUK_BW_WRITE_RAW_HBUFFER(thr,bw_ctx,val) do { \
        duk_size_t duk__val_len; \
        duk__val_len = DUK_HBUFFER_GET_SIZE((val)); \
        duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \
        (bw_ctx)->p += duk__val_len; \
    } while (0)
#define DUK_BW_WRITE_RAW_HBUFFER_FIXED(thr,bw_ctx,val) do { \
        duk_size_t duk__val_len; \
        duk__val_len = DUK_HBUFFER_FIXED_GET_SIZE((val)); \
        duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_FIXED_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \
        (bw_ctx)->p += duk__val_len; \
    } while (0)
#define DUK_BW_WRITE_RAW_HBUFFER_DYNAMIC(thr,bw_ctx,val) do { \
        duk_size_t duk__val_len; \
        duk__val_len = DUK_HBUFFER_DYNAMIC_GET_SIZE((val)); \
        duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \
        (bw_ctx)->p += duk__val_len; \
    } while (0)

/* Append bytes from a slice already in the buffer. */
#define DUK_BW_WRITE_RAW_SLICE(thr,bw,dst_off,dst_len) \
    duk_bw_write_raw_slice((thr), (bw), (dst_off), (dst_len))

/* Insert bytes in the middle of the buffer from an external buffer. */
#define DUK_BW_INSERT_RAW_BYTES(thr,bw,dst_off,buf,len) \
    duk_bw_insert_raw_bytes((thr), (bw), (dst_off), (buf), (len))

/* Insert bytes in the middle of the buffer from a slice already
 * in the buffer.  Source offset is interpreted "before" the operation.
 */
#define DUK_BW_INSERT_RAW_SLICE(thr,bw,dst_off,src_off,len) \
    duk_bw_insert_raw_slice((thr), (bw), (dst_off), (src_off), (len))

/* Insert a reserved area somewhere in the buffer; caller fills it.
 * Evaluates to a (duk_uint_t *) pointing to the start of the reserved
 * area for convenience.
 */
#define DUK_BW_INSERT_RAW_AREA(thr,bw,off,len) \
    duk_bw_insert_raw_area((thr), (bw), (off), (len))

/* Remove a slice from inside buffer. */
#define DUK_BW_REMOVE_RAW_SLICE(thr,bw,off,len) \
    duk_bw_remove_raw_slice((thr), (bw), (off), (len))

/* Safe write calls which will ensure space first. */

#define DUK_BW_WRITE_ENSURE_U8(thr,bw_ctx,val) do { \
        DUK_BW_ENSURE((thr), (bw_ctx), 1); \
        DUK_BW_WRITE_RAW_U8((thr), (bw_ctx), (val)); \
    } while (0)
#define DUK_BW_WRITE_ENSURE_U8_2(thr,bw_ctx,val1,val2) do { \
        DUK_BW_ENSURE((thr), (bw_ctx), 2); \
        DUK_BW_WRITE_RAW_U8_2((thr), (bw_ctx), (val1), (val2)); \
    } while (0)
#define DUK_BW_WRITE_ENSURE_U8_3(thr,bw_ctx,val1,val2,val3) do { \
        DUK_BW_ENSURE((thr), (bw_ctx), 3); \
        DUK_BW_WRITE_RAW_U8_3((thr), (bw_ctx), (val1), (val2), (val3)); \
    } while (0)
#define DUK_BW_WRITE_ENSURE_U8_4(thr,bw_ctx,val1,val2,val3,val4) do { \
        DUK_BW_ENSURE((thr), (bw_ctx), 4); \
        DUK_BW_WRITE_RAW_U8_4((thr), (bw_ctx), (val1), (val2), (val3), (val4)); \
    } while (0)
#define DUK_BW_WRITE_ENSURE_U8_5(thr,bw_ctx,val1,val2,val3,val4,val5) do { \
        DUK_BW_ENSURE((thr), (bw_ctx), 5); \
        DUK_BW_WRITE_RAW_U8_5((thr), (bw_ctx), (val1), (val2), (val3), (val4), (val5)); \
    } while (0)
#define DUK_BW_WRITE_ENSURE_U8_6(thr,bw_ctx,val1,val2,val3,val4,val5,val6) do { \
        DUK_BW_ENSURE((thr), (bw_ctx), 6); \
        DUK_BW_WRITE_RAW_U8_6((thr), (bw_ctx), (val1), (val2), (val3), (val4), (val5), (val6)); \
    } while (0)
#define DUK_BW_WRITE_ENSURE_XUTF8(thr,bw_ctx,cp) do { \
        DUK_BW_ENSURE((thr), (bw_ctx), DUK_UNICODE_MAX_XUTF8_LENGTH); \
        DUK_BW_WRITE_RAW_XUTF8((thr), (bw_ctx), (cp)); \
    } while (0)
#define DUK_BW_WRITE_ENSURE_CESU8(thr,bw_ctx,cp) do { \
        DUK_BW_ENSURE((thr), (bw_ctx), DUK_UNICODE_MAX_CESU8_LENGTH); \
        DUK_BW_WRITE_RAW_CESU8((thr), (bw_ctx), (cp)); \
    } while (0)
/* XXX: add temporary duk__p pointer here too; sharing */
/* XXX: avoid unsafe */
#define DUK_BW_WRITE_ENSURE_BYTES(thr,bw_ctx,valptr,valsz) do { \
        const void *duk__valptr; \
        duk_size_t duk__valsz; \
        duk__valptr = (const void *) (valptr); \
        duk__valsz = (duk_size_t) (valsz); \
        DUK_BW_ENSURE((thr), (bw_ctx), duk__valsz); \
        duk_memcpy_unsafe((void *) ((bw_ctx)->p), duk__valptr, duk__valsz); \
        (bw_ctx)->p += duk__valsz; \
    } while (0)
#define DUK_BW_WRITE_ENSURE_CSTRING(thr,bw_ctx,val) do { \
        const duk_uint8_t *duk__val; \
        duk_size_t duk__val_len; \
        duk__val = (const duk_uint8_t *) (val); \
        duk__val_len = DUK_STRLEN((const char *) duk__val); \
        DUK_BW_ENSURE((thr), (bw_ctx), duk__val_len); \
        duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) duk__val, duk__val_len); \
        (bw_ctx)->p += duk__val_len; \
    } while (0)
#define DUK_BW_WRITE_ENSURE_HSTRING(thr,bw_ctx,val) do { \
        duk_size_t duk__val_len; \
        duk__val_len = DUK_HSTRING_GET_BYTELEN((val)); \
        DUK_BW_ENSURE((thr), (bw_ctx), duk__val_len); \
        duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HSTRING_GET_DATA((val)), duk__val_len); \
        (bw_ctx)->p += duk__val_len; \
    } while (0)
#define DUK_BW_WRITE_ENSURE_HBUFFER(thr,bw_ctx,val) do { \
        duk_size_t duk__val_len; \
        duk__val_len = DUK_HBUFFER_GET_SIZE((val)); \
        DUK_BW_ENSURE((thr), (bw_ctx), duk__val_len); \
        duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \
        (bw_ctx)->p += duk__val_len; \
    } while (0)
#define DUK_BW_WRITE_ENSURE_HBUFFER_FIXED(thr,bw_ctx,val) do { \
        duk_size_t duk__val_len; \
        duk__val_len = DUK_HBUFFER_FIXED_GET_SIZE((val)); \
        DUK_BW_ENSURE((thr), (bw_ctx), duk__val_len); \
        duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_FIXED_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \
        (bw_ctx)->p += duk__val_len; \
    } while (0)
#define DUK_BW_WRITE_ENSURE_HBUFFER_DYNAMIC(thr,bw_ctx,val) do { \
        duk_size_t duk__val_len; \
        duk__val_len = DUK_HBUFFER_DYNAMIC_GET_SIZE((val)); \
        DUK_BW_ENSURE((thr), (bw_ctx), duk__val_len); \
        duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \
        (bw_ctx)->p += duk__val_len; \
    } while (0)

#define DUK_BW_WRITE_ENSURE_SLICE(thr,bw,dst_off,dst_len) \
    duk_bw_write_ensure_slice((thr), (bw), (dst_off), (dst_len))
#define DUK_BW_INSERT_ENSURE_BYTES(thr,bw,dst_off,buf,len) \
    duk_bw_insert_ensure_bytes((thr), (bw), (dst_off), (buf), (len))
#define DUK_BW_INSERT_ENSURE_SLICE(thr,bw,dst_off,src_off,len) \
    duk_bw_insert_ensure_slice((thr), (bw), (dst_off), (src_off), (len))
#define DUK_BW_INSERT_ENSURE_AREA(thr,bw,off,len) \
    /* Evaluates to (duk_uint8_t *) pointing to start of area. */ \
    duk_bw_insert_ensure_area((thr), (bw), (off), (len))
#define DUK_BW_REMOVE_ENSURE_SLICE(thr,bw,off,len) \
    /* No difference between raw/ensure because the buffer shrinks. */ \
    DUK_BW_REMOVE_RAW_SLICE((thr), (bw), (off), (len))

/*
 *  Externs and prototypes
 */

#if !defined(DUK_SINGLE_FILE)
DUK_INTERNAL_DECL const duk_uint8_t duk_lc_digits[36];
DUK_INTERNAL_DECL const duk_uint8_t duk_uc_nybbles[16];
DUK_INTERNAL_DECL const duk_int8_t duk_hex_dectab[256];
#if defined(DUK_USE_HEX_FASTPATH)
DUK_INTERNAL_DECL const duk_int16_t duk_hex_dectab_shift4[256];
DUK_INTERNAL_DECL const duk_uint16_t duk_hex_enctab[256];
#endif
#endif  /* !DUK_SINGLE_FILE */

/* Note: assumes that duk_util_probe_steps size is 32 */
#if defined(DUK_USE_HOBJECT_HASH_PART)
#if !defined(DUK_SINGLE_FILE)
DUK_INTERNAL_DECL duk_uint8_t duk_util_probe_steps[32];
#endif  /* !DUK_SINGLE_FILE */
#endif

#if defined(DUK_USE_STRHASH_DENSE)
DUK_INTERNAL_DECL duk_uint32_t duk_util_hashbytes(const duk_uint8_t *data, duk_size_t len, duk_uint32_t seed);
#endif

DUK_INTERNAL_DECL duk_uint32_t duk_bd_decode(duk_bitdecoder_ctx *ctx, duk_small_int_t bits);
DUK_INTERNAL_DECL duk_small_uint_t duk_bd_decode_flag(duk_bitdecoder_ctx *ctx);
DUK_INTERNAL_DECL duk_uint32_t duk_bd_decode_flagged(duk_bitdecoder_ctx *ctx, duk_small_int_t bits, duk_uint32_t def_value);
DUK_INTERNAL_DECL duk_int32_t duk_bd_decode_flagged_signed(duk_bitdecoder_ctx *ctx, duk_small_int_t bits, duk_int32_t def_value);
DUK_INTERNAL_DECL duk_uint32_t duk_bd_decode_varuint(duk_bitdecoder_ctx *ctx);
DUK_INTERNAL_DECL duk_small_uint_t duk_bd_decode_bitpacked_string(duk_bitdecoder_ctx *bd, duk_uint8_t *out);

DUK_INTERNAL_DECL void duk_be_encode(duk_bitencoder_ctx *ctx, duk_uint32_t data, duk_small_int_t bits);
DUK_INTERNAL_DECL void duk_be_finish(duk_bitencoder_ctx *ctx);

#if !defined(DUK_USE_GET_RANDOM_DOUBLE)
DUK_INTERNAL_DECL duk_double_t duk_util_tinyrandom_get_double(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_util_tinyrandom_prepare_seed(duk_hthread *thr);
#endif

DUK_INTERNAL_DECL void duk_bw_init(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_hbuffer_dynamic *h_buf);
DUK_INTERNAL_DECL void duk_bw_init_pushbuf(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_size_t buf_size);
DUK_INTERNAL_DECL duk_uint8_t *duk_bw_resize(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_size_t sz);
DUK_INTERNAL_DECL void duk_bw_compact(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx);
DUK_INTERNAL_DECL void duk_bw_write_raw_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t src_off, duk_size_t len);
DUK_INTERNAL_DECL void duk_bw_write_ensure_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t src_off, duk_size_t len);
DUK_INTERNAL_DECL void duk_bw_insert_raw_bytes(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t dst_off, const duk_uint8_t *buf, duk_size_t len);
DUK_INTERNAL_DECL void duk_bw_insert_ensure_bytes(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t dst_off, const duk_uint8_t *buf, duk_size_t len);
DUK_INTERNAL_DECL void duk_bw_insert_raw_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t dst_off, duk_size_t src_off, duk_size_t len);
DUK_INTERNAL_DECL void duk_bw_insert_ensure_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t dst_off, duk_size_t src_off, duk_size_t len);
DUK_INTERNAL_DECL duk_uint8_t *duk_bw_insert_raw_area(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t off, duk_size_t len);
DUK_INTERNAL_DECL duk_uint8_t *duk_bw_insert_ensure_area(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t off, duk_size_t len);
DUK_INTERNAL_DECL void duk_bw_remove_raw_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t off, duk_size_t len);
/* No duk_bw_remove_ensure_slice(), functionality would be identical. */

DUK_INTERNAL_DECL duk_uint16_t duk_raw_read_u16_be(const duk_uint8_t *p);
DUK_INTERNAL_DECL duk_uint32_t duk_raw_read_u32_be(const duk_uint8_t *p);
DUK_INTERNAL_DECL duk_float_t duk_raw_read_float_be(const duk_uint8_t *p);
DUK_INTERNAL_DECL duk_double_t duk_raw_read_double_be(const duk_uint8_t *p);
DUK_INTERNAL_DECL duk_uint16_t duk_raw_readinc_u16_be(const duk_uint8_t **p);
DUK_INTERNAL_DECL duk_uint32_t duk_raw_readinc_u32_be(const duk_uint8_t **p);
DUK_INTERNAL_DECL duk_float_t duk_raw_readinc_float_be(const duk_uint8_t **p);
DUK_INTERNAL_DECL duk_double_t duk_raw_readinc_double_be(const duk_uint8_t **p);
DUK_INTERNAL_DECL void duk_raw_write_u16_be(duk_uint8_t *p, duk_uint16_t val);
DUK_INTERNAL_DECL void duk_raw_write_u32_be(duk_uint8_t *p, duk_uint32_t val);
DUK_INTERNAL_DECL void duk_raw_write_float_be(duk_uint8_t *p, duk_float_t val);
DUK_INTERNAL_DECL void duk_raw_write_double_be(duk_uint8_t *p, duk_double_t val);
DUK_INTERNAL_DECL duk_small_int_t duk_raw_write_xutf8(duk_uint8_t *p, duk_ucodepoint_t val);
DUK_INTERNAL_DECL duk_small_int_t duk_raw_write_cesu8(duk_uint8_t *p, duk_ucodepoint_t val);
DUK_INTERNAL_DECL void duk_raw_writeinc_u16_be(duk_uint8_t **p, duk_uint16_t val);
DUK_INTERNAL_DECL void duk_raw_writeinc_u32_be(duk_uint8_t **p, duk_uint32_t val);
DUK_INTERNAL_DECL void duk_raw_writeinc_float_be(duk_uint8_t **p, duk_float_t val);
DUK_INTERNAL_DECL void duk_raw_writeinc_double_be(duk_uint8_t **p, duk_double_t val);
DUK_INTERNAL_DECL void duk_raw_writeinc_xutf8(duk_uint8_t **p, duk_ucodepoint_t val);
DUK_INTERNAL_DECL void duk_raw_writeinc_cesu8(duk_uint8_t **p, duk_ucodepoint_t val);

#if defined(DUK_USE_DEBUGGER_SUPPORT)  /* For now only needed by the debugger. */
DUK_INTERNAL_DECL void duk_byteswap_bytes(duk_uint8_t *p, duk_small_uint_t len);
#endif

/* memcpy(), memmove() etc wrappers.  The plain variants like duk_memcpy()
 * assume C99+ and 'src' and 'dst' pointers must be non-NULL even when the
 * operation size is zero.  The unsafe variants like duk_memcpy_safe() deal
 * with the zero size case explicitly, and allow NULL pointers in that case
 * (which is undefined behavior in C99+).  For the majority of actual targets
 * a NULL pointer with a zero length is fine in practice.  These wrappers are
 * macros to force inlining; because there are hundreds of call sites, even a
 * few extra bytes per call site adds up to ~1kB footprint.
 */
#if defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR)
#define duk_memcpy(dst,src,len)  do { \
        void *duk__dst = (dst); \
        const void *duk__src = (src); \
        duk_size_t duk__len = (len); \
        DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \
        DUK_ASSERT(duk__src != NULL || duk__len == 0U); \
        (void) DUK_MEMCPY(duk__dst, duk__src, (size_t) duk__len); \
    } while (0)
#define duk_memcpy_unsafe(dst,src,len)  duk_memcpy((dst), (src), (len))
#define duk_memmove(dst,src,len)  do { \
        void *duk__dst = (dst); \
        const void *duk__src = (src); \
        duk_size_t duk__len = (len); \
        DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \
        DUK_ASSERT(duk__src != NULL || duk__len == 0U); \
        (void) DUK_MEMMOVE(duk__dst, duk__src, (size_t) duk__len); \
    } while (0)
#define duk_memmove_unsafe(dst,src,len)  duk_memmove((dst), (src), (len))
#define duk_memset(dst,val,len)  do { \
        void *duk__dst = (dst); \
        duk_small_int_t duk__val = (val); \
        duk_size_t duk__len = (len); \
        DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \
        (void) DUK_MEMSET(duk__dst, duk__val, (size_t) duk__len); \
    } while (0)
#define duk_memset_unsafe(dst,val,len)  duk_memset((dst), (val), (len))
#define duk_memzero(dst,len)  do { \
        void *duk__dst = (dst); \
        duk_size_t duk__len = (len); \
        DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \
        (void) DUK_MEMZERO(duk__dst, (size_t) duk__len); \
    } while (0)
#define duk_memzero_unsafe(dst,len)  duk_memzero((dst), (len))
#else  /* DUK_USE_ALLOW_UNDEFINED_BEHAVIOR */
#define duk_memcpy(dst,src,len)  do { \
        void *duk__dst = (dst); \
        const void *duk__src = (src); \
        duk_size_t duk__len = (len); \
        DUK_ASSERT(duk__dst != NULL); \
        DUK_ASSERT(duk__src != NULL); \
        (void) DUK_MEMCPY(duk__dst, duk__src, (size_t) duk__len); \
    } while (0)
#define duk_memcpy_unsafe(dst,src,len)  do { \
        void *duk__dst = (dst); \
        const void *duk__src = (src); \
        duk_size_t duk__len = (len); \
        DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \
        DUK_ASSERT(duk__src != NULL || duk__len == 0U); \
        if (DUK_LIKELY(duk__len > 0U)) { \
            DUK_ASSERT(duk__dst != NULL); \
            DUK_ASSERT(duk__src != NULL); \
            (void) DUK_MEMCPY(duk__dst, duk__src, (size_t) duk__len); \
        } \
    } while (0)
#define duk_memmove(dst,src,len)  do { \
        void *duk__dst = (dst); \
        const void *duk__src = (src); \
        duk_size_t duk__len = (len); \
        DUK_ASSERT(duk__dst != NULL); \
        DUK_ASSERT(duk__src != NULL); \
        (void) DUK_MEMMOVE(duk__dst, duk__src, (size_t) duk__len); \
    } while (0)
#define duk_memmove_unsafe(dst,src,len)  do { \
        void *duk__dst = (dst); \
        const void *duk__src = (src); \
        duk_size_t duk__len = (len); \
        DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \
        DUK_ASSERT(duk__src != NULL || duk__len == 0U); \
        if (DUK_LIKELY(duk__len > 0U)) { \
            DUK_ASSERT(duk__dst != NULL); \
            DUK_ASSERT(duk__src != NULL); \
            (void) DUK_MEMMOVE(duk__dst, duk__src, (size_t) duk__len); \
        } \
    } while (0)
#define duk_memset(dst,val,len)  do { \
        void *duk__dst = (dst); \
        duk_small_int_t duk__val = (val); \
        duk_size_t duk__len = (len); \
        DUK_ASSERT(duk__dst != NULL); \
        (void) DUK_MEMSET(duk__dst, duk__val, (size_t) duk__len); \
    } while (0)
#define duk_memset_unsafe(dst,val,len)  do { \
        void *duk__dst = (dst); \
        duk_small_int_t duk__val = (val); \
        duk_size_t duk__len = (len); \
        DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \
        if (DUK_LIKELY(duk__len > 0U)) { \
            DUK_ASSERT(duk__dst != NULL); \
            (void) DUK_MEMSET(duk__dst, duk__val, (size_t) duk__len); \
        } \
    } while (0)
#define duk_memzero(dst,len)  do { \
        void *duk__dst = (dst); \
        duk_size_t duk__len = (len); \
        DUK_ASSERT(duk__dst != NULL); \
        (void) DUK_MEMZERO(duk__dst, (size_t) duk__len); \
    } while (0)
#define duk_memzero_unsafe(dst,len)  do { \
        void *duk__dst = (dst); \
        duk_size_t duk__len = (len); \
        DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \
        if (DUK_LIKELY(duk__len > 0U)) { \
            DUK_ASSERT(duk__dst != NULL); \
            (void) DUK_MEMZERO(duk__dst, (size_t) duk__len); \
        } \
    } while (0)
#endif  /* DUK_USE_ALLOW_UNDEFINED_BEHAVIOR */

DUK_INTERNAL_DECL duk_small_int_t duk_memcmp(const void *s1, const void *s2, duk_size_t len);
DUK_INTERNAL_DECL duk_small_int_t duk_memcmp_unsafe(const void *s1, const void *s2, duk_size_t len);

DUK_INTERNAL_DECL duk_bool_t duk_is_whole_get_int32_nonegzero(duk_double_t x, duk_int32_t *ival);
DUK_INTERNAL_DECL duk_bool_t duk_is_whole_get_int32(duk_double_t x, duk_int32_t *ival);
DUK_INTERNAL_DECL duk_bool_t duk_double_is_anyinf(duk_double_t x);
DUK_INTERNAL_DECL duk_bool_t duk_double_is_posinf(duk_double_t x);
DUK_INTERNAL_DECL duk_bool_t duk_double_is_neginf(duk_double_t x);
DUK_INTERNAL_DECL duk_bool_t duk_double_is_nan(duk_double_t x);
DUK_INTERNAL_DECL duk_bool_t duk_double_is_nan_or_zero(duk_double_t x);
DUK_INTERNAL_DECL duk_bool_t duk_double_is_nan_or_inf(duk_double_t x);
DUK_INTERNAL_DECL duk_bool_t duk_double_is_nan_zero_inf(duk_double_t x);
DUK_INTERNAL_DECL duk_small_uint_t duk_double_signbit(duk_double_t x);
DUK_INTERNAL_DECL duk_double_t duk_double_trunc_towards_zero(duk_double_t x);
DUK_INTERNAL_DECL duk_bool_t duk_double_same_sign(duk_double_t x, duk_double_t y);
DUK_INTERNAL_DECL duk_double_t duk_double_fmin(duk_double_t x, duk_double_t y);
DUK_INTERNAL_DECL duk_double_t duk_double_fmax(duk_double_t x, duk_double_t y);
DUK_INTERNAL_DECL duk_bool_t duk_double_is_finite(duk_double_t x);
DUK_INTERNAL_DECL duk_bool_t duk_double_is_integer(duk_double_t x);
DUK_INTERNAL_DECL duk_bool_t duk_double_is_safe_integer(duk_double_t x);

DUK_INTERNAL_DECL duk_double_t duk_double_div(duk_double_t x, duk_double_t y);
DUK_INTERNAL_DECL duk_int_t duk_double_to_int_t(duk_double_t x);
DUK_INTERNAL_DECL duk_uint_t duk_double_to_uint_t(duk_double_t x);
DUK_INTERNAL_DECL duk_int32_t duk_double_to_int32_t(duk_double_t x);
DUK_INTERNAL_DECL duk_uint32_t duk_double_to_uint32_t(duk_double_t x);
DUK_INTERNAL_DECL duk_float_t duk_double_to_float_t(duk_double_t x);
DUK_INTERNAL_DECL duk_bool_t duk_double_equals(duk_double_t x, duk_double_t y);
DUK_INTERNAL_DECL duk_bool_t duk_float_equals(duk_float_t x, duk_float_t y);

/*
 *  Miscellaneous
 */

/* Example: x     = 0x10 = 0b00010000
 *          x - 1 = 0x0f = 0b00001111
 *          x & (x - 1) == 0
 *
 *          x     = 0x07 = 0b00000111
 *          x - 1 = 0x06 = 0b00000110
 *          x & (x - 1) != 0
 *
 * However, incorrectly true for x == 0 so check for that explicitly.
 */
#define DUK_IS_POWER_OF_TWO(x) \
    ((x) != 0U && ((x) & ((x) - 1U)) == 0U)

#endif  /* DUK_UTIL_H_INCLUDED */
/* #include duk_strings.h */
//#line 1 "duk_strings.h"
/*
 *  Shared string macros.
 *
 *  Using shared macros helps minimize strings data size because it's easy
 *  to check if an existing string could be used.  String constants don't
 *  need to be all defined here; defining a string here makes sense if there's
 *  a high chance the string could be reused.  Also, using macros allows
 *  a call site express the exact string needed, but the macro may map to an
 *  approximate string to reduce unique string count.  Macros can also be
 *  more easily tuned for low memory targets than #if defined()s throughout
 *  the code base.
 *
 *  Because format strings behave differently in the call site (they need to
 *  be followed by format arguments), they use a special prefix DUK_STR_FMT_.
 *
 *  On some compilers using explicit shared strings is preferable; on others
 *  it may be better to use straight literals because the compiler will combine
 *  them anyway, and such strings won't end up unnecessarily in a symbol table.
 */

#if !defined(DUK_ERRMSG_H_INCLUDED)
#define DUK_ERRMSG_H_INCLUDED

/* Mostly API and built-in method related */
#define DUK_STR_INTERNAL_ERROR                   "internal error"
#define DUK_STR_UNSUPPORTED                      "unsupported"
#define DUK_STR_INVALID_COUNT                    "invalid count"
#define DUK_STR_INVALID_ARGS                     "invalid args"
#define DUK_STR_INVALID_STATE                    "invalid state"
#define DUK_STR_INVALID_INPUT                    "invalid input"
#define DUK_STR_INVALID_LENGTH                   "invalid length"
#define DUK_STR_NOT_CONSTRUCTABLE                "not constructable"
#define DUK_STR_CONSTRUCT_ONLY                   "constructor requires 'new'"
#define DUK_STR_NOT_CALLABLE                     "not callable"
#define DUK_STR_NOT_EXTENSIBLE                   "not extensible"
#define DUK_STR_NOT_WRITABLE                     "not writable"
#define DUK_STR_NOT_CONFIGURABLE                 "not configurable"
#define DUK_STR_INVALID_CONTEXT                  "invalid context"
#define DUK_STR_INVALID_INDEX                    "invalid args"
#define DUK_STR_PUSH_BEYOND_ALLOC_STACK          "cannot push beyond allocated stack"
#define DUK_STR_NOT_UNDEFINED                    "unexpected type"
#define DUK_STR_NOT_NULL                         "unexpected type"
#define DUK_STR_NOT_BOOLEAN                      "unexpected type"
#define DUK_STR_NOT_NUMBER                       "unexpected type"
#define DUK_STR_NOT_STRING                       "unexpected type"
#define DUK_STR_NOT_OBJECT                       "unexpected type"
#define DUK_STR_NOT_POINTER                      "unexpected type"
#define DUK_STR_NOT_BUFFER                       "not buffer"  /* still in use with verbose messages */
#define DUK_STR_UNEXPECTED_TYPE                  "unexpected type"
#define DUK_STR_NOT_THREAD                       "unexpected type"
#define DUK_STR_NOT_COMPFUNC                     "unexpected type"
#define DUK_STR_NOT_NATFUNC                      "unexpected type"
#define DUK_STR_NOT_C_FUNCTION                   "unexpected type"
#define DUK_STR_NOT_FUNCTION                     "unexpected type"
#define DUK_STR_NOT_REGEXP                       "unexpected type"
#define DUK_STR_TOPRIMITIVE_FAILED               "coercion to primitive failed"
#define DUK_STR_NUMBER_OUTSIDE_RANGE             "number outside range"
#define DUK_STR_NOT_OBJECT_COERCIBLE             "not object coercible"
#define DUK_STR_CANNOT_NUMBER_COERCE_SYMBOL      "cannot number coerce Symbol"
#define DUK_STR_CANNOT_STRING_COERCE_SYMBOL      "cannot string coerce Symbol"
#define DUK_STR_STRING_TOO_LONG                  "string too long"
#define DUK_STR_BUFFER_TOO_LONG                  "buffer too long"
#define DUK_STR_ALLOC_FAILED                     "alloc failed"
#define DUK_STR_WRONG_BUFFER_TYPE                "wrong buffer type"
#define DUK_STR_BASE64_ENCODE_FAILED             "base64 encode failed"
#define DUK_STR_SOURCE_DECODE_FAILED             "source decode failed"
#define DUK_STR_UTF8_DECODE_FAILED               "utf-8 decode failed"
#define DUK_STR_BASE64_DECODE_FAILED             "base64 decode failed"
#define DUK_STR_HEX_DECODE_FAILED                "hex decode failed"
#define DUK_STR_INVALID_BYTECODE                 "invalid bytecode"
#define DUK_STR_NO_SOURCECODE                    "no sourcecode"
#define DUK_STR_RESULT_TOO_LONG                  "result too long"
#define DUK_STR_INVALID_CFUNC_RC                 "invalid C function rc"
#define DUK_STR_INVALID_INSTANCEOF_RVAL          "invalid instanceof rval"
#define DUK_STR_INVALID_INSTANCEOF_RVAL_NOPROTO  "instanceof rval has no .prototype"

/* JSON */
#define DUK_STR_FMT_PTR                          "%p"
#define DUK_STR_FMT_INVALID_JSON                 "invalid json (at offset %ld)"
#define DUK_STR_CYCLIC_INPUT                     "cyclic input"

/* Generic codec */
#define DUK_STR_DEC_RECLIMIT                     "decode recursion limit"
#define DUK_STR_ENC_RECLIMIT                     "encode recursion limit"

/* Object property access */
#define DUK_STR_INVALID_BASE                     "invalid base value"
#define DUK_STR_STRICT_CALLER_READ               "cannot read strict 'caller'"
#define DUK_STR_PROXY_REJECTED                   "proxy rejected"
#define DUK_STR_INVALID_ARRAY_LENGTH             "invalid array length"
#define DUK_STR_SETTER_UNDEFINED                 "setter undefined"
#define DUK_STR_INVALID_DESCRIPTOR               "invalid descriptor"

/* Proxy */
#define DUK_STR_PROXY_REVOKED                    "proxy revoked"
#define DUK_STR_INVALID_TRAP_RESULT              "invalid trap result"

/* Variables */

/* Lexer */
#define DUK_STR_INVALID_ESCAPE                   "invalid escape"
#define DUK_STR_UNTERMINATED_STRING              "unterminated string"
#define DUK_STR_UNTERMINATED_COMMENT             "unterminated comment"
#define DUK_STR_UNTERMINATED_REGEXP              "unterminated regexp"
#define DUK_STR_TOKEN_LIMIT                      "token limit"
#define DUK_STR_REGEXP_SUPPORT_DISABLED          "regexp support disabled"
#define DUK_STR_INVALID_NUMBER_LITERAL           "invalid number literal"
#define DUK_STR_INVALID_TOKEN                    "invalid token"

/* Compiler */
#define DUK_STR_PARSE_ERROR                      "parse error"
#define DUK_STR_DUPLICATE_LABEL                  "duplicate label"
#define DUK_STR_INVALID_LABEL                    "invalid label"
#define DUK_STR_INVALID_ARRAY_LITERAL            "invalid array literal"
#define DUK_STR_INVALID_OBJECT_LITERAL           "invalid object literal"
#define DUK_STR_INVALID_VAR_DECLARATION          "invalid variable declaration"
#define DUK_STR_CANNOT_DELETE_IDENTIFIER         "cannot delete identifier"
#define DUK_STR_INVALID_EXPRESSION               "invalid expression"
#define DUK_STR_INVALID_LVALUE                   "invalid lvalue"
#define DUK_STR_INVALID_NEWTARGET                "invalid new.target"
#define DUK_STR_EXPECTED_IDENTIFIER              "expected identifier"
#define DUK_STR_EMPTY_EXPR_NOT_ALLOWED           "empty expression not allowed"
#define DUK_STR_INVALID_FOR                      "invalid for statement"
#define DUK_STR_INVALID_SWITCH                   "invalid switch statement"
#define DUK_STR_INVALID_BREAK_CONT_LABEL         "invalid break/continue label"
#define DUK_STR_INVALID_RETURN                   "invalid return"
#define DUK_STR_INVALID_TRY                      "invalid try"
#define DUK_STR_INVALID_THROW                    "invalid throw"
#define DUK_STR_WITH_IN_STRICT_MODE              "with in strict mode"
#define DUK_STR_FUNC_STMT_NOT_ALLOWED            "function statement not allowed"
#define DUK_STR_UNTERMINATED_STMT                "unterminated statement"
#define DUK_STR_INVALID_ARG_NAME                 "invalid argument name"
#define DUK_STR_INVALID_FUNC_NAME                "invalid function name"
#define DUK_STR_INVALID_GETSET_NAME              "invalid getter/setter name"
#define DUK_STR_FUNC_NAME_REQUIRED               "function name required"

/* RegExp */
#define DUK_STR_INVALID_QUANTIFIER               "invalid regexp quantifier"
#define DUK_STR_INVALID_QUANTIFIER_NO_ATOM       "quantifier without preceding atom"
#define DUK_STR_INVALID_QUANTIFIER_VALUES        "quantifier values invalid (qmin > qmax)"
#define DUK_STR_QUANTIFIER_TOO_MANY_COPIES       "quantifier requires too many atom copies"
#define DUK_STR_UNEXPECTED_CLOSING_PAREN         "unexpected closing parenthesis"
#define DUK_STR_UNEXPECTED_END_OF_PATTERN        "unexpected end of pattern"
#define DUK_STR_UNEXPECTED_REGEXP_TOKEN          "unexpected token in regexp"
#define DUK_STR_INVALID_REGEXP_FLAGS             "invalid regexp flags"
#define DUK_STR_INVALID_REGEXP_ESCAPE            "invalid regexp escape"
#define DUK_STR_INVALID_BACKREFS                 "invalid backreference(s)"
#define DUK_STR_INVALID_REGEXP_CHARACTER         "invalid regexp character"
#define DUK_STR_INVALID_REGEXP_GROUP             "invalid regexp group"
#define DUK_STR_UNTERMINATED_CHARCLASS           "unterminated character class"
#define DUK_STR_INVALID_RANGE                    "invalid range"

/* Limits */
#define DUK_STR_VALSTACK_LIMIT                   "valstack limit"
#define DUK_STR_CALLSTACK_LIMIT                  "callstack limit"
#define DUK_STR_PROTOTYPE_CHAIN_LIMIT            "prototype chain limit"
#define DUK_STR_BOUND_CHAIN_LIMIT                "function call bound chain limit"
#define DUK_STR_NATIVE_STACK_LIMIT               "C stack depth limit"
#define DUK_STR_COMPILER_RECURSION_LIMIT         "compiler recursion limit"
#define DUK_STR_BYTECODE_LIMIT                   "bytecode limit"
#define DUK_STR_REG_LIMIT                        "register limit"
#define DUK_STR_TEMP_LIMIT                       "temp limit"
#define DUK_STR_CONST_LIMIT                      "const limit"
#define DUK_STR_FUNC_LIMIT                       "function limit"
#define DUK_STR_REGEXP_COMPILER_RECURSION_LIMIT  "regexp compiler recursion limit"
#define DUK_STR_REGEXP_EXECUTOR_RECURSION_LIMIT  "regexp executor recursion limit"
#define DUK_STR_REGEXP_EXECUTOR_STEP_LIMIT       "regexp step limit"

#endif  /* DUK_ERRMSG_H_INCLUDED */
/* #include duk_js_bytecode.h */
//#line 1 "duk_js_bytecode.h"
/*
 *  ECMAScript bytecode
 */

#if !defined(DUK_JS_BYTECODE_H_INCLUDED)
#define DUK_JS_BYTECODE_H_INCLUDED

/*
 *  Bytecode instruction layout
 *  ===========================
 *
 *  Instructions are unsigned 32-bit integers divided as follows:
 *
 *  !3!3!2!2!2!2!2!2!2!2!2!2!1!1!1!1!1!1!1!1!1!1! ! ! ! ! ! ! ! ! ! !
 *  !1!0!9!8!7!6!5!4!3!2!1!0!9!8!7!6!5!4!3!2!1!0!9!8!7!6!5!4!3!2!1!0!
 *  +-----------------------------------------------+---------------+
 *  !       C       !       B       !       A       !       OP      !
 *  +-----------------------------------------------+---------------+
 *
 *  OP (8 bits):  opcode (DUK_OP_*), access should be fastest
 *                consecutive opcodes allocated when opcode needs flags
 *   A (8 bits):  typically a target register number
 *   B (8 bits):  typically first source register/constant number
 *   C (8 bits):  typically second source register/constant number
 *
 *  Some instructions combine BC or ABC together for larger parameter values.
 *  Signed integers (e.g. jump offsets) are encoded as unsigned, with an
 *  opcode specific bias.
 *
 *  Some opcodes have flags which are handled by allocating consecutive
 *  opcodes to make space for 1-N flags.  Flags can also be e.g. in the 'A'
 *  field when there's room for the specific opcode.
 *
 *  For example, if three flags were needed, they could be allocated from
 *  the opcode field as follows:
 *
 *  !3!3!2!2!2!2!2!2!2!2!2!2!1!1!1!1!1!1!1!1!1!1! ! ! ! ! ! ! ! ! ! !
 *  !1!0!9!8!7!6!5!4!3!2!1!0!9!8!7!6!5!4!3!2!1!0!9!8!7!6!5!4!3!2!1!0!
 *  +-----------------------------------------------+---------------+
 *  !       C       !       B       !       A       !    OP   !Z!Y!X!
 *  +-----------------------------------------------+---------------+
 *
 *  Some opcodes accept a reg/const argument which is handled by allocating
 *  flags in the OP field, see DUK_BC_ISREG() and DUK_BC_ISCONST().  The
 *  following convention is shared by most opcodes, so that the compiler
 *  can handle reg/const flagging without opcode specific code paths:
 *
 *  !3!3!2!2!2!2!2!2!2!2!2!2!1!1!1!1!1!1!1!1!1!1! ! ! ! ! ! ! ! ! ! !
 *  !1!0!9!8!7!6!5!4!3!2!1!0!9!8!7!6!5!4!3!2!1!0!9!8!7!6!5!4!3!2!1!0!
 *  +-----------------------------------------------+---------------+
 *  !       C       !       B       !       A       !     OP    !Y!X!
 *  +-----------------------------------------------+---------------+
 *
 *    X  1=B is const, 0=B is reg
 *    Y  1=C is const, 0=C is reg
 *
 *    In effect OP, OP + 1, OP + 2, and OP + 3 are allocated from the
 *    8-bit opcode space for a single logical opcode.  The base opcode
 *    number should be divisible by 4.  If the opcode is called 'FOO'
 *    the following opcode constants would be defined:
 *
 *      DUK_OP_FOO     100       // base opcode number
 *      DUK_OP_FOO_RR  100       // FOO, B=reg, C=reg
 *      DUK_OP_FOO_CR  101       // FOO, B=const, C=reg
 *      DUK_OP_FOO_RC  102       // FOO, B=reg, C=const
 *      DUK_OP_FOO_CC  103       // FOO, B=const, C=const
 *
 *  If only B or C is a reg/const, the unused opcode combinations can be
 *  used for other opcodes (which take no reg/const argument).  However,
 *  such opcode values are initially reserved, at least while opcode space
 *  is available.  For example, if 'BAR' uses B for a register field and
 *  C is a reg/const:
 *
 *      DUK_OP_BAR            116    // base opcode number
 *      DUK_OP_BAR_RR         116    // BAR, B=reg, C=reg
 *      DUK_OP_BAR_CR_UNUSED  117    // unused, could be repurposed
 *      DUK_OP_BAR_RC         118    // BAR, B=reg, C=const
 *      DUK_OP_BAR_CC_UNUSED  119    // unused, could be repurposed
 *
 *  Macro naming is a bit misleading, e.g. "ABC" in macro name but the
 *  field layout is concretely "CBA" in the register.
 */

typedef duk_uint32_t duk_instr_t;

#define DUK_BC_SHIFT_OP             0
#define DUK_BC_SHIFT_A              8
#define DUK_BC_SHIFT_B              16
#define DUK_BC_SHIFT_C              24
#define DUK_BC_SHIFT_BC             DUK_BC_SHIFT_B
#define DUK_BC_SHIFT_ABC            DUK_BC_SHIFT_A

#define DUK_BC_UNSHIFTED_MASK_OP    0xffUL
#define DUK_BC_UNSHIFTED_MASK_A     0xffUL
#define DUK_BC_UNSHIFTED_MASK_B     0xffUL
#define DUK_BC_UNSHIFTED_MASK_C     0xffUL
#define DUK_BC_UNSHIFTED_MASK_BC    0xffffUL
#define DUK_BC_UNSHIFTED_MASK_ABC   0xffffffUL

#define DUK_BC_SHIFTED_MASK_OP      (DUK_BC_UNSHIFTED_MASK_OP << DUK_BC_SHIFT_OP)
#define DUK_BC_SHIFTED_MASK_A       (DUK_BC_UNSHIFTED_MASK_A << DUK_BC_SHIFT_A)
#define DUK_BC_SHIFTED_MASK_B       (DUK_BC_UNSHIFTED_MASK_B << DUK_BC_SHIFT_B)
#define DUK_BC_SHIFTED_MASK_C       (DUK_BC_UNSHIFTED_MASK_C << DUK_BC_SHIFT_C)
#define DUK_BC_SHIFTED_MASK_BC      (DUK_BC_UNSHIFTED_MASK_BC << DUK_BC_SHIFT_BC)
#define DUK_BC_SHIFTED_MASK_ABC     (DUK_BC_UNSHIFTED_MASK_ABC << DUK_BC_SHIFT_ABC)

#define DUK_DEC_OP(x)               ((x) & 0xffUL)
#define DUK_DEC_A(x)                (((x) >> 8) & 0xffUL)
#define DUK_DEC_B(x)                (((x) >> 16) & 0xffUL)
#define DUK_DEC_C(x)                (((x) >> 24) & 0xffUL)
#define DUK_DEC_BC(x)               (((x) >> 16) & 0xffffUL)
#define DUK_DEC_ABC(x)              (((x) >> 8) & 0xffffffUL)

#define DUK_ENC_OP(op)              ((duk_instr_t) (op))
#define DUK_ENC_OP_ABC(op,abc)      ((duk_instr_t) ( \
                                        (((duk_instr_t) (abc)) << 8) | \
                                        ((duk_instr_t) (op)) \
                                    ))
#define DUK_ENC_OP_A_BC(op,a,bc)    ((duk_instr_t) ( \
                                        (((duk_instr_t) (bc)) << 16) | \
                                        (((duk_instr_t) (a)) << 8) | \
                                        ((duk_instr_t) (op)) \
                                    ))
#define DUK_ENC_OP_A_B_C(op,a,b,c)  ((duk_instr_t) ( \
                                        (((duk_instr_t) (c)) << 24) | \
                                        (((duk_instr_t) (b)) << 16) | \
                                        (((duk_instr_t) (a)) << 8) | \
                                        ((duk_instr_t) (op)) \
                                    ))
#define DUK_ENC_OP_A_B(op,a,b)      DUK_ENC_OP_A_B_C((op),(a),(b),0)
#define DUK_ENC_OP_A(op,a)          DUK_ENC_OP_A_B_C((op),(a),0,0)
#define DUK_ENC_OP_BC(op,bc)        DUK_ENC_OP_A_BC((op),0,(bc))

/* Get opcode base value with B/C reg/const flags cleared. */
#define DUK_BC_NOREGCONST_OP(op)    ((op) & 0xfc)

/* Constants should be signed so that signed arithmetic involving them
 * won't cause values to be coerced accidentally to unsigned.
 */
#define DUK_BC_OP_MIN               0
#define DUK_BC_OP_MAX               0xffL
#define DUK_BC_A_MIN                0
#define DUK_BC_A_MAX                0xffL
#define DUK_BC_B_MIN                0
#define DUK_BC_B_MAX                0xffL
#define DUK_BC_C_MIN                0
#define DUK_BC_C_MAX                0xffL
#define DUK_BC_BC_MIN               0
#define DUK_BC_BC_MAX               0xffffL
#define DUK_BC_ABC_MIN              0
#define DUK_BC_ABC_MAX              0xffffffL

/* Masks for B/C reg/const indicator in opcode field. */
#define DUK_BC_REGCONST_B           (0x01UL)
#define DUK_BC_REGCONST_C           (0x02UL)

/* Misc. masks for opcode field. */
#define DUK_BC_INCDECP_FLAG_DEC     (0x04UL)
#define DUK_BC_INCDECP_FLAG_POST    (0x08UL)

/* Opcodes. */
#define DUK_OP_LDREG                0
#define DUK_OP_STREG                1
#define DUK_OP_JUMP                 2
#define DUK_OP_LDCONST              3
#define DUK_OP_LDINT                4
#define DUK_OP_LDINTX               5
#define DUK_OP_LDTHIS               6
#define DUK_OP_LDUNDEF              7
#define DUK_OP_LDNULL               8
#define DUK_OP_LDTRUE               9
#define DUK_OP_LDFALSE              10
#define DUK_OP_GETVAR               11
#define DUK_OP_BNOT                 12
#define DUK_OP_LNOT                 13
#define DUK_OP_UNM                  14
#define DUK_OP_UNP                  15
#define DUK_OP_EQ                   16
#define DUK_OP_EQ_RR                16
#define DUK_OP_EQ_CR                17
#define DUK_OP_EQ_RC                18
#define DUK_OP_EQ_CC                19
#define DUK_OP_NEQ                  20
#define DUK_OP_NEQ_RR               20
#define DUK_OP_NEQ_CR               21
#define DUK_OP_NEQ_RC               22
#define DUK_OP_NEQ_CC               23
#define DUK_OP_SEQ                  24
#define DUK_OP_SEQ_RR               24
#define DUK_OP_SEQ_CR               25
#define DUK_OP_SEQ_RC               26
#define DUK_OP_SEQ_CC               27
#define DUK_OP_SNEQ                 28
#define DUK_OP_SNEQ_RR              28
#define DUK_OP_SNEQ_CR              29
#define DUK_OP_SNEQ_RC              30
#define DUK_OP_SNEQ_CC              31
#define DUK_OP_GT                   32
#define DUK_OP_GT_RR                32
#define DUK_OP_GT_CR                33
#define DUK_OP_GT_RC                34
#define DUK_OP_GT_CC                35
#define DUK_OP_GE                   36
#define DUK_OP_GE_RR                36
#define DUK_OP_GE_CR                37
#define DUK_OP_GE_RC                38
#define DUK_OP_GE_CC                39
#define DUK_OP_LT                   40
#define DUK_OP_LT_RR                40
#define DUK_OP_LT_CR                41
#define DUK_OP_LT_RC                42
#define DUK_OP_LT_CC                43
#define DUK_OP_LE                   44
#define DUK_OP_LE_RR                44
#define DUK_OP_LE_CR                45
#define DUK_OP_LE_RC                46
#define DUK_OP_LE_CC                47
#define DUK_OP_IFTRUE               48
#define DUK_OP_IFTRUE_R             48
#define DUK_OP_IFTRUE_C             49
#define DUK_OP_IFFALSE              50
#define DUK_OP_IFFALSE_R            50
#define DUK_OP_IFFALSE_C            51
#define DUK_OP_ADD                  52
#define DUK_OP_ADD_RR               52
#define DUK_OP_ADD_CR               53
#define DUK_OP_ADD_RC               54
#define DUK_OP_ADD_CC               55
#define DUK_OP_SUB                  56
#define DUK_OP_SUB_RR               56
#define DUK_OP_SUB_CR               57
#define DUK_OP_SUB_RC               58
#define DUK_OP_SUB_CC               59
#define DUK_OP_MUL                  60
#define DUK_OP_MUL_RR               60
#define DUK_OP_MUL_CR               61
#define DUK_OP_MUL_RC               62
#define DUK_OP_MUL_CC               63
#define DUK_OP_DIV                  64
#define DUK_OP_DIV_RR               64
#define DUK_OP_DIV_CR               65
#define DUK_OP_DIV_RC               66
#define DUK_OP_DIV_CC               67
#define DUK_OP_MOD                  68
#define DUK_OP_MOD_RR               68
#define DUK_OP_MOD_CR               69
#define DUK_OP_MOD_RC               70
#define DUK_OP_MOD_CC               71
#define DUK_OP_EXP                  72
#define DUK_OP_EXP_RR               72
#define DUK_OP_EXP_CR               73
#define DUK_OP_EXP_RC               74
#define DUK_OP_EXP_CC               75
#define DUK_OP_BAND                 76
#define DUK_OP_BAND_RR              76
#define DUK_OP_BAND_CR              77
#define DUK_OP_BAND_RC              78
#define DUK_OP_BAND_CC              79
#define DUK_OP_BOR                  80
#define DUK_OP_BOR_RR               80
#define DUK_OP_BOR_CR               81
#define DUK_OP_BOR_RC               82
#define DUK_OP_BOR_CC               83
#define DUK_OP_BXOR                 84
#define DUK_OP_BXOR_RR              84
#define DUK_OP_BXOR_CR              85
#define DUK_OP_BXOR_RC              86
#define DUK_OP_BXOR_CC              87
#define DUK_OP_BASL                 88
#define DUK_OP_BASL_RR              88
#define DUK_OP_BASL_CR              89
#define DUK_OP_BASL_RC              90
#define DUK_OP_BASL_CC              91
#define DUK_OP_BLSR                 92
#define DUK_OP_BLSR_RR              92
#define DUK_OP_BLSR_CR              93
#define DUK_OP_BLSR_RC              94
#define DUK_OP_BLSR_CC              95
#define DUK_OP_BASR                 96
#define DUK_OP_BASR_RR              96
#define DUK_OP_BASR_CR              97
#define DUK_OP_BASR_RC              98
#define DUK_OP_BASR_CC              99
#define DUK_OP_INSTOF               100
#define DUK_OP_INSTOF_RR            100
#define DUK_OP_INSTOF_CR            101
#define DUK_OP_INSTOF_RC            102
#define DUK_OP_INSTOF_CC            103
#define DUK_OP_IN                   104
#define DUK_OP_IN_RR                104
#define DUK_OP_IN_CR                105
#define DUK_OP_IN_RC                106
#define DUK_OP_IN_CC                107
#define DUK_OP_GETPROP              108
#define DUK_OP_GETPROP_RR           108
#define DUK_OP_GETPROP_CR           109
#define DUK_OP_GETPROP_RC           110
#define DUK_OP_GETPROP_CC           111
#define DUK_OP_PUTPROP              112
#define DUK_OP_PUTPROP_RR           112
#define DUK_OP_PUTPROP_CR           113
#define DUK_OP_PUTPROP_RC           114
#define DUK_OP_PUTPROP_CC           115
#define DUK_OP_DELPROP              116
#define DUK_OP_DELPROP_RR           116
#define DUK_OP_DELPROP_CR_UNUSED    117  /* unused now */
#define DUK_OP_DELPROP_RC           118
#define DUK_OP_DELPROP_CC_UNUSED    119  /* unused now */
#define DUK_OP_PREINCR              120  /* pre/post opcode values have constraints, */
#define DUK_OP_PREDECR              121  /* see duk_js_executor.c and duk_js_compiler.c. */
#define DUK_OP_POSTINCR             122
#define DUK_OP_POSTDECR             123
#define DUK_OP_PREINCV              124
#define DUK_OP_PREDECV              125
#define DUK_OP_POSTINCV             126
#define DUK_OP_POSTDECV             127
#define DUK_OP_PREINCP              128  /* pre/post inc/dec prop opcodes have constraints */
#define DUK_OP_PREINCP_RR           128
#define DUK_OP_PREINCP_CR           129
#define DUK_OP_PREINCP_RC           130
#define DUK_OP_PREINCP_CC           131
#define DUK_OP_PREDECP              132
#define DUK_OP_PREDECP_RR           132
#define DUK_OP_PREDECP_CR           133
#define DUK_OP_PREDECP_RC           134
#define DUK_OP_PREDECP_CC           135
#define DUK_OP_POSTINCP             136
#define DUK_OP_POSTINCP_RR          136
#define DUK_OP_POSTINCP_CR          137
#define DUK_OP_POSTINCP_RC          138
#define DUK_OP_POSTINCP_CC          139
#define DUK_OP_POSTDECP             140
#define DUK_OP_POSTDECP_RR          140
#define DUK_OP_POSTDECP_CR          141
#define DUK_OP_POSTDECP_RC          142
#define DUK_OP_POSTDECP_CC          143
#define DUK_OP_DECLVAR              144
#define DUK_OP_DECLVAR_RR           144
#define DUK_OP_DECLVAR_CR           145
#define DUK_OP_DECLVAR_RC           146
#define DUK_OP_DECLVAR_CC           147
#define DUK_OP_REGEXP               148
#define DUK_OP_REGEXP_RR            148
#define DUK_OP_REGEXP_CR            149
#define DUK_OP_REGEXP_RC            150
#define DUK_OP_REGEXP_CC            151
#define DUK_OP_CLOSURE              152
#define DUK_OP_TYPEOF               153
#define DUK_OP_TYPEOFID             154
#define DUK_OP_PUTVAR               155
#define DUK_OP_DELVAR               156
#define DUK_OP_RETREG               157
#define DUK_OP_RETUNDEF             158
#define DUK_OP_RETCONST             159
#define DUK_OP_RETCONSTN            160  /* return const without incref (e.g. number) */
#define DUK_OP_LABEL                161
#define DUK_OP_ENDLABEL             162
#define DUK_OP_BREAK                163
#define DUK_OP_CONTINUE             164
#define DUK_OP_TRYCATCH             165
#define DUK_OP_ENDTRY               166
#define DUK_OP_ENDCATCH             167
#define DUK_OP_ENDFIN               168
#define DUK_OP_THROW                169
#define DUK_OP_INVLHS               170
#define DUK_OP_CSREG                171
#define DUK_OP_CSVAR                172
#define DUK_OP_CSVAR_RR             172
#define DUK_OP_CSVAR_CR             173
#define DUK_OP_CSVAR_RC             174
#define DUK_OP_CSVAR_CC             175
#define DUK_OP_CALL0                176  /* DUK_OP_CALL0 & 0x0F must be zero. */
#define DUK_OP_CALL1                177
#define DUK_OP_CALL2                178
#define DUK_OP_CALL3                179
#define DUK_OP_CALL4                180
#define DUK_OP_CALL5                181
#define DUK_OP_CALL6                182
#define DUK_OP_CALL7                183
#define DUK_OP_CALL8                184
#define DUK_OP_CALL9                185
#define DUK_OP_CALL10               186
#define DUK_OP_CALL11               187
#define DUK_OP_CALL12               188
#define DUK_OP_CALL13               189
#define DUK_OP_CALL14               190
#define DUK_OP_CALL15               191
#define DUK_OP_NEWOBJ               192
#define DUK_OP_NEWARR               193
#define DUK_OP_MPUTOBJ              194
#define DUK_OP_MPUTOBJI             195
#define DUK_OP_INITSET              196
#define DUK_OP_INITGET              197
#define DUK_OP_MPUTARR              198
#define DUK_OP_MPUTARRI             199
#define DUK_OP_SETALEN              200
#define DUK_OP_INITENUM             201
#define DUK_OP_NEXTENUM             202
#define DUK_OP_NEWTARGET            203
#define DUK_OP_DEBUGGER             204
#define DUK_OP_NOP                  205
#define DUK_OP_INVALID              206
#define DUK_OP_UNUSED207            207
#define DUK_OP_GETPROPC             208
#define DUK_OP_GETPROPC_RR          208
#define DUK_OP_GETPROPC_CR          209
#define DUK_OP_GETPROPC_RC          210
#define DUK_OP_GETPROPC_CC          211
#define DUK_OP_UNUSED212            212
#define DUK_OP_UNUSED213            213
#define DUK_OP_UNUSED214            214
#define DUK_OP_UNUSED215            215
#define DUK_OP_UNUSED216            216
#define DUK_OP_UNUSED217            217
#define DUK_OP_UNUSED218            218
#define DUK_OP_UNUSED219            219
#define DUK_OP_UNUSED220            220
#define DUK_OP_UNUSED221            221
#define DUK_OP_UNUSED222            222
#define DUK_OP_UNUSED223            223
#define DUK_OP_UNUSED224            224
#define DUK_OP_UNUSED225            225
#define DUK_OP_UNUSED226            226
#define DUK_OP_UNUSED227            227
#define DUK_OP_UNUSED228            228
#define DUK_OP_UNUSED229            229
#define DUK_OP_UNUSED230            230
#define DUK_OP_UNUSED231            231
#define DUK_OP_UNUSED232            232
#define DUK_OP_UNUSED233            233
#define DUK_OP_UNUSED234            234
#define DUK_OP_UNUSED235            235
#define DUK_OP_UNUSED236            236
#define DUK_OP_UNUSED237            237
#define DUK_OP_UNUSED238            238
#define DUK_OP_UNUSED239            239
#define DUK_OP_UNUSED240            240
#define DUK_OP_UNUSED241            241
#define DUK_OP_UNUSED242            242
#define DUK_OP_UNUSED243            243
#define DUK_OP_UNUSED244            244
#define DUK_OP_UNUSED245            245
#define DUK_OP_UNUSED246            246
#define DUK_OP_UNUSED247            247
#define DUK_OP_UNUSED248            248
#define DUK_OP_UNUSED249            249
#define DUK_OP_UNUSED250            250
#define DUK_OP_UNUSED251            251
#define DUK_OP_UNUSED252            252
#define DUK_OP_UNUSED253            253
#define DUK_OP_UNUSED254            254
#define DUK_OP_UNUSED255            255
#define DUK_OP_NONE                 256  /* dummy value used as marker (doesn't fit in 8-bit field) */

/* XXX: Allocate flags from opcode field?  Would take 16 opcode slots
 * but avoids shuffling in more cases.  Maybe not worth it.
 */
/* DUK_OP_TRYCATCH flags in A. */
#define DUK_BC_TRYCATCH_FLAG_HAVE_CATCH     (1U << 0)
#define DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY   (1U << 1)
#define DUK_BC_TRYCATCH_FLAG_CATCH_BINDING  (1U << 2)
#define DUK_BC_TRYCATCH_FLAG_WITH_BINDING   (1U << 3)

/* DUK_OP_DECLVAR flags in A; bottom bits are reserved for propdesc flags
 * (DUK_PROPDESC_FLAG_XXX).
 */
#define DUK_BC_DECLVAR_FLAG_FUNC_DECL       (1U << 4)  /* function declaration */

/* DUK_OP_CALLn flags, part of opcode field.  Three lowest bits must match
 * DUK_CALL_FLAG_xxx directly.
 */
#define DUK_BC_CALL_FLAG_TAILCALL           (1U << 0)
#define DUK_BC_CALL_FLAG_CONSTRUCT          (1U << 1)
#define DUK_BC_CALL_FLAG_CALLED_AS_EVAL     (1U << 2)
#define DUK_BC_CALL_FLAG_INDIRECT           (1U << 3)

/* Misc constants and helper macros. */
#define DUK_BC_LDINT_BIAS           (1L << 15)
#define DUK_BC_LDINTX_SHIFT         16
#define DUK_BC_JUMP_BIAS            (1L << 23)

#endif  /* DUK_JS_BYTECODE_H_INCLUDED */
/* #include duk_lexer.h */
//#line 1 "duk_lexer.h"
/*
 *  Lexer defines.
 */

#if !defined(DUK_LEXER_H_INCLUDED)
#define DUK_LEXER_H_INCLUDED

typedef void (*duk_re_range_callback)(void *user, duk_codepoint_t r1, duk_codepoint_t r2, duk_bool_t direct);

/*
 *  A token is interpreted as any possible production of InputElementDiv
 *  and InputElementRegExp, see E5 Section 7 in its entirety.  Note that
 *  the E5 "Token" production does not cover all actual tokens of the
 *  language (which is explicitly stated in the specification, Section 7.5).
 *  Null and boolean literals are defined as part of both ReservedWord
 *  (E5 Section 7.6.1) and Literal (E5 Section 7.8) productions.  Here,
 *  null and boolean values have literal tokens, and are not reserved
 *  words.
 *
 *  Decimal literal negative/positive sign is -not- part of DUK_TOK_NUMBER.
 *  The number tokens always have a non-negative value.  The unary minus
 *  operator in "-1.0" is optimized during compilation to yield a single
 *  negative constant.
 *
 *  Token numbering is free except that reserved words are required to be
 *  in a continuous range and in a particular order.  See genstrings.py.
 */

#define DUK_LEXER_INITCTX(ctx)        duk_lexer_initctx((ctx))

#define DUK_LEXER_SETPOINT(ctx,pt)    duk_lexer_setpoint((ctx), (pt))

#define DUK_LEXER_GETPOINT(ctx,pt)    duk_lexer_getpoint((ctx), (pt))

/* Currently 6 characters of lookup are actually needed (duk_lexer.c). */
#define DUK_LEXER_WINDOW_SIZE                     6
#if defined(DUK_USE_LEXER_SLIDING_WINDOW)
#define DUK_LEXER_BUFFER_SIZE                     64
#endif

#define DUK_TOK_MINVAL                            0

/* returned after EOF (infinite amount) */
#define DUK_TOK_EOF                               0

/* identifier names (E5 Section 7.6) */
#define DUK_TOK_IDENTIFIER                        1

/* reserved words: keywords */
#define DUK_TOK_START_RESERVED                    2
#define DUK_TOK_BREAK                             2
#define DUK_TOK_CASE                              3
#define DUK_TOK_CATCH                             4
#define DUK_TOK_CONTINUE                          5
#define DUK_TOK_DEBUGGER                          6
#define DUK_TOK_DEFAULT                           7
#define DUK_TOK_DELETE                            8
#define DUK_TOK_DO                                9
#define DUK_TOK_ELSE                              10
#define DUK_TOK_FINALLY                           11
#define DUK_TOK_FOR                               12
#define DUK_TOK_FUNCTION                          13
#define DUK_TOK_IF                                14
#define DUK_TOK_IN                                15
#define DUK_TOK_INSTANCEOF                        16
#define DUK_TOK_NEW                               17
#define DUK_TOK_RETURN                            18
#define DUK_TOK_SWITCH                            19
#define DUK_TOK_THIS                              20
#define DUK_TOK_THROW                             21
#define DUK_TOK_TRY                               22
#define DUK_TOK_TYPEOF                            23
#define DUK_TOK_VAR                               24
#define DUK_TOK_CONST                             25
#define DUK_TOK_VOID                              26
#define DUK_TOK_WHILE                             27
#define DUK_TOK_WITH                              28

/* reserved words: future reserved words */
#define DUK_TOK_CLASS                             29
#define DUK_TOK_ENUM                              30
#define DUK_TOK_EXPORT                            31
#define DUK_TOK_EXTENDS                           32
#define DUK_TOK_IMPORT                            33
#define DUK_TOK_SUPER                             34

/* "null", "true", and "false" are always reserved words.
 * Note that "get" and "set" are not!
 */
#define DUK_TOK_NULL                              35
#define DUK_TOK_TRUE                              36
#define DUK_TOK_FALSE                             37

/* reserved words: additional future reserved words in strict mode */
#define DUK_TOK_START_STRICT_RESERVED             38  /* inclusive */
#define DUK_TOK_IMPLEMENTS                        38
#define DUK_TOK_INTERFACE                         39
#define DUK_TOK_LET                               40
#define DUK_TOK_PACKAGE                           41
#define DUK_TOK_PRIVATE                           42
#define DUK_TOK_PROTECTED                         43
#define DUK_TOK_PUBLIC                            44
#define DUK_TOK_STATIC                            45
#define DUK_TOK_YIELD                             46

#define DUK_TOK_END_RESERVED                      47  /* exclusive */

/* "get" and "set" are tokens but NOT ReservedWords.  They are currently
 * parsed and identifiers and these defines are actually now unused.
 */
#define DUK_TOK_GET                               47
#define DUK_TOK_SET                               48

/* punctuators (unlike the spec, also includes "/" and "/=") */
#define DUK_TOK_LCURLY                            49
#define DUK_TOK_RCURLY                            50
#define DUK_TOK_LBRACKET                          51
#define DUK_TOK_RBRACKET                          52
#define DUK_TOK_LPAREN                            53
#define DUK_TOK_RPAREN                            54
#define DUK_TOK_PERIOD                            55
#define DUK_TOK_SEMICOLON                         56
#define DUK_TOK_COMMA                             57
#define DUK_TOK_LT                                58
#define DUK_TOK_GT                                59
#define DUK_TOK_LE                                60
#define DUK_TOK_GE                                61
#define DUK_TOK_EQ                                62
#define DUK_TOK_NEQ                               63
#define DUK_TOK_SEQ                               64
#define DUK_TOK_SNEQ                              65
#define DUK_TOK_ADD                               66
#define DUK_TOK_SUB                               67
#define DUK_TOK_MUL                               68
#define DUK_TOK_DIV                               69
#define DUK_TOK_MOD                               70
#define DUK_TOK_EXP                               71
#define DUK_TOK_INCREMENT                         72
#define DUK_TOK_DECREMENT                         73
#define DUK_TOK_ALSHIFT                           74   /* named "arithmetic" because result is signed */
#define DUK_TOK_ARSHIFT                           75
#define DUK_TOK_RSHIFT                            76
#define DUK_TOK_BAND                              77
#define DUK_TOK_BOR                               78
#define DUK_TOK_BXOR                              79
#define DUK_TOK_LNOT                              80
#define DUK_TOK_BNOT                              81
#define DUK_TOK_LAND                              82
#define DUK_TOK_LOR                               83
#define DUK_TOK_QUESTION                          84
#define DUK_TOK_COLON                             85
#define DUK_TOK_EQUALSIGN                         86
#define DUK_TOK_ADD_EQ                            87
#define DUK_TOK_SUB_EQ                            88
#define DUK_TOK_MUL_EQ                            89
#define DUK_TOK_DIV_EQ                            90
#define DUK_TOK_MOD_EQ                            91
#define DUK_TOK_EXP_EQ                            92
#define DUK_TOK_ALSHIFT_EQ                        93
#define DUK_TOK_ARSHIFT_EQ                        94
#define DUK_TOK_RSHIFT_EQ                         95
#define DUK_TOK_BAND_EQ                           96
#define DUK_TOK_BOR_EQ                            97
#define DUK_TOK_BXOR_EQ                           98

/* literals (E5 Section 7.8), except null, true, false, which are treated
 * like reserved words (above).
 */
#define DUK_TOK_NUMBER                            99
#define DUK_TOK_STRING                            100
#define DUK_TOK_REGEXP                            101

#define DUK_TOK_MAXVAL                            101  /* inclusive */

#define DUK_TOK_INVALID                           DUK_SMALL_UINT_MAX

/* Convert heap string index to a token (reserved words) */
#define DUK_STRIDX_TO_TOK(x)                        ((x) - DUK_STRIDX_START_RESERVED + DUK_TOK_START_RESERVED)

/* Sanity check */
#if (DUK_TOK_MAXVAL > 255)
#error DUK_TOK_MAXVAL too large, code assumes it fits into 8 bits
#endif

/* Sanity checks for string and token defines */
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_BREAK) != DUK_TOK_BREAK)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_CASE) != DUK_TOK_CASE)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_CATCH) != DUK_TOK_CATCH)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_CONTINUE) != DUK_TOK_CONTINUE)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_DEBUGGER) != DUK_TOK_DEBUGGER)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_DEFAULT) != DUK_TOK_DEFAULT)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_DELETE) != DUK_TOK_DELETE)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_DO) != DUK_TOK_DO)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_ELSE) != DUK_TOK_ELSE)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_FINALLY) != DUK_TOK_FINALLY)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_FOR) != DUK_TOK_FOR)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_LC_FUNCTION) != DUK_TOK_FUNCTION)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_IF) != DUK_TOK_IF)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_IN) != DUK_TOK_IN)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_INSTANCEOF) != DUK_TOK_INSTANCEOF)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_NEW) != DUK_TOK_NEW)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_RETURN) != DUK_TOK_RETURN)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_SWITCH) != DUK_TOK_SWITCH)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_THIS) != DUK_TOK_THIS)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_THROW) != DUK_TOK_THROW)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_TRY) != DUK_TOK_TRY)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_TYPEOF) != DUK_TOK_TYPEOF)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_VAR) != DUK_TOK_VAR)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_VOID) != DUK_TOK_VOID)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_WHILE) != DUK_TOK_WHILE)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_WITH) != DUK_TOK_WITH)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_CLASS) != DUK_TOK_CLASS)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_CONST) != DUK_TOK_CONST)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_ENUM) != DUK_TOK_ENUM)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_EXPORT) != DUK_TOK_EXPORT)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_EXTENDS) != DUK_TOK_EXTENDS)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_IMPORT) != DUK_TOK_IMPORT)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_SUPER) != DUK_TOK_SUPER)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_LC_NULL) != DUK_TOK_NULL)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_TRUE) != DUK_TOK_TRUE)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_FALSE) != DUK_TOK_FALSE)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_IMPLEMENTS) != DUK_TOK_IMPLEMENTS)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_INTERFACE) != DUK_TOK_INTERFACE)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_LET) != DUK_TOK_LET)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_PACKAGE) != DUK_TOK_PACKAGE)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_PRIVATE) != DUK_TOK_PRIVATE)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_PROTECTED) != DUK_TOK_PROTECTED)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_PUBLIC) != DUK_TOK_PUBLIC)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_STATIC) != DUK_TOK_STATIC)
#error mismatch in token defines
#endif
#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_YIELD) != DUK_TOK_YIELD)
#error mismatch in token defines
#endif

/* Regexp tokens */
#define DUK_RETOK_EOF                              0
#define DUK_RETOK_DISJUNCTION                      1
#define DUK_RETOK_QUANTIFIER                       2
#define DUK_RETOK_ASSERT_START                     3
#define DUK_RETOK_ASSERT_END                       4
#define DUK_RETOK_ASSERT_WORD_BOUNDARY             5
#define DUK_RETOK_ASSERT_NOT_WORD_BOUNDARY         6
#define DUK_RETOK_ASSERT_START_POS_LOOKAHEAD       7
#define DUK_RETOK_ASSERT_START_NEG_LOOKAHEAD       8
#define DUK_RETOK_ATOM_PERIOD                      9
#define DUK_RETOK_ATOM_CHAR                        10
#define DUK_RETOK_ATOM_DIGIT                       11  /* assumptions in regexp compiler */
#define DUK_RETOK_ATOM_NOT_DIGIT                   12  /* -""- */
#define DUK_RETOK_ATOM_WHITE                       13  /* -""- */
#define DUK_RETOK_ATOM_NOT_WHITE                   14  /* -""- */
#define DUK_RETOK_ATOM_WORD_CHAR                   15  /* -""- */
#define DUK_RETOK_ATOM_NOT_WORD_CHAR               16  /* -""- */
#define DUK_RETOK_ATOM_BACKREFERENCE               17
#define DUK_RETOK_ATOM_START_CAPTURE_GROUP         18
#define DUK_RETOK_ATOM_START_NONCAPTURE_GROUP      19
#define DUK_RETOK_ATOM_START_CHARCLASS             20
#define DUK_RETOK_ATOM_START_CHARCLASS_INVERTED    21
#define DUK_RETOK_ATOM_END_GROUP                   22

/* Constants for duk_lexer_ctx.buf. */
#define DUK_LEXER_TEMP_BUF_LIMIT                   256

/* A token value.  Can be memcpy()'d, but note that slot1/slot2 values are on the valstack.
 * Some fields (like num, str1, str2) are only valid for specific token types and may have
 * stale values otherwise.
 */
struct duk_token {
    duk_small_uint_t t;           /* token type (with reserved word identification) */
    duk_small_uint_t t_nores;     /* token type (with reserved words as DUK_TOK_IDENTIFER) */
    duk_double_t num;             /* numeric value of token */
    duk_hstring *str1;            /* string 1 of token (borrowed, stored to ctx->slot1_idx) */
    duk_hstring *str2;            /* string 2 of token (borrowed, stored to ctx->slot2_idx) */
    duk_size_t start_offset;      /* start byte offset of token in lexer input */
    duk_int_t start_line;         /* start line of token (first char) */
    duk_int_t num_escapes;        /* number of escapes and line continuations (for directive prologue) */
    duk_bool_t lineterm;          /* token was preceded by a lineterm */
    duk_bool_t allow_auto_semi;   /* token allows automatic semicolon insertion (eof or preceded by newline) */
};

#define DUK_RE_QUANTIFIER_INFINITE         ((duk_uint32_t) 0xffffffffUL)

/* A regexp token value. */
struct duk_re_token {
    duk_small_uint_t t;          /* token type */
    duk_small_uint_t greedy;
    duk_uint32_t num;            /* numeric value (character, count) */
    duk_uint32_t qmin;
    duk_uint32_t qmax;
};

/* A structure for 'snapshotting' a point for rewinding */
struct duk_lexer_point {
    duk_size_t offset;
    duk_int_t line;
};

/* Lexer codepoint with additional info like offset/line number */
struct duk_lexer_codepoint {
    duk_codepoint_t codepoint;
    duk_size_t offset;
    duk_int_t line;
};

/* Lexer context.  Same context is used for ECMAScript and Regexp parsing. */
struct duk_lexer_ctx {
#if defined(DUK_USE_LEXER_SLIDING_WINDOW)
    duk_lexer_codepoint *window; /* unicode code points, window[0] is always next, points to 'buffer' */
    duk_lexer_codepoint buffer[DUK_LEXER_BUFFER_SIZE];
#else
    duk_lexer_codepoint window[DUK_LEXER_WINDOW_SIZE]; /* unicode code points, window[0] is always next */
#endif

    duk_hthread *thr;                              /* thread; minimizes argument passing */

    const duk_uint8_t *input;                      /* input string (may be a user pointer) */
    duk_size_t input_length;                       /* input byte length */
    duk_size_t input_offset;                       /* input offset for window leading edge (not window[0]) */
    duk_int_t input_line;                          /* input linenumber at input_offset (not window[0]), init to 1 */

    duk_idx_t slot1_idx;                           /* valstack slot for 1st token value */
    duk_idx_t slot2_idx;                           /* valstack slot for 2nd token value */
    duk_idx_t buf_idx;                             /* valstack slot for temp buffer */
    duk_hbuffer_dynamic *buf;                      /* temp accumulation buffer */
    duk_bufwriter_ctx bw;                          /* bufwriter for temp accumulation */

    duk_int_t token_count;                         /* number of tokens parsed */
    duk_int_t token_limit;                         /* maximum token count before error (sanity backstop) */

    duk_small_uint_t flags;                        /* lexer flags, use compiler flag defines for now */
};

/*
 *  Prototypes
 */

DUK_INTERNAL_DECL void duk_lexer_initctx(duk_lexer_ctx *lex_ctx);

DUK_INTERNAL_DECL void duk_lexer_getpoint(duk_lexer_ctx *lex_ctx, duk_lexer_point *pt);
DUK_INTERNAL_DECL void duk_lexer_setpoint(duk_lexer_ctx *lex_ctx, duk_lexer_point *pt);

DUK_INTERNAL_DECL
void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx,
                                      duk_token *out_token,
                                      duk_bool_t strict_mode,
                                      duk_bool_t regexp_mode);
#if defined(DUK_USE_REGEXP_SUPPORT)
DUK_INTERNAL_DECL void duk_lexer_parse_re_token(duk_lexer_ctx *lex_ctx, duk_re_token *out_token);
DUK_INTERNAL_DECL void duk_lexer_parse_re_ranges(duk_lexer_ctx *lex_ctx, duk_re_range_callback gen_range, void *userdata);
#endif  /* DUK_USE_REGEXP_SUPPORT */

#endif  /* DUK_LEXER_H_INCLUDED */
/* #include duk_js_compiler.h */
//#line 1 "duk_js_compiler.h"
/*
 *  ECMAScript compiler.
 */

#if !defined(DUK_JS_COMPILER_H_INCLUDED)
#define DUK_JS_COMPILER_H_INCLUDED

/* ECMAScript compiler limits */
#define DUK_COMPILER_TOKEN_LIMIT           100000000L  /* 1e8: protects against deeply nested inner functions */

/* maximum loopcount for peephole optimization */
#define DUK_COMPILER_PEEPHOLE_MAXITER      3

/* maximum bytecode length in instructions */
#define DUK_COMPILER_MAX_BYTECODE_LENGTH   (256L * 1024L * 1024L)  /* 1 GB */

/*
 *  Compiler intermediate values
 *
 *  Intermediate values describe either plain values (e.g. strings or
 *  numbers) or binary operations which have not yet been coerced into
 *  either a left-hand-side or right-hand-side role (e.g. object property).
 */

#define DUK_IVAL_NONE          0   /* no value */
#define DUK_IVAL_PLAIN         1   /* register, constant, or value */
#define DUK_IVAL_ARITH         2   /* binary arithmetic; DUK_OP_ADD, DUK_OP_EQ, other binary ops */
#define DUK_IVAL_PROP          3   /* property access */
#define DUK_IVAL_VAR           4   /* variable access */

#define DUK_ISPEC_NONE         0   /* no value */
#define DUK_ISPEC_VALUE        1   /* value resides in 'valstack_idx' */
#define DUK_ISPEC_REGCONST     2   /* value resides in a register or constant */

/* Bit mask which indicates that a regconst is a constant instead of a register.
 * Chosen so that when a regconst is cast to duk_int32_t, all consts are
 * negative values.
 */
#define DUK_REGCONST_CONST_MARKER    DUK_INT32_MIN  /* = -0x80000000 */

/* Type to represent a reg/const reference during compilation, with <0
 * indicating a constant.  Some call sites also use -1 to indicate 'none'.
 */
typedef duk_int32_t duk_regconst_t;

typedef struct {
    duk_small_uint_t t;          /* DUK_ISPEC_XXX */
    duk_regconst_t regconst;
    duk_idx_t valstack_idx;      /* always set; points to a reserved valstack slot */
} duk_ispec;

typedef struct {
    /*
     *  PLAIN: x1
     *  ARITH: x1 <op> x2
     *  PROP: x1.x2
     *  VAR: x1 (name)
     */

    /* XXX: can be optimized for smaller footprint esp. on 32-bit environments */
    duk_small_uint_t t;          /* DUK_IVAL_XXX */
    duk_small_uint_t op;         /* bytecode opcode for binary ops */
    duk_ispec x1;
    duk_ispec x2;
} duk_ivalue;

/*
 *  Bytecode instruction representation during compilation
 *
 *  Contains the actual instruction and (optionally) debug info.
 */

struct duk_compiler_instr {
    duk_instr_t ins;
#if defined(DUK_USE_PC2LINE)
    duk_uint32_t line;
#endif
};

/*
 *  Compiler state
 */

#define DUK_LABEL_FLAG_ALLOW_BREAK       (1U << 0)
#define DUK_LABEL_FLAG_ALLOW_CONTINUE    (1U << 1)

#define DUK_DECL_TYPE_VAR                0
#define DUK_DECL_TYPE_FUNC               1

/* XXX: optimize to 16 bytes */
typedef struct {
    duk_small_uint_t flags;
    duk_int_t label_id;          /* numeric label_id (-1 reserved as marker) */
    duk_hstring *h_label;        /* borrowed label name */
    duk_int_t catch_depth;       /* catch depth at point of definition */
    duk_int_t pc_label;          /* pc of label statement:
                                  * pc+1: break jump site
                                  * pc+2: continue jump site
                                  */

    /* Fast jumps (which avoid longjmp) jump directly to the jump sites
     * which are always known even while the iteration/switch statement
     * is still being parsed.  A final peephole pass "straightens out"
     * the jumps.
     */
} duk_labelinfo;

/* Compiling state of one function, eventually converted to duk_hcompfunc */
struct duk_compiler_func {
    /* These pointers are at the start of the struct so that they pack
     * nicely.  Mixing pointers and integer values is bad on some
     * platforms (e.g. if int is 32 bits and pointers are 64 bits).
     */

    duk_bufwriter_ctx bw_code;          /* bufwriter for code */

    duk_hstring *h_name;                /* function name (borrowed reference), ends up in _name */
    /* h_code: held in bw_code */
    duk_hobject *h_consts;              /* array */
    duk_hobject *h_funcs;               /* array of function templates: [func1, offset1, line1, func2, offset2, line2]
                                         * offset/line points to closing brace to allow skipping on pass 2
                                         */
    duk_hobject *h_decls;               /* array of declarations: [ name1, val1, name2, val2, ... ]
                                         * valN = (typeN) | (fnum << 8), where fnum is inner func number (0 for vars)
                                         * record function and variable declarations in pass 1
                                         */
    duk_hobject *h_labelnames;          /* array of active label names */
    duk_hbuffer_dynamic *h_labelinfos;  /* C array of duk_labelinfo */
    duk_hobject *h_argnames;            /* array of formal argument names (-> _Formals) */
    duk_hobject *h_varmap;              /* variable map for pass 2 (identifier -> register number or null (unmapped)) */

    /* Value stack indices for tracking objects. */
    /* code_idx: not needed */
    duk_idx_t consts_idx;
    duk_idx_t funcs_idx;
    duk_idx_t decls_idx;
    duk_idx_t labelnames_idx;
    duk_idx_t labelinfos_idx;
    duk_idx_t argnames_idx;
    duk_idx_t varmap_idx;

    /* Temp reg handling. */
    duk_regconst_t temp_first;           /* first register that is a temporary (below: variables) */
    duk_regconst_t temp_next;            /* next temporary register to allocate */
    duk_regconst_t temp_max;             /* highest value of temp_reg (temp_max - 1 is highest used reg) */

    /* Shuffle registers if large number of regs/consts. */
    duk_regconst_t shuffle1;
    duk_regconst_t shuffle2;
    duk_regconst_t shuffle3;

    /* Stats for current expression being parsed. */
    duk_int_t nud_count;
    duk_int_t led_count;
    duk_int_t paren_level;              /* parenthesis count, 0 = top level */
    duk_bool_t expr_lhs;                /* expression is left-hand-side compatible */
    duk_bool_t allow_in;                /* current paren level allows 'in' token */

    /* Misc. */
    duk_int_t stmt_next;                /* statement id allocation (running counter) */
    duk_int_t label_next;               /* label id allocation (running counter) */
    duk_int_t catch_depth;              /* catch stack depth */
    duk_int_t with_depth;               /* with stack depth (affects identifier lookups) */
    duk_int_t fnum_next;                /* inner function numbering */
    duk_int_t num_formals;              /* number of formal arguments */
    duk_regconst_t reg_stmt_value;      /* register for writing value of 'non-empty' statements (global or eval code), -1 is marker */
#if defined(DUK_USE_DEBUGGER_SUPPORT)
    duk_int_t min_line;                 /* XXX: typing (duk_hcompfunc has duk_uint32_t) */
    duk_int_t max_line;
#endif

    /* Status booleans. */
    duk_uint8_t is_function;             /* is an actual function (not global/eval code) */
    duk_uint8_t is_eval;                 /* is eval code */
    duk_uint8_t is_global;               /* is global code */
    duk_uint8_t is_namebinding;          /* needs a name binding */
    duk_uint8_t is_constructable;        /* result is constructable */
    duk_uint8_t is_setget;               /* is a setter/getter */
    duk_uint8_t is_strict;               /* function is strict */
    duk_uint8_t is_notail;               /* function must not be tail called */
    duk_uint8_t in_directive_prologue;   /* parsing in "directive prologue", recognize directives */
    duk_uint8_t in_scanning;             /* parsing in "scanning" phase (first pass) */
    duk_uint8_t may_direct_eval;         /* function may call direct eval */
    duk_uint8_t id_access_arguments;     /* function refers to 'arguments' identifier */
    duk_uint8_t id_access_slow;          /* function makes one or more slow path accesses that won't match own static variables */
    duk_uint8_t id_access_slow_own;      /* function makes one or more slow path accesses that may match own static variables */
    duk_uint8_t is_arguments_shadowed;   /* argument/function declaration shadows 'arguments' */
    duk_uint8_t needs_shuffle;           /* function needs shuffle registers */
    duk_uint8_t reject_regexp_in_adv;    /* reject RegExp literal on next advance() call; needed for handling IdentifierName productions */
    duk_uint8_t allow_regexp_in_adv;     /* allow RegExp literal on next advance() call */
};

struct duk_compiler_ctx {
    duk_hthread *thr;

    /* filename being compiled (ends up in functions' '_filename' property) */
    duk_hstring *h_filename;            /* borrowed reference */

    /* lexing (tokenization) state (contains two valstack slot indices) */
    duk_lexer_ctx lex;

    /* current and previous token for parsing */
    duk_token prev_token;
    duk_token curr_token;
    duk_idx_t tok11_idx;                /* curr_token slot1 (matches 'lex' slot1_idx) */
    duk_idx_t tok12_idx;                /* curr_token slot2 (matches 'lex' slot2_idx) */
    duk_idx_t tok21_idx;                /* prev_token slot1 */
    duk_idx_t tok22_idx;                /* prev_token slot2 */

    /* recursion limit */
    duk_int_t recursion_depth;
    duk_int_t recursion_limit;

    /* code emission temporary */
    duk_int_t emit_jumpslot_pc;

    /* current function being compiled (embedded instead of pointer for more compact access) */
    duk_compiler_func curr_func;
};

/*
 *  Prototypes
 */

DUK_INTERNAL_DECL void duk_js_compile(duk_hthread *thr, const duk_uint8_t *src_buffer, duk_size_t src_length, duk_small_uint_t flags);

#endif  /* DUK_JS_COMPILER_H_INCLUDED */
/* #include duk_regexp.h */
//#line 1 "duk_regexp.h"
/*
 *  Regular expression structs, constants, and bytecode defines.
 */

#if !defined(DUK_REGEXP_H_INCLUDED)
#define DUK_REGEXP_H_INCLUDED

/* maximum bytecode copies for {n,m} quantifiers */
#define DUK_RE_MAX_ATOM_COPIES             1000

/* regexp compilation limits */
#define DUK_RE_COMPILE_TOKEN_LIMIT         100000000L   /* 1e8 */

/* regexp execution limits */
#define DUK_RE_EXECUTE_STEPS_LIMIT         1000000000L  /* 1e9 */

/* regexp opcodes */
#define DUK_REOP_MATCH                     1
#define DUK_REOP_CHAR                      2
#define DUK_REOP_PERIOD                    3
#define DUK_REOP_RANGES                    4
#define DUK_REOP_INVRANGES                 5
#define DUK_REOP_JUMP                      6
#define DUK_REOP_SPLIT1                    7
#define DUK_REOP_SPLIT2                    8
#define DUK_REOP_SQMINIMAL                 9
#define DUK_REOP_SQGREEDY                  10
#define DUK_REOP_SAVE                      11
#define DUK_REOP_WIPERANGE                 12
#define DUK_REOP_LOOKPOS                   13
#define DUK_REOP_LOOKNEG                   14
#define DUK_REOP_BACKREFERENCE             15
#define DUK_REOP_ASSERT_START              16
#define DUK_REOP_ASSERT_END                17
#define DUK_REOP_ASSERT_WORD_BOUNDARY      18
#define DUK_REOP_ASSERT_NOT_WORD_BOUNDARY  19

/* flags */
#define DUK_RE_FLAG_GLOBAL                 (1U << 0)
#define DUK_RE_FLAG_IGNORE_CASE            (1U << 1)
#define DUK_RE_FLAG_MULTILINE              (1U << 2)

struct duk_re_matcher_ctx {
    duk_hthread *thr;

    duk_uint32_t re_flags;
    const duk_uint8_t *input;
    const duk_uint8_t *input_end;
    const duk_uint8_t *bytecode;
    const duk_uint8_t *bytecode_end;
    const duk_uint8_t **saved;  /* allocated from valstack (fixed buffer) */
    duk_uint32_t nsaved;
    duk_uint32_t recursion_depth;
    duk_uint32_t recursion_limit;
    duk_uint32_t steps_count;
    duk_uint32_t steps_limit;
};

struct duk_re_compiler_ctx {
    duk_hthread *thr;

    duk_uint32_t re_flags;
    duk_lexer_ctx lex;
    duk_re_token curr_token;
    duk_bufwriter_ctx bw;
    duk_uint32_t captures;  /* highest capture number emitted so far (used as: ++captures) */
    duk_uint32_t highest_backref;
    duk_uint32_t recursion_depth;
    duk_uint32_t recursion_limit;
    duk_uint32_t nranges;  /* internal temporary value, used for char classes */
};

/*
 *  Prototypes
 */

#if defined(DUK_USE_REGEXP_SUPPORT)
DUK_INTERNAL_DECL void duk_regexp_compile(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_regexp_create_instance(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_regexp_match(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_regexp_match_force_global(duk_hthread *thr);  /* hacky helper for String.prototype.split() */
#endif

#endif  /* DUK_REGEXP_H_INCLUDED */
/* #include duk_heaphdr.h */
//#line 1 "duk_heaphdr.h"
/*
 *  Heap header definition and assorted macros, including ref counting.
 *  Access all fields through the accessor macros.
 */

#if !defined(DUK_HEAPHDR_H_INCLUDED)
#define DUK_HEAPHDR_H_INCLUDED

/*
 *  Common heap header
 *
 *  All heap objects share the same flags and refcount fields.  Objects other
 *  than strings also need to have a single or double linked list pointers
 *  for insertion into the "heap allocated" list.  Strings have single linked
 *  list pointers for string table chaining.
 *
 *  Technically, 'h_refcount' must be wide enough to guarantee that it cannot
 *  wrap; otherwise objects might be freed incorrectly after wrapping.  The
 *  default refcount field is 32 bits even on 64-bit systems: while that's in
 *  theory incorrect, the Duktape heap needs to be larger than 64GB for the
 *  count to actually wrap (assuming 16-byte duk_tvals).  This is very unlikely
 *  to ever be an issue, but if it is, disabling DUK_USE_REFCOUNT32 causes
 *  Duktape to use size_t for refcounts which should always be safe.
 *
 *  Heap header size on 32-bit platforms: 8 bytes without reference counting,
 *  16 bytes with reference counting.
 *
 *  Note that 'raw' macros such as DUK_HEAPHDR_GET_REFCOUNT() are not
 *  defined without DUK_USE_REFERENCE_COUNTING, so caller must #if defined()
 *  around them.
 */

/* XXX: macro for shared header fields (avoids some padding issues) */

struct duk_heaphdr {
    duk_uint32_t h_flags;

#if defined(DUK_USE_REFERENCE_COUNTING)
#if defined(DUK_USE_ASSERTIONS)
    /* When assertions enabled, used by mark-and-sweep for refcount
     * validation.  Largest reasonable type; also detects overflows.
     */
    duk_size_t h_assert_refcount;
#endif
#if defined(DUK_USE_REFCOUNT16)
    duk_uint16_t h_refcount;
#elif defined(DUK_USE_REFCOUNT32)
    duk_uint32_t h_refcount;
#else
    duk_size_t h_refcount;
#endif
#endif  /* DUK_USE_REFERENCE_COUNTING */

#if defined(DUK_USE_HEAPPTR16)
    duk_uint16_t h_next16;
#else
    duk_heaphdr *h_next;
#endif

#if defined(DUK_USE_DOUBLE_LINKED_HEAP)
    /* refcounting requires direct heap frees, which in turn requires a dual linked heap */
#if defined(DUK_USE_HEAPPTR16)
    duk_uint16_t h_prev16;
#else
    duk_heaphdr *h_prev;
#endif
#endif

    /* When DUK_USE_HEAPPTR16 (and DUK_USE_REFCOUNT16) is in use, the
     * struct won't align nicely to 4 bytes.  This 16-bit extra field
     * is added to make the alignment clean; the field can be used by
     * heap objects when 16-bit packing is used.  This field is now
     * conditional to DUK_USE_HEAPPTR16 only, but it is intended to be
     * used with DUK_USE_REFCOUNT16 and DUK_USE_DOUBLE_LINKED_HEAP;
     * this only matter to low memory environments anyway.
     */
#if defined(DUK_USE_HEAPPTR16)
    duk_uint16_t h_extra16;
#endif
};

struct duk_heaphdr_string {
    /* 16 bits would be enough for shared heaphdr flags and duk_hstring
     * flags.  The initial parts of duk_heaphdr_string and duk_heaphdr
     * must match so changing the flags field size here would be quite
     * awkward.  However, to minimize struct size, we can pack at least
     * 16 bits of duk_hstring data into the flags field.
     */
    duk_uint32_t h_flags;

#if defined(DUK_USE_REFERENCE_COUNTING)
#if defined(DUK_USE_ASSERTIONS)
    /* When assertions enabled, used by mark-and-sweep for refcount
     * validation.  Largest reasonable type; also detects overflows.
     */
    duk_size_t h_assert_refcount;
#endif
#if defined(DUK_USE_REFCOUNT16)
    duk_uint16_t h_refcount;
    duk_uint16_t h_strextra16;  /* round out to 8 bytes */
#elif defined(DUK_USE_REFCOUNT32)
    duk_uint32_t h_refcount;
#else
    duk_size_t h_refcount;
#endif
#else
    duk_uint16_t h_strextra16;
#endif  /* DUK_USE_REFERENCE_COUNTING */

    duk_hstring *h_next;
    /* No 'h_prev' pointer for strings. */
};

#define DUK_HEAPHDR_FLAGS_TYPE_MASK      0x00000003UL
#define DUK_HEAPHDR_FLAGS_FLAG_MASK      (~DUK_HEAPHDR_FLAGS_TYPE_MASK)

                                             /* 2 bits for heap type */
#define DUK_HEAPHDR_FLAGS_HEAP_START     2   /* 5 heap flags */
#define DUK_HEAPHDR_FLAGS_USER_START     7   /* 25 user flags */

#define DUK_HEAPHDR_HEAP_FLAG_NUMBER(n)  (DUK_HEAPHDR_FLAGS_HEAP_START + (n))
#define DUK_HEAPHDR_USER_FLAG_NUMBER(n)  (DUK_HEAPHDR_FLAGS_USER_START + (n))
#define DUK_HEAPHDR_HEAP_FLAG(n)         (1UL << (DUK_HEAPHDR_FLAGS_HEAP_START + (n)))
#define DUK_HEAPHDR_USER_FLAG(n)         (1UL << (DUK_HEAPHDR_FLAGS_USER_START + (n)))

#define DUK_HEAPHDR_FLAG_REACHABLE       DUK_HEAPHDR_HEAP_FLAG(0)  /* mark-and-sweep: reachable */
#define DUK_HEAPHDR_FLAG_TEMPROOT        DUK_HEAPHDR_HEAP_FLAG(1)  /* mark-and-sweep: children not processed */
#define DUK_HEAPHDR_FLAG_FINALIZABLE     DUK_HEAPHDR_HEAP_FLAG(2)  /* mark-and-sweep: finalizable (on current pass) */
#define DUK_HEAPHDR_FLAG_FINALIZED       DUK_HEAPHDR_HEAP_FLAG(3)  /* mark-and-sweep: finalized (on previous pass) */
#define DUK_HEAPHDR_FLAG_READONLY        DUK_HEAPHDR_HEAP_FLAG(4)  /* read-only object, in code section */

#define DUK_HTYPE_MIN                    0
#define DUK_HTYPE_STRING                 0
#define DUK_HTYPE_OBJECT                 1
#define DUK_HTYPE_BUFFER                 2
#define DUK_HTYPE_MAX                    2

#if defined(DUK_USE_HEAPPTR16)
#define DUK_HEAPHDR_GET_NEXT(heap,h) \
    ((duk_heaphdr *) DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (h)->h_next16))
#define DUK_HEAPHDR_SET_NEXT(heap,h,val)   do { \
        (h)->h_next16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) val); \
    } while (0)
#else
#define DUK_HEAPHDR_GET_NEXT(heap,h)  ((h)->h_next)
#define DUK_HEAPHDR_SET_NEXT(heap,h,val)   do { \
        (h)->h_next = (val); \
    } while (0)
#endif

#if defined(DUK_USE_DOUBLE_LINKED_HEAP)
#if defined(DUK_USE_HEAPPTR16)
#define DUK_HEAPHDR_GET_PREV(heap,h) \
    ((duk_heaphdr *) DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (h)->h_prev16))
#define DUK_HEAPHDR_SET_PREV(heap,h,val)   do { \
        (h)->h_prev16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (val)); \
    } while (0)
#else
#define DUK_HEAPHDR_GET_PREV(heap,h)       ((h)->h_prev)
#define DUK_HEAPHDR_SET_PREV(heap,h,val)   do { \
        (h)->h_prev = (val); \
    } while (0)
#endif
#endif

#if defined(DUK_USE_REFERENCE_COUNTING)
#define DUK_HEAPHDR_GET_REFCOUNT(h)   ((h)->h_refcount)
#define DUK_HEAPHDR_SET_REFCOUNT(h,val)  do { \
        (h)->h_refcount = (val); \
        DUK_ASSERT((h)->h_refcount == (val));  /* No truncation. */ \
    } while (0)
#define DUK_HEAPHDR_PREINC_REFCOUNT(h)  (++(h)->h_refcount)  /* result: updated refcount */
#define DUK_HEAPHDR_PREDEC_REFCOUNT(h)  (--(h)->h_refcount)  /* result: updated refcount */
#else
/* refcount macros not defined without refcounting, caller must #if defined() now */
#endif  /* DUK_USE_REFERENCE_COUNTING */

/*
 *  Note: type is treated as a field separate from flags, so some masking is
 *  involved in the macros below.
 */

#define DUK_HEAPHDR_GET_FLAGS_RAW(h)  ((h)->h_flags)
#define DUK_HEAPHDR_SET_FLAGS_RAW(h,val)  do { \
        (h)->h_flags = (val); } \
    }
#define DUK_HEAPHDR_GET_FLAGS(h)      ((h)->h_flags & DUK_HEAPHDR_FLAGS_FLAG_MASK)
#define DUK_HEAPHDR_SET_FLAGS(h,val)  do { \
        (h)->h_flags = ((h)->h_flags & ~(DUK_HEAPHDR_FLAGS_FLAG_MASK)) | (val); \
    } while (0)
#define DUK_HEAPHDR_GET_TYPE(h)       ((h)->h_flags & DUK_HEAPHDR_FLAGS_TYPE_MASK)
#define DUK_HEAPHDR_SET_TYPE(h,val)   do { \
        (h)->h_flags = ((h)->h_flags & ~(DUK_HEAPHDR_FLAGS_TYPE_MASK)) | (val); \
    } while (0)

/* Comparison for type >= DUK_HTYPE_MIN skipped; because DUK_HTYPE_MIN is zero
 * and the comparison is unsigned, it's always true and generates warnings.
 */
#define DUK_HEAPHDR_HTYPE_VALID(h)    ( \
    DUK_HEAPHDR_GET_TYPE((h)) <= DUK_HTYPE_MAX \
    )

#define DUK_HEAPHDR_SET_TYPE_AND_FLAGS(h,tval,fval)  do { \
        (h)->h_flags = ((tval) & DUK_HEAPHDR_FLAGS_TYPE_MASK) | \
                       ((fval) & DUK_HEAPHDR_FLAGS_FLAG_MASK); \
    } while (0)

#define DUK_HEAPHDR_SET_FLAG_BITS(h,bits)  do { \
        DUK_ASSERT(((bits) & ~(DUK_HEAPHDR_FLAGS_FLAG_MASK)) == 0); \
        (h)->h_flags |= (bits); \
    } while (0)

#define DUK_HEAPHDR_CLEAR_FLAG_BITS(h,bits)  do { \
        DUK_ASSERT(((bits) & ~(DUK_HEAPHDR_FLAGS_FLAG_MASK)) == 0); \
        (h)->h_flags &= ~((bits)); \
    } while (0)

#define DUK_HEAPHDR_CHECK_FLAG_BITS(h,bits)  (((h)->h_flags & (bits)) != 0)

#define DUK_HEAPHDR_SET_REACHABLE(h)      DUK_HEAPHDR_SET_FLAG_BITS((h),DUK_HEAPHDR_FLAG_REACHABLE)
#define DUK_HEAPHDR_CLEAR_REACHABLE(h)    DUK_HEAPHDR_CLEAR_FLAG_BITS((h),DUK_HEAPHDR_FLAG_REACHABLE)
#define DUK_HEAPHDR_HAS_REACHABLE(h)      DUK_HEAPHDR_CHECK_FLAG_BITS((h),DUK_HEAPHDR_FLAG_REACHABLE)

#define DUK_HEAPHDR_SET_TEMPROOT(h)       DUK_HEAPHDR_SET_FLAG_BITS((h),DUK_HEAPHDR_FLAG_TEMPROOT)
#define DUK_HEAPHDR_CLEAR_TEMPROOT(h)     DUK_HEAPHDR_CLEAR_FLAG_BITS((h),DUK_HEAPHDR_FLAG_TEMPROOT)
#define DUK_HEAPHDR_HAS_TEMPROOT(h)       DUK_HEAPHDR_CHECK_FLAG_BITS((h),DUK_HEAPHDR_FLAG_TEMPROOT)

#define DUK_HEAPHDR_SET_FINALIZABLE(h)    DUK_HEAPHDR_SET_FLAG_BITS((h),DUK_HEAPHDR_FLAG_FINALIZABLE)
#define DUK_HEAPHDR_CLEAR_FINALIZABLE(h)  DUK_HEAPHDR_CLEAR_FLAG_BITS((h),DUK_HEAPHDR_FLAG_FINALIZABLE)
#define DUK_HEAPHDR_HAS_FINALIZABLE(h)    DUK_HEAPHDR_CHECK_FLAG_BITS((h),DUK_HEAPHDR_FLAG_FINALIZABLE)

#define DUK_HEAPHDR_SET_FINALIZED(h)      DUK_HEAPHDR_SET_FLAG_BITS((h),DUK_HEAPHDR_FLAG_FINALIZED)
#define DUK_HEAPHDR_CLEAR_FINALIZED(h)    DUK_HEAPHDR_CLEAR_FLAG_BITS((h),DUK_HEAPHDR_FLAG_FINALIZED)
#define DUK_HEAPHDR_HAS_FINALIZED(h)      DUK_HEAPHDR_CHECK_FLAG_BITS((h),DUK_HEAPHDR_FLAG_FINALIZED)

#define DUK_HEAPHDR_SET_READONLY(h)       DUK_HEAPHDR_SET_FLAG_BITS((h),DUK_HEAPHDR_FLAG_READONLY)
#define DUK_HEAPHDR_CLEAR_READONLY(h)     DUK_HEAPHDR_CLEAR_FLAG_BITS((h),DUK_HEAPHDR_FLAG_READONLY)
#define DUK_HEAPHDR_HAS_READONLY(h)       DUK_HEAPHDR_CHECK_FLAG_BITS((h),DUK_HEAPHDR_FLAG_READONLY)

/* get or set a range of flags; m=first bit number, n=number of bits */
#define DUK_HEAPHDR_GET_FLAG_RANGE(h,m,n)  (((h)->h_flags >> (m)) & ((1UL << (n)) - 1UL))

#define DUK_HEAPHDR_SET_FLAG_RANGE(h,m,n,v)  do { \
        (h)->h_flags = \
            ((h)->h_flags & (~(((1UL << (n)) - 1UL) << (m)))) \
            | ((v) << (m)); \
    } while (0)

/* init pointer fields to null */
#if defined(DUK_USE_DOUBLE_LINKED_HEAP)
#define DUK_HEAPHDR_INIT_NULLS(h)       do { \
        DUK_HEAPHDR_SET_NEXT((h), (void *) NULL); \
        DUK_HEAPHDR_SET_PREV((h), (void *) NULL); \
    } while (0)
#else
#define DUK_HEAPHDR_INIT_NULLS(h)       do { \
        DUK_HEAPHDR_SET_NEXT((h), (void *) NULL); \
    } while (0)
#endif

#define DUK_HEAPHDR_STRING_INIT_NULLS(h)  do { \
        (h)->h_next = NULL; \
    } while (0)

/*
 *  Type tests
 */

/* Take advantage of the fact that for DUK_HTYPE_xxx numbers the lowest bit
 * is only set for DUK_HTYPE_OBJECT (= 1).
 */
#if 0
#define DUK_HEAPHDR_IS_OBJECT(h) (DUK_HEAPHDR_GET_TYPE((h)) == DUK_HTYPE_OBJECT)
#endif
#define DUK_HEAPHDR_IS_OBJECT(h) ((h)->h_flags & 0x01UL)
#define DUK_HEAPHDR_IS_STRING(h) (DUK_HEAPHDR_GET_TYPE((h)) == DUK_HTYPE_STRING)
#define DUK_HEAPHDR_IS_BUFFER(h) (DUK_HEAPHDR_GET_TYPE((h)) == DUK_HTYPE_BUFFER)

/*
 *  Assert helpers
 */

/* Check that prev/next links are consistent: if e.g. h->prev is != NULL,
 * h->prev->next should point back to h.
 */
#if defined(DUK_USE_ASSERTIONS)
DUK_INTERNAL_DECL void duk_heaphdr_assert_valid_subclassed(duk_heaphdr *h);
DUK_INTERNAL_DECL void duk_heaphdr_assert_links(duk_heap *heap, duk_heaphdr *h);
DUK_INTERNAL_DECL void duk_heaphdr_assert_valid(duk_heaphdr *h);
#define DUK_HEAPHDR_ASSERT_LINKS(heap,h)  do { duk_heaphdr_assert_links((heap), (h)); } while (0)
#define DUK_HEAPHDR_ASSERT_VALID(h)  do { duk_heaphdr_assert_valid((h)); } while (0)
#else
#define DUK_HEAPHDR_ASSERT_LINKS(heap,h)  do {} while (0)
#define DUK_HEAPHDR_ASSERT_VALID(h)  do {} while (0)
#endif

#endif  /* DUK_HEAPHDR_H_INCLUDED */
/* #include duk_refcount.h */
//#line 1 "duk_refcount.h"
/*
 *  Reference counting helper macros.  The macros take a thread argument
 *  and must thus always be executed in a specific thread context.  The
 *  thread argument is not really needed anymore: DECREF can operate with
 *  a heap pointer only, and INCREF needs neither.
 */

#if !defined(DUK_REFCOUNT_H_INCLUDED)
#define DUK_REFCOUNT_H_INCLUDED

#if defined(DUK_USE_REFERENCE_COUNTING)

#if defined(DUK_USE_ROM_OBJECTS)
/* With ROM objects "needs refcount update" is true when the value is
 * heap allocated and is not a ROM object.
 */
/* XXX: double evaluation for 'tv' argument. */
#define DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv) \
    (DUK_TVAL_IS_HEAP_ALLOCATED((tv)) && !DUK_HEAPHDR_HAS_READONLY(DUK_TVAL_GET_HEAPHDR((tv))))
#define DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE(h)  (!DUK_HEAPHDR_HAS_READONLY((h)))
#else  /* DUK_USE_ROM_OBJECTS */
/* Without ROM objects "needs refcount update" == is heap allocated. */
#define DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv)    DUK_TVAL_IS_HEAP_ALLOCATED((tv))
#define DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE(h)  1
#endif  /* DUK_USE_ROM_OBJECTS */

/* Fast variants, inline refcount operations except for refzero handling.
 * Can be used explicitly when speed is always more important than size.
 * For a good compiler and a single file build, these are basically the
 * same as a forced inline.
 */
#define DUK_TVAL_INCREF_FAST(thr,tv) do { \
        duk_tval *duk__tv = (tv); \
        DUK_ASSERT(duk__tv != NULL); \
        if (DUK_TVAL_NEEDS_REFCOUNT_UPDATE(duk__tv)) { \
            duk_heaphdr *duk__h = DUK_TVAL_GET_HEAPHDR(duk__tv); \
            DUK_ASSERT(duk__h != NULL); \
            DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(duk__h)); \
            DUK_HEAPHDR_PREINC_REFCOUNT(duk__h); \
            DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(duk__h) != 0);  /* No wrapping. */ \
        } \
    } while (0)
#define DUK_TVAL_DECREF_FAST(thr,tv) do { \
        duk_tval *duk__tv = (tv); \
        DUK_ASSERT(duk__tv != NULL); \
        if (DUK_TVAL_NEEDS_REFCOUNT_UPDATE(duk__tv)) { \
            duk_heaphdr *duk__h = DUK_TVAL_GET_HEAPHDR(duk__tv); \
            DUK_ASSERT(duk__h != NULL); \
            DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(duk__h)); \
            DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(duk__h) > 0); \
            if (DUK_HEAPHDR_PREDEC_REFCOUNT(duk__h) == 0) { \
                duk_heaphdr_refzero((thr), duk__h); \
            } \
        } \
    } while (0)
#define DUK_TVAL_DECREF_NORZ_FAST(thr,tv) do { \
        duk_tval *duk__tv = (tv); \
        DUK_ASSERT(duk__tv != NULL); \
        if (DUK_TVAL_NEEDS_REFCOUNT_UPDATE(duk__tv)) { \
            duk_heaphdr *duk__h = DUK_TVAL_GET_HEAPHDR(duk__tv); \
            DUK_ASSERT(duk__h != NULL); \
            DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(duk__h)); \
            DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(duk__h) > 0); \
            if (DUK_HEAPHDR_PREDEC_REFCOUNT(duk__h) == 0) { \
                duk_heaphdr_refzero_norz((thr), duk__h); \
            } \
        } \
    } while (0)
#define DUK_HEAPHDR_INCREF_FAST(thr,h) do { \
        duk_heaphdr *duk__h = (duk_heaphdr *) (h); \
        DUK_ASSERT(duk__h != NULL); \
        DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(duk__h)); \
        if (DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE(duk__h)) { \
            DUK_HEAPHDR_PREINC_REFCOUNT(duk__h); \
            DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(duk__h) != 0);  /* No wrapping. */ \
        } \
    } while (0)
#define DUK_HEAPHDR_DECREF_FAST_RAW(thr,h,rzcall,rzcast) do { \
        duk_heaphdr *duk__h = (duk_heaphdr *) (h); \
        DUK_ASSERT(duk__h != NULL); \
        DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(duk__h)); \
        DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(duk__h) > 0); \
        if (DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE(duk__h)) { \
            if (DUK_HEAPHDR_PREDEC_REFCOUNT(duk__h) == 0) { \
                (rzcall)((thr), (rzcast) duk__h); \
            } \
        } \
    } while (0)
#define DUK_HEAPHDR_DECREF_FAST(thr,h) \
    DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_heaphdr_refzero,duk_heaphdr *)
#define DUK_HEAPHDR_DECREF_NORZ_FAST(thr,h) \
    DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_heaphdr_refzero_norz,duk_heaphdr *)

/* Slow variants, call to a helper to reduce code size.
 * Can be used explicitly when size is always more important than speed.
 */
#define DUK_TVAL_INCREF_SLOW(thr,tv)         do { duk_tval_incref((tv)); } while (0)
#define DUK_TVAL_DECREF_SLOW(thr,tv)         do { duk_tval_decref((thr), (tv)); } while (0)
#define DUK_TVAL_DECREF_NORZ_SLOW(thr,tv)    do { duk_tval_decref_norz((thr), (tv)); } while (0)
#define DUK_HEAPHDR_INCREF_SLOW(thr,h)       do { duk_heaphdr_incref((duk_heaphdr *) (h)); } while (0)
#define DUK_HEAPHDR_DECREF_SLOW(thr,h)       do { duk_heaphdr_decref((thr), (duk_heaphdr *) (h)); } while (0)
#define DUK_HEAPHDR_DECREF_NORZ_SLOW(thr,h)  do { duk_heaphdr_decref_norz((thr), (duk_heaphdr *) (h)); } while (0)
#define DUK_HSTRING_INCREF_SLOW(thr,h)       do { duk_heaphdr_incref((duk_heaphdr *) (h)); } while (0)
#define DUK_HSTRING_DECREF_SLOW(thr,h)       do { duk_heaphdr_decref((thr), (duk_heaphdr *) (h)); } while (0)
#define DUK_HSTRING_DECREF_NORZ_SLOW(thr,h)  do { duk_heaphdr_decref_norz((thr), (duk_heaphdr *) (h)); } while (0)
#define DUK_HBUFFER_INCREF_SLOW(thr,h)       do { duk_heaphdr_incref((duk_heaphdr *) (h)); } while (0)
#define DUK_HBUFFER_DECREF_SLOW(thr,h)       do { duk_heaphdr_decref((thr), (duk_heaphdr *) (h)); } while (0)
#define DUK_HBUFFER_DECREF_NORZ_SLOW(thr,h)  do { duk_heaphdr_decref_norz((thr), (duk_heaphdr *) (h)); } while (0)
#define DUK_HOBJECT_INCREF_SLOW(thr,h)       do { duk_heaphdr_incref((duk_heaphdr *) (h)); } while (0)
#define DUK_HOBJECT_DECREF_SLOW(thr,h)       do { duk_heaphdr_decref((thr), (duk_heaphdr *) (h)); } while (0)
#define DUK_HOBJECT_DECREF_NORZ_SLOW(thr,h)  do { duk_heaphdr_decref_norz((thr), (duk_heaphdr *) (h)); } while (0)

/* Default variants.  Selection depends on speed/size preference.
 * Concretely: with gcc 4.8.1 -Os x64 the difference in final binary
 * is about +1kB for _FAST variants.
 */
#if defined(DUK_USE_FAST_REFCOUNT_DEFAULT)
/* XXX: It would be nice to specialize for specific duk_hobject subtypes
 * but current refzero queue handling prevents that.
 */
#define DUK_TVAL_INCREF(thr,tv)                DUK_TVAL_INCREF_FAST((thr),(tv))
#define DUK_TVAL_DECREF(thr,tv)                DUK_TVAL_DECREF_FAST((thr),(tv))
#define DUK_TVAL_DECREF_NORZ(thr,tv)           DUK_TVAL_DECREF_NORZ_FAST((thr),(tv))
#define DUK_HEAPHDR_INCREF(thr,h)              DUK_HEAPHDR_INCREF_FAST((thr),(h))
#define DUK_HEAPHDR_DECREF(thr,h)              DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_heaphdr_refzero,duk_heaphdr *)
#define DUK_HEAPHDR_DECREF_NORZ(thr,h)         DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_heaphdr_refzero_norz,duk_heaphdr *)
#define DUK_HSTRING_INCREF(thr,h)              DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) (h))
#define DUK_HSTRING_DECREF(thr,h)              DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hstring_refzero,duk_hstring *)
#define DUK_HSTRING_DECREF_NORZ(thr,h)         DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hstring_refzero,duk_hstring *)  /* no 'norz' variant */
#define DUK_HOBJECT_INCREF(thr,h)              DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) (h))
#define DUK_HOBJECT_DECREF(thr,h)              DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hobject_refzero,duk_hobject *)
#define DUK_HOBJECT_DECREF_NORZ(thr,h)         DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hobject_refzero_norz,duk_hobject *)
#define DUK_HBUFFER_INCREF(thr,h)              DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) (h))
#define DUK_HBUFFER_DECREF(thr,h)              DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hbuffer_refzero,duk_hbuffer *)
#define DUK_HBUFFER_DECREF_NORZ(thr,h)         DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hbuffer_refzero,duk_hbuffer *)  /* no 'norz' variant */
#define DUK_HCOMPFUNC_INCREF(thr,h)            DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj)
#define DUK_HCOMPFUNC_DECREF(thr,h)            DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hobject_refzero,duk_hobject *)
#define DUK_HCOMPFUNC_DECREF_NORZ(thr,h)       DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hobject_refzero_norz,duk_hobject *)
#define DUK_HNATFUNC_INCREF(thr,h)             DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj)
#define DUK_HNATFUNC_DECREF(thr,h)             DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hobject_refzero,duk_hobject *)
#define DUK_HNATFUNC_DECREF_NORZ(thr,h)        DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hobject_refzero_norz,duk_hobject *)
#define DUK_HBUFOBJ_INCREF(thr,h)              DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj)
#define DUK_HBUFOBJ_DECREF(thr,h)              DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hobject_refzero,duk_hobject *)
#define DUK_HBUFOBJ_DECREF_NORZ(thr,h)         DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hobject_refzero_norz,duk_hobject *)
#define DUK_HTHREAD_INCREF(thr,h)              DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj)
#define DUK_HTHREAD_DECREF(thr,h)              DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hobject_refzero,duk_hobject *)
#define DUK_HTHREAD_DECREF_NORZ(thr,h)         DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hobject_refzero_norz,duk_hobject *)
#else
#define DUK_TVAL_INCREF(thr,tv)                DUK_TVAL_INCREF_SLOW((thr),(tv))
#define DUK_TVAL_DECREF(thr,tv)                DUK_TVAL_DECREF_SLOW((thr),(tv))
#define DUK_TVAL_DECREF_NORZ(thr,tv)           DUK_TVAL_DECREF_NORZ_SLOW((thr),(tv))
#define DUK_HEAPHDR_INCREF(thr,h)              DUK_HEAPHDR_INCREF_SLOW((thr),(h))
#define DUK_HEAPHDR_DECREF(thr,h)              DUK_HEAPHDR_DECREF_SLOW((thr),(h))
#define DUK_HEAPHDR_DECREF_NORZ(thr,h)         DUK_HEAPHDR_DECREF_NORZ_SLOW((thr),(h))
#define DUK_HSTRING_INCREF(thr,h)              DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) (h))
#define DUK_HSTRING_DECREF(thr,h)              DUK_HSTRING_DECREF_SLOW((thr),(h))
#define DUK_HSTRING_DECREF_NORZ(thr,h)         DUK_HSTRING_DECREF_NORZ_SLOW((thr),(h))
#define DUK_HOBJECT_INCREF(thr,h)              DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) (h))
#define DUK_HOBJECT_DECREF(thr,h)              DUK_HOBJECT_DECREF_SLOW((thr),(h))
#define DUK_HOBJECT_DECREF_NORZ(thr,h)         DUK_HOBJECT_DECREF_NORZ_SLOW((thr),(h))
#define DUK_HBUFFER_INCREF(thr,h)              DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) (h))
#define DUK_HBUFFER_DECREF(thr,h)              DUK_HBUFFER_DECREF_SLOW((thr),(h))
#define DUK_HBUFFER_DECREF_NORZ(thr,h)         DUK_HBUFFER_DECREF_NORZ_SLOW((thr),(h))
#define DUK_HCOMPFUNC_INCREF(thr,h)            DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj)
#define DUK_HCOMPFUNC_DECREF(thr,h)            DUK_HOBJECT_DECREF_SLOW((thr),(duk_hobject *) &(h)->obj)
#define DUK_HCOMPFUNC_DECREF_NORZ(thr,h)       DUK_HOBJECT_DECREF_NORZ_SLOW((thr),(duk_hobject *) &(h)->obj)
#define DUK_HNATFUNC_INCREF(thr,h)             DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj)
#define DUK_HNATFUNC_DECREF(thr,h)             DUK_HOBJECT_DECREF_SLOW((thr),(duk_hobject *) &(h)->obj)
#define DUK_HNATFUNC_DECREF_NORZ(thr,h)        DUK_HOBJECT_DECREF_NORZ_SLOW((thr),(duk_hobject *) &(h)->obj)
#define DUK_HBUFOBJ_INCREF(thr,h)              DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj)
#define DUK_HBUFOBJ_DECREF(thr,h)              DUK_HOBJECT_DECREF_SLOW((thr),(duk_hobject *) &(h)->obj)
#define DUK_HBUFOB_DECREF_NORZ(thr,h)          DUK_HOBJECT_DECREF_NORZ_SLOW((thr),(duk_hobject *) &(h)->obj)
#define DUK_HTHREAD_INCREF(thr,h)              DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj)
#define DUK_HTHREAD_DECREF(thr,h)              DUK_HOBJECT_DECREF_SLOW((thr),(duk_hobject *) &(h)->obj)
#define DUK_HTHREAD_DECREF_NORZ(thr,h)         DUK_HOBJECT_DECREF_NORZ_SLOW((thr),(duk_hobject *) &(h)->obj)
#endif

/* Convenience for some situations; the above macros don't allow NULLs
 * for performance reasons.  Macros cover only actually needed cases.
 */
#define DUK_HEAPHDR_INCREF_ALLOWNULL(thr,h) do { \
        if ((h) != NULL) { \
            DUK_HEAPHDR_INCREF((thr), (duk_heaphdr *) (h)); \
        } \
    } while (0)
#define DUK_HEAPHDR_DECREF_ALLOWNULL(thr,h) do { \
        if ((h) != NULL) { \
            DUK_HEAPHDR_DECREF((thr), (duk_heaphdr *) (h)); \
        } \
    } while (0)
#define DUK_HEAPHDR_DECREF_NORZ_ALLOWNULL(thr,h) do { \
        if ((h) != NULL) { \
            DUK_HEAPHDR_DECREF_NORZ((thr), (duk_heaphdr *) (h)); \
        } \
    } while (0)
#define DUK_HOBJECT_INCREF_ALLOWNULL(thr,h) do { \
        if ((h) != NULL) { \
            DUK_HOBJECT_INCREF((thr), (h)); \
        } \
    } while (0)
#define DUK_HOBJECT_DECREF_ALLOWNULL(thr,h) do { \
        if ((h) != NULL) { \
            DUK_HOBJECT_DECREF((thr), (h)); \
        } \
    } while (0)
#define DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr,h) do { \
        if ((h) != NULL) { \
            DUK_HOBJECT_DECREF_NORZ((thr), (h)); \
        } \
    } while (0)
#define DUK_HBUFFER_INCREF_ALLOWNULL(thr,h) do { \
        if ((h) != NULL) { \
            DUK_HBUFFER_INCREF((thr), (h)); \
        } \
    } while (0)
#define DUK_HBUFFER_DECREF_ALLOWNULL(thr,h) do { \
        if ((h) != NULL) { \
            DUK_HBUFFER_DECREF((thr), (h)); \
        } \
    } while (0)
#define DUK_HBUFFER_DECREF_NORZ_ALLOWNULL(thr,h) do { \
        if ((h) != NULL) { \
            DUK_HBUFFER_DECREF_NORZ((thr), (h)); \
        } \
    } while (0)
#define DUK_HTHREAD_INCREF_ALLOWNULL(thr,h) do { \
        if ((h) != NULL) { \
            DUK_HTHREAD_INCREF((thr), (h)); \
        } \
    } while (0)
#define DUK_HTHREAD_DECREF_ALLOWNULL(thr,h) do { \
        if ((h) != NULL) { \
            DUK_HTHREAD_DECREF((thr), (h)); \
        } \
    } while (0)
#define DUK_HTHREAD_DECREF_NORZ_ALLOWNULL(thr,h) do { \
        if ((h) != NULL) { \
            DUK_HTHREAD_DECREF_NORZ((thr), (h)); \
        } \
    } while (0)

/* Called after one or more DECREF NORZ calls to handle pending side effects.
 * At present DECREF NORZ does freeing inline but doesn't execute finalizers,
 * so these macros check for pending finalizers and execute them.  The FAST
 * variant is performance critical.
 */
#if defined(DUK_USE_FINALIZER_SUPPORT)
#define DUK_REFZERO_CHECK_FAST(thr) do { \
        duk_refzero_check_fast((thr)); \
    } while (0)
#define DUK_REFZERO_CHECK_SLOW(thr) do { \
        duk_refzero_check_slow((thr)); \
    } while (0)
#else  /* DUK_USE_FINALIZER_SUPPORT */
#define DUK_REFZERO_CHECK_FAST(thr) do { } while (0)
#define DUK_REFZERO_CHECK_SLOW(thr) do { } while (0)
#endif  /* DUK_USE_FINALIZER_SUPPORT */

/*
 *  Macros to set a duk_tval and update refcount of the target (decref the
 *  old value and incref the new value if necessary).  This is both performance
 *  and footprint critical; any changes made should be measured for size/speed.
 */

#define DUK_TVAL_SET_UNDEFINED_UPDREF_ALT0(thr,tvptr_dst) do { \
        duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \
        DUK_TVAL_SET_UNDEFINED(tv__dst); \
        DUK_TVAL_DECREF((thr), &tv__tmp);  /* side effects */ \
    } while (0)

#define DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ_ALT0(thr,tvptr_dst) do { \
        duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \
        DUK_TVAL_SET_UNDEFINED(tv__dst); \
        DUK_TVAL_DECREF_NORZ((thr), &tv__tmp); \
    } while (0)

#define DUK_TVAL_SET_UNUSED_UPDREF_ALT0(thr,tvptr_dst) do { \
        duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \
        DUK_TVAL_SET_UNUSED(tv__dst); \
        DUK_TVAL_DECREF((thr), &tv__tmp);  /* side effects */ \
    } while (0)

#define DUK_TVAL_SET_NULL_UPDREF_ALT0(thr,tvptr_dst) do { \
        duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \
        DUK_TVAL_SET_NULL(tv__dst); \
        DUK_TVAL_DECREF((thr), &tv__tmp);  /* side effects */ \
    } while (0)

#define DUK_TVAL_SET_BOOLEAN_UPDREF_ALT0(thr,tvptr_dst,newval) do { \
        duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \
        DUK_TVAL_SET_BOOLEAN(tv__dst, (newval)); \
        DUK_TVAL_DECREF((thr), &tv__tmp);  /* side effects */ \
    } while (0)

#define DUK_TVAL_SET_NUMBER_UPDREF_ALT0(thr,tvptr_dst,newval) do { \
        duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \
        DUK_TVAL_SET_NUMBER(tv__dst, (newval)); \
        DUK_TVAL_DECREF((thr), &tv__tmp);  /* side effects */ \
    } while (0)
#define DUK_TVAL_SET_NUMBER_CHKFAST_UPDREF_ALT0(thr,tvptr_dst,newval) do { \
        duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \
        DUK_TVAL_SET_NUMBER_CHKFAST_FAST(tv__dst, (newval)); \
        DUK_TVAL_DECREF((thr), &tv__tmp);  /* side effects */ \
    } while (0)
#define DUK_TVAL_SET_DOUBLE_UPDREF_ALT0(thr,tvptr_dst,newval) do { \
        duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \
        DUK_TVAL_SET_DOUBLE(tv__dst, (newval)); \
        DUK_TVAL_DECREF((thr), &tv__tmp);  /* side effects */ \
    } while (0)
#define DUK_TVAL_SET_NAN_UPDREF_ALT0(thr,tvptr_dst) do { \
        duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \
        DUK_TVAL_SET_NAN(tv__dst); \
        DUK_TVAL_DECREF((thr), &tv__tmp);  /* side effects */ \
    } while (0)
#if defined(DUK_USE_FASTINT)
#define DUK_TVAL_SET_I48_UPDREF_ALT0(thr,tvptr_dst,newval) do { \
        duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \
        DUK_TVAL_SET_I48(tv__dst, (newval)); \
        DUK_TVAL_DECREF((thr), &tv__tmp);  /* side effects */ \
    } while (0)
#define DUK_TVAL_SET_I32_UPDREF_ALT0(thr,tvptr_dst,newval) do { \
        duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \
        DUK_TVAL_SET_I32(tv__dst, (newval)); \
        DUK_TVAL_DECREF((thr), &tv__tmp);  /* side effects */ \
    } while (0)
#define DUK_TVAL_SET_U32_UPDREF_ALT0(thr,tvptr_dst,newval) do { \
        duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \
        DUK_TVAL_SET_U32(tv__dst, (newval)); \
        DUK_TVAL_DECREF((thr), &tv__tmp);  /* side effects */ \
    } while (0)
#else
#define DUK_TVAL_SET_DOUBLE_CAST_UPDREF(thr,tvptr_dst,newval) \
    DUK_TVAL_SET_DOUBLE_UPDREF((thr), (tvptr_dst), (duk_double_t) (newval))
#endif  /* DUK_USE_FASTINT */

#define DUK_TVAL_SET_LIGHTFUNC_UPDREF_ALT0(thr,tvptr_dst,lf_v,lf_fp,lf_flags) do { \
        duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \
        DUK_TVAL_SET_LIGHTFUNC(tv__dst, (lf_v), (lf_fp), (lf_flags)); \
        DUK_TVAL_DECREF((thr), &tv__tmp);  /* side effects */ \
    } while (0)

#define DUK_TVAL_SET_STRING_UPDREF_ALT0(thr,tvptr_dst,newval) do { \
        duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \
        DUK_TVAL_SET_STRING(tv__dst, (newval)); \
        DUK_HSTRING_INCREF((thr), (newval)); \
        DUK_TVAL_DECREF((thr), &tv__tmp);  /* side effects */ \
    } while (0)

#define DUK_TVAL_SET_OBJECT_UPDREF_ALT0(thr,tvptr_dst,newval) do { \
        duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \
        DUK_TVAL_SET_OBJECT(tv__dst, (newval)); \
        DUK_HOBJECT_INCREF((thr), (newval)); \
        DUK_TVAL_DECREF((thr), &tv__tmp);  /* side effects */ \
    } while (0)

#define DUK_TVAL_SET_BUFFER_UPDREF_ALT0(thr,tvptr_dst,newval) do { \
        duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \
        DUK_TVAL_SET_BUFFER(tv__dst, (newval)); \
        DUK_HBUFFER_INCREF((thr), (newval)); \
        DUK_TVAL_DECREF((thr), &tv__tmp);  /* side effects */ \
    } while (0)

#define DUK_TVAL_SET_POINTER_UPDREF_ALT0(thr,tvptr_dst,newval) do { \
        duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \
        DUK_TVAL_SET_POINTER(tv__dst, (newval)); \
        DUK_TVAL_DECREF((thr), &tv__tmp);  /* side effects */ \
    } while (0)

/* DUK_TVAL_SET_TVAL_UPDREF() is used a lot in executor, property lookups,
 * etc, so it's very important for performance.  Measure when changing.
 *
 * NOTE: the source and destination duk_tval pointers may be the same, and
 * the macros MUST deal with that correctly.
 */

/* Original idiom used, minimal code size. */
#define DUK_TVAL_SET_TVAL_UPDREF_ALT0(thr,tvptr_dst,tvptr_src) do { \
        duk_tval *tv__dst, *tv__src; duk_tval tv__tmp; \
        tv__dst = (tvptr_dst); tv__src = (tvptr_src); \
        DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \
        DUK_TVAL_SET_TVAL(tv__dst, tv__src); \
        DUK_TVAL_INCREF((thr), tv__src); \
        DUK_TVAL_DECREF((thr), &tv__tmp);  /* side effects */ \
    } while (0)

/* Faster alternative: avoid making a temporary copy of tvptr_dst and use
 * fast incref/decref macros.
 */
#define DUK_TVAL_SET_TVAL_UPDREF_ALT1(thr,tvptr_dst,tvptr_src) do { \
        duk_tval *tv__dst, *tv__src; duk_heaphdr *h__obj; \
        tv__dst = (tvptr_dst); tv__src = (tvptr_src); \
        DUK_TVAL_INCREF_FAST((thr), tv__src); \
        if (DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv__dst)) { \
            h__obj = DUK_TVAL_GET_HEAPHDR(tv__dst); \
            DUK_ASSERT(h__obj != NULL); \
            DUK_TVAL_SET_TVAL(tv__dst, tv__src); \
            DUK_HEAPHDR_DECREF_FAST((thr), h__obj);  /* side effects */ \
        } else { \
            DUK_TVAL_SET_TVAL(tv__dst, tv__src); \
        } \
    } while (0)

/* XXX: no optimized variants yet */
#define DUK_TVAL_SET_UNDEFINED_UPDREF         DUK_TVAL_SET_UNDEFINED_UPDREF_ALT0
#define DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ    DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ_ALT0
#define DUK_TVAL_SET_UNUSED_UPDREF            DUK_TVAL_SET_UNUSED_UPDREF_ALT0
#define DUK_TVAL_SET_NULL_UPDREF              DUK_TVAL_SET_NULL_UPDREF_ALT0
#define DUK_TVAL_SET_BOOLEAN_UPDREF           DUK_TVAL_SET_BOOLEAN_UPDREF_ALT0
#define DUK_TVAL_SET_NUMBER_UPDREF            DUK_TVAL_SET_NUMBER_UPDREF_ALT0
#define DUK_TVAL_SET_NUMBER_CHKFAST_UPDREF    DUK_TVAL_SET_NUMBER_CHKFAST_UPDREF_ALT0
#define DUK_TVAL_SET_DOUBLE_UPDREF            DUK_TVAL_SET_DOUBLE_UPDREF_ALT0
#define DUK_TVAL_SET_NAN_UPDREF               DUK_TVAL_SET_NAN_UPDREF_ALT0
#if defined(DUK_USE_FASTINT)
#define DUK_TVAL_SET_I48_UPDREF               DUK_TVAL_SET_I48_UPDREF_ALT0
#define DUK_TVAL_SET_I32_UPDREF               DUK_TVAL_SET_I32_UPDREF_ALT0
#define DUK_TVAL_SET_U32_UPDREF               DUK_TVAL_SET_U32_UPDREF_ALT0
#else
#define DUK_TVAL_SET_I48_UPDREF               DUK_TVAL_SET_DOUBLE_CAST_UPDREF  /* XXX: fast int-to-double */
#define DUK_TVAL_SET_I32_UPDREF               DUK_TVAL_SET_DOUBLE_CAST_UPDREF
#define DUK_TVAL_SET_U32_UPDREF               DUK_TVAL_SET_DOUBLE_CAST_UPDREF
#endif  /* DUK_USE_FASTINT */
#define DUK_TVAL_SET_FASTINT_UPDREF           DUK_TVAL_SET_I48_UPDREF  /* convenience */
#define DUK_TVAL_SET_LIGHTFUNC_UPDREF         DUK_TVAL_SET_LIGHTFUNC_UPDREF_ALT0
#define DUK_TVAL_SET_STRING_UPDREF            DUK_TVAL_SET_STRING_UPDREF_ALT0
#define DUK_TVAL_SET_OBJECT_UPDREF            DUK_TVAL_SET_OBJECT_UPDREF_ALT0
#define DUK_TVAL_SET_BUFFER_UPDREF            DUK_TVAL_SET_BUFFER_UPDREF_ALT0
#define DUK_TVAL_SET_POINTER_UPDREF           DUK_TVAL_SET_POINTER_UPDREF_ALT0

#if defined(DUK_USE_FAST_REFCOUNT_DEFAULT)
/* Optimized for speed. */
#define DUK_TVAL_SET_TVAL_UPDREF              DUK_TVAL_SET_TVAL_UPDREF_ALT1
#define DUK_TVAL_SET_TVAL_UPDREF_FAST         DUK_TVAL_SET_TVAL_UPDREF_ALT1
#define DUK_TVAL_SET_TVAL_UPDREF_SLOW         DUK_TVAL_SET_TVAL_UPDREF_ALT0
#else
/* Optimized for size. */
#define DUK_TVAL_SET_TVAL_UPDREF              DUK_TVAL_SET_TVAL_UPDREF_ALT0
#define DUK_TVAL_SET_TVAL_UPDREF_FAST         DUK_TVAL_SET_TVAL_UPDREF_ALT0
#define DUK_TVAL_SET_TVAL_UPDREF_SLOW         DUK_TVAL_SET_TVAL_UPDREF_ALT0
#endif

#else  /* DUK_USE_REFERENCE_COUNTING */

#define DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv)     0
#define DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE(h)   0

#define DUK_TVAL_INCREF_FAST(thr,v)            do {} while (0) /* nop */
#define DUK_TVAL_DECREF_FAST(thr,v)            do {} while (0) /* nop */
#define DUK_TVAL_DECREF_NORZ_FAST(thr,v)       do {} while (0) /* nop */
#define DUK_TVAL_INCREF_SLOW(thr,v)            do {} while (0) /* nop */
#define DUK_TVAL_DECREF_SLOW(thr,v)            do {} while (0) /* nop */
#define DUK_TVAL_DECREF_NORZ_SLOW(thr,v)       do {} while (0) /* nop */
#define DUK_TVAL_INCREF(thr,v)                 do {} while (0) /* nop */
#define DUK_TVAL_DECREF(thr,v)                 do {} while (0) /* nop */
#define DUK_TVAL_DECREF_NORZ(thr,v)            do {} while (0) /* nop */
#define DUK_HEAPHDR_INCREF_FAST(thr,h)         do {} while (0) /* nop */
#define DUK_HEAPHDR_DECREF_FAST(thr,h)         do {} while (0) /* nop */
#define DUK_HEAPHDR_DECREF_NORZ_FAST(thr,h)    do {} while (0) /* nop */
#define DUK_HEAPHDR_INCREF_SLOW(thr,h)         do {} while (0) /* nop */
#define DUK_HEAPHDR_DECREF_SLOW(thr,h)         do {} while (0) /* nop */
#define DUK_HEAPHDR_DECREF_NORZ_SLOW(thr,h)    do {} while (0) /* nop */
#define DUK_HEAPHDR_INCREF(thr,h)              do {} while (0) /* nop */
#define DUK_HEAPHDR_DECREF(thr,h)              do {} while (0) /* nop */
#define DUK_HEAPHDR_DECREF_NORZ(thr,h)         do {} while (0) /* nop */
#define DUK_HSTRING_INCREF_FAST(thr,h)         do {} while (0) /* nop */
#define DUK_HSTRING_DECREF_FAST(thr,h)         do {} while (0) /* nop */
#define DUK_HSTRING_DECREF_NORZ_FAST(thr,h)    do {} while (0) /* nop */
#define DUK_HSTRING_INCREF_SLOW(thr,h)         do {} while (0) /* nop */
#define DUK_HSTRING_DECREF_SLOW(thr,h)         do {} while (0) /* nop */
#define DUK_HSTRING_DECREF_NORZ_SLOW(thr,h)    do {} while (0) /* nop */
#define DUK_HSTRING_INCREF(thr,h)              do {} while (0) /* nop */
#define DUK_HSTRING_DECREF(thr,h)              do {} while (0) /* nop */
#define DUK_HSTRING_DECREF_NORZ(thr,h)         do {} while (0) /* nop */
#define DUK_HOBJECT_INCREF_FAST(thr,h)         do {} while (0) /* nop */
#define DUK_HOBJECT_DECREF_FAST(thr,h)         do {} while (0) /* nop */
#define DUK_HOBJECT_DECREF_NORZ_FAST(thr,h)    do {} while (0) /* nop */
#define DUK_HOBJECT_INCREF_SLOW(thr,h)         do {} while (0) /* nop */
#define DUK_HOBJECT_DECREF_SLOW(thr,h)         do {} while (0) /* nop */
#define DUK_HOBJECT_DECREF_NORZ_SLOW(thr,h)    do {} while (0) /* nop */
#define DUK_HOBJECT_INCREF(thr,h)              do {} while (0) /* nop */
#define DUK_HOBJECT_DECREF(thr,h)              do {} while (0) /* nop */
#define DUK_HOBJECT_DECREF_NORZ(thr,h)         do {} while (0) /* nop */
#define DUK_HBUFFER_INCREF_FAST(thr,h)         do {} while (0) /* nop */
#define DUK_HBUFFER_DECREF_FAST(thr,h)         do {} while (0) /* nop */
#define DUK_HBUFFER_DECREF_NORZ_FAST(thr,h)    do {} while (0) /* nop */
#define DUK_HBUFFER_INCREF_SLOW(thr,h)         do {} while (0) /* nop */
#define DUK_HBUFFER_DECREF_SLOW(thr,h)         do {} while (0) /* nop */
#define DUK_HBUFFER_DECREF_NORZ_SLOW(thr,h)    do {} while (0) /* nop */
#define DUK_HBUFFER_INCREF(thr,h)              do {} while (0) /* nop */
#define DUK_HBUFFER_DECREF(thr,h)              do {} while (0) /* nop */
#define DUK_HBUFFER_DECREF_NORZ(thr,h)         do {} while (0) /* nop */

#define DUK_HCOMPFUNC_INCREF(thr,h)            do {} while (0) /* nop */
#define DUK_HCOMPFUNC_DECREF(thr,h)            do {} while (0) /* nop */
#define DUK_HCOMPFUNC_DECREF_NORZ(thr,h)       do {} while (0) /* nop */
#define DUK_HNATFUNC_INCREF(thr,h)             do {} while (0) /* nop */
#define DUK_HNATFUNC_DECREF(thr,h)             do {} while (0) /* nop */
#define DUK_HNATFUNC_DECREF_NORZ(thr,h)        do {} while (0) /* nop */
#define DUK_HBUFOBJ_INCREF(thr,h)              do {} while (0) /* nop */
#define DUK_HBUFOBJ_DECREF(thr,h)              do {} while (0) /* nop */
#define DUK_HBUFOBJ_DECREF_NORZ(thr,h)         do {} while (0) /* nop */
#define DUK_HTHREAD_INCREF(thr,h)              do {} while (0) /* nop */
#define DUK_HTHREAD_DECREF(thr,h)              do {} while (0) /* nop */
#define DUK_HTHREAD_DECREF_NORZ(thr,h)         do {} while (0) /* nop */
#define DUK_HOBJECT_INCREF_ALLOWNULL(thr,h)    do {} while (0) /* nop */
#define DUK_HOBJECT_DECREF_ALLOWNULL(thr,h)    do {} while (0) /* nop */
#define DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr,h)  do {} while (0) /* nop */
#define DUK_HBUFFER_INCREF_ALLOWNULL(thr,h)    do {} while (0) /* nop */
#define DUK_HBUFFER_DECREF_ALLOWNULL(thr,h)    do {} while (0) /* nop */
#define DUK_HBUFFER_DECREF_NORZ_ALLOWNULL(thr,h)  do {} while (0) /* nop */

#define DUK_REFZERO_CHECK_FAST(thr)            do {} while (0) /* nop */
#define DUK_REFZERO_CHECK_SLOW(thr)            do {} while (0) /* nop */

#define DUK_TVAL_SET_UNDEFINED_UPDREF_ALT0(thr,tvptr_dst) do { \
        duk_tval *tv__dst; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_UNDEFINED(tv__dst); \
        DUK_UNREF((thr)); \
    } while (0)

#define DUK_TVAL_SET_UNUSED_UPDREF_ALT0(thr,tvptr_dst) do { \
        duk_tval *tv__dst; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_UNUSED(tv__dst); \
        DUK_UNREF((thr)); \
    } while (0)

#define DUK_TVAL_SET_NULL_UPDREF_ALT0(thr,tvptr_dst) do { \
        duk_tval *tv__dst; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_NULL(tv__dst); \
        DUK_UNREF((thr)); \
    } while (0)

#define DUK_TVAL_SET_BOOLEAN_UPDREF_ALT0(thr,tvptr_dst,newval) do { \
        duk_tval *tv__dst; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_BOOLEAN(tv__dst, (newval)); \
        DUK_UNREF((thr)); \
    } while (0)

#define DUK_TVAL_SET_NUMBER_UPDREF_ALT0(thr,tvptr_dst,newval) do { \
        duk_tval *tv__dst; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_NUMBER(tv__dst, (newval)); \
        DUK_UNREF((thr)); \
    } while (0)
#define DUK_TVAL_SET_NUMBER_CHKFAST_UPDREF_ALT0(thr,tvptr_dst,newval) do { \
        duk_tval *tv__dst; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_NUMBER_CHKFAST_FAST(tv__dst, (newval)); \
        DUK_UNREF((thr)); \
    } while (0)
#define DUK_TVAL_SET_DOUBLE_UPDREF_ALT0(thr,tvptr_dst,newval) do { \
        duk_tval *tv__dst; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_DOUBLE(tv__dst, (newval)); \
        DUK_UNREF((thr)); \
    } while (0)
#define DUK_TVAL_SET_NAN_UPDREF_ALT0(thr,tvptr_dst) do { \
        duk_tval *tv__dst; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_NAN(tv__dst); \
        DUK_UNREF((thr)); \
    } while (0)
#if defined(DUK_USE_FASTINT)
#define DUK_TVAL_SET_I48_UPDREF_ALT0(thr,tvptr_dst,newval) do { \
        duk_tval *tv__dst; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_I48(tv__dst, (newval)); \
        DUK_UNREF((thr)); \
    } while (0)
#define DUK_TVAL_SET_I32_UPDREF_ALT0(thr,tvptr_dst,newval) do { \
        duk_tval *tv__dst; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_I32(tv__dst, (newval)); \
        DUK_UNREF((thr)); \
    } while (0)
#define DUK_TVAL_SET_U32_UPDREF_ALT0(thr,tvptr_dst,newval) do { \
        duk_tval *tv__dst; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_U32(tv__dst, (newval)); \
        DUK_UNREF((thr)); \
    } while (0)
#else
#define DUK_TVAL_SET_DOUBLE_CAST_UPDREF(thr,tvptr_dst,newval) \
    DUK_TVAL_SET_DOUBLE_UPDREF((thr), (tvptr_dst), (duk_double_t) (newval))
#endif  /* DUK_USE_FASTINT */

#define DUK_TVAL_SET_LIGHTFUNC_UPDREF_ALT0(thr,tvptr_dst,lf_v,lf_fp,lf_flags) do { \
        duk_tval *tv__dst; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_LIGHTFUNC(tv__dst, (lf_v), (lf_fp), (lf_flags)); \
        DUK_UNREF((thr)); \
    } while (0)

#define DUK_TVAL_SET_STRING_UPDREF_ALT0(thr,tvptr_dst,newval) do { \
        duk_tval *tv__dst; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_STRING(tv__dst, (newval)); \
        DUK_UNREF((thr)); \
    } while (0)

#define DUK_TVAL_SET_OBJECT_UPDREF_ALT0(thr,tvptr_dst,newval) do { \
        duk_tval *tv__dst; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_OBJECT(tv__dst, (newval)); \
        DUK_UNREF((thr)); \
    } while (0)

#define DUK_TVAL_SET_BUFFER_UPDREF_ALT0(thr,tvptr_dst,newval) do { \
        duk_tval *tv__dst; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_BUFFER(tv__dst, (newval)); \
        DUK_UNREF((thr)); \
    } while (0)

#define DUK_TVAL_SET_POINTER_UPDREF_ALT0(thr,tvptr_dst,newval) do { \
        duk_tval *tv__dst; tv__dst = (tvptr_dst); \
        DUK_TVAL_SET_POINTER(tv__dst, (newval)); \
        DUK_UNREF((thr)); \
    } while (0)

#define DUK_TVAL_SET_TVAL_UPDREF_ALT0(thr,tvptr_dst,tvptr_src) do { \
        duk_tval *tv__dst, *tv__src; \
        tv__dst = (tvptr_dst); tv__src = (tvptr_src); \
        DUK_TVAL_SET_TVAL(tv__dst, tv__src); \
        DUK_UNREF((thr)); \
    } while (0)

#define DUK_TVAL_SET_UNDEFINED_UPDREF         DUK_TVAL_SET_UNDEFINED_UPDREF_ALT0
#define DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ    DUK_TVAL_SET_UNDEFINED_UPDREF_ALT0
#define DUK_TVAL_SET_UNUSED_UPDREF            DUK_TVAL_SET_UNUSED_UPDREF_ALT0
#define DUK_TVAL_SET_NULL_UPDREF              DUK_TVAL_SET_NULL_UPDREF_ALT0
#define DUK_TVAL_SET_BOOLEAN_UPDREF           DUK_TVAL_SET_BOOLEAN_UPDREF_ALT0
#define DUK_TVAL_SET_NUMBER_UPDREF            DUK_TVAL_SET_NUMBER_UPDREF_ALT0
#define DUK_TVAL_SET_NUMBER_CHKFAST_UPDREF    DUK_TVAL_SET_NUMBER_CHKFAST_UPDREF_ALT0
#define DUK_TVAL_SET_DOUBLE_UPDREF            DUK_TVAL_SET_DOUBLE_UPDREF_ALT0
#define DUK_TVAL_SET_NAN_UPDREF               DUK_TVAL_SET_NAN_UPDREF_ALT0
#if defined(DUK_USE_FASTINT)
#define DUK_TVAL_SET_I48_UPDREF               DUK_TVAL_SET_I48_UPDREF_ALT0
#define DUK_TVAL_SET_I32_UPDREF               DUK_TVAL_SET_I32_UPDREF_ALT0
#define DUK_TVAL_SET_U32_UPDREF               DUK_TVAL_SET_U32_UPDREF_ALT0
#else
#define DUK_TVAL_SET_I48_UPDREF               DUK_TVAL_SET_DOUBLE_CAST_UPDREF  /* XXX: fast-int-to-double */
#define DUK_TVAL_SET_I32_UPDREF               DUK_TVAL_SET_DOUBLE_CAST_UPDREF
#define DUK_TVAL_SET_U32_UPDREF               DUK_TVAL_SET_DOUBLE_CAST_UPDREF
#endif  /* DUK_USE_FASTINT */
#define DUK_TVAL_SET_FASTINT_UPDREF           DUK_TVAL_SET_I48_UPDREF  /* convenience */
#define DUK_TVAL_SET_LIGHTFUNC_UPDREF         DUK_TVAL_SET_LIGHTFUNC_UPDREF_ALT0
#define DUK_TVAL_SET_STRING_UPDREF            DUK_TVAL_SET_STRING_UPDREF_ALT0
#define DUK_TVAL_SET_OBJECT_UPDREF            DUK_TVAL_SET_OBJECT_UPDREF_ALT0
#define DUK_TVAL_SET_BUFFER_UPDREF            DUK_TVAL_SET_BUFFER_UPDREF_ALT0
#define DUK_TVAL_SET_POINTER_UPDREF           DUK_TVAL_SET_POINTER_UPDREF_ALT0

#define DUK_TVAL_SET_TVAL_UPDREF              DUK_TVAL_SET_TVAL_UPDREF_ALT0
#define DUK_TVAL_SET_TVAL_UPDREF_FAST         DUK_TVAL_SET_TVAL_UPDREF_ALT0
#define DUK_TVAL_SET_TVAL_UPDREF_SLOW         DUK_TVAL_SET_TVAL_UPDREF_ALT0

#endif  /* DUK_USE_REFERENCE_COUNTING */

/*
 *  Some convenience macros that don't have optimized implementations now.
 */

#define DUK_TVAL_SET_TVAL_UPDREF_NORZ(thr,tv_dst,tv_src) do { \
        duk_hthread *duk__thr = (thr); \
        duk_tval *duk__dst = (tv_dst); \
        duk_tval *duk__src = (tv_src); \
        DUK_UNREF(duk__thr); \
        DUK_TVAL_DECREF_NORZ(thr, duk__dst); \
        DUK_TVAL_SET_TVAL(duk__dst, duk__src); \
        DUK_TVAL_INCREF(thr, duk__dst); \
    } while (0)

#define DUK_TVAL_SET_U32_UPDREF_NORZ(thr,tv_dst,val) do { \
        duk_hthread *duk__thr = (thr); \
        duk_tval *duk__dst = (tv_dst); \
        duk_uint32_t duk__val = (duk_uint32_t) (val); \
        DUK_UNREF(duk__thr); \
        DUK_TVAL_DECREF_NORZ(thr, duk__dst); \
        DUK_TVAL_SET_U32(duk__dst, duk__val); \
    } while (0)

/*
 *  Prototypes
 */

#if defined(DUK_USE_REFERENCE_COUNTING)
#if defined(DUK_USE_FINALIZER_SUPPORT)
DUK_INTERNAL_DECL void duk_refzero_check_slow(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_refzero_check_fast(duk_hthread *thr);
#endif
DUK_INTERNAL_DECL void duk_heaphdr_refcount_finalize_norz(duk_heap *heap, duk_heaphdr *hdr);
DUK_INTERNAL_DECL void duk_hobject_refcount_finalize_norz(duk_heap *heap, duk_hobject *h);
#if 0  /* Not needed: fast path handles inline; slow path uses duk_heaphdr_decref() which is needed anyway. */
DUK_INTERNAL_DECL void duk_hstring_decref(duk_hthread *thr, duk_hstring *h);
DUK_INTERNAL_DECL void duk_hstring_decref_norz(duk_hthread *thr, duk_hstring *h);
DUK_INTERNAL_DECL void duk_hbuffer_decref(duk_hthread *thr, duk_hbuffer *h);
DUK_INTERNAL_DECL void duk_hbuffer_decref_norz(duk_hthread *thr, duk_hbuffer *h);
DUK_INTERNAL_DECL void duk_hobject_decref(duk_hthread *thr, duk_hobject *h);
DUK_INTERNAL_DECL void duk_hobject_decref_norz(duk_hthread *thr, duk_hobject *h);
#endif
DUK_INTERNAL_DECL void duk_heaphdr_refzero(duk_hthread *thr, duk_heaphdr *h);
DUK_INTERNAL_DECL void duk_heaphdr_refzero_norz(duk_hthread *thr, duk_heaphdr *h);
#if defined(DUK_USE_FAST_REFCOUNT_DEFAULT)
DUK_INTERNAL_DECL void duk_hstring_refzero(duk_hthread *thr, duk_hstring *h);  /* no 'norz' variant */
DUK_INTERNAL_DECL void duk_hbuffer_refzero(duk_hthread *thr, duk_hbuffer *h);  /* no 'norz' variant */
DUK_INTERNAL_DECL void duk_hobject_refzero(duk_hthread *thr, duk_hobject *h);
DUK_INTERNAL_DECL void duk_hobject_refzero_norz(duk_hthread *thr, duk_hobject *h);
#else
DUK_INTERNAL_DECL void duk_tval_incref(duk_tval *tv);
DUK_INTERNAL_DECL void duk_tval_decref(duk_hthread *thr, duk_tval *tv);
DUK_INTERNAL_DECL void duk_tval_decref_norz(duk_hthread *thr, duk_tval *tv);
DUK_INTERNAL_DECL void duk_heaphdr_incref(duk_heaphdr *h);
DUK_INTERNAL_DECL void duk_heaphdr_decref(duk_hthread *thr, duk_heaphdr *h);
DUK_INTERNAL_DECL void duk_heaphdr_decref_norz(duk_hthread *thr, duk_heaphdr *h);
#endif
#else  /* DUK_USE_REFERENCE_COUNTING */
/* no refcounting */
#endif  /* DUK_USE_REFERENCE_COUNTING */

#endif  /* DUK_REFCOUNT_H_INCLUDED */
/* #include duk_api_internal.h */
//#line 1 "duk_api_internal.h"
/*
 *  Internal API calls which have (stack and other) semantics similar
 *  to the public API.
 */

#if !defined(DUK_API_INTERNAL_H_INCLUDED)
#define DUK_API_INTERNAL_H_INCLUDED

/* Inline macro helpers. */
#if defined(DUK_USE_PREFER_SIZE)
#define DUK_INLINE_PERF
#define DUK_ALWAYS_INLINE_PERF
#define DUK_NOINLINE_PERF
#else
#define DUK_INLINE_PERF DUK_INLINE
#define DUK_ALWAYS_INLINE_PERF DUK_ALWAYS_INLINE
#define DUK_NOINLINE_PERF DUK_NOINLINE
#endif

/* Inline macro helpers, for bytecode executor. */
#if defined(DUK_USE_EXEC_PREFER_SIZE)
#define DUK_EXEC_INLINE_PERF
#define DUK_EXEC_ALWAYS_INLINE_PERF
#define DUK_EXEC_NOINLINE_PERF
#else
#define DUK_EXEC_INLINE_PERF DUK_INLINE
#define DUK_EXEC_ALWAYS_INLINE_PERF DUK_ALWAYS_INLINE
#define DUK_EXEC_NOINLINE_PERF DUK_NOINLINE
#endif

/* duk_push_sprintf constants */
#define DUK_PUSH_SPRINTF_INITIAL_SIZE  256L
#define DUK_PUSH_SPRINTF_SANITY_LIMIT  (1L * 1024L * 1024L * 1024L)

/* Flag ORed to err_code to indicate __FILE__ / __LINE__ is not
 * blamed as source of error for error fileName / lineNumber.
 */
#define DUK_ERRCODE_FLAG_NOBLAME_FILELINE  (1L << 24)

/* Current convention is to use duk_size_t for value stack sizes and global indices,
 * and duk_idx_t for local frame indices.
 */
DUK_INTERNAL_DECL void duk_valstack_grow_check_throw(duk_hthread *thr, duk_size_t min_bytes);
DUK_INTERNAL_DECL duk_bool_t duk_valstack_grow_check_nothrow(duk_hthread *thr, duk_size_t min_bytes);
DUK_INTERNAL_DECL void duk_valstack_shrink_check_nothrow(duk_hthread *thr, duk_bool_t snug);

DUK_INTERNAL_DECL void duk_copy_tvals_incref(duk_hthread *thr, duk_tval *tv_dst, duk_tval *tv_src, duk_size_t count);

DUK_INTERNAL_DECL duk_tval *duk_reserve_gap(duk_hthread *thr, duk_idx_t idx_base, duk_idx_t count);

DUK_INTERNAL_DECL void duk_set_top_unsafe(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL void duk_set_top_and_wipe(duk_hthread *thr, duk_idx_t top, duk_idx_t idx_wipe_start);

DUK_INTERNAL_DECL void duk_dup_0(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_dup_1(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_dup_2(duk_hthread *thr);
/* duk_dup_m1() would be same as duk_dup_top() */
DUK_INTERNAL_DECL void duk_dup_m2(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_dup_m3(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_dup_m4(duk_hthread *thr);

DUK_INTERNAL_DECL void duk_remove_unsafe(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL void duk_remove_m2(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_remove_n(duk_hthread *thr, duk_idx_t idx, duk_idx_t count);
DUK_INTERNAL_DECL void duk_remove_n_unsafe(duk_hthread *thr, duk_idx_t idx, duk_idx_t count);

DUK_INTERNAL_DECL duk_int_t duk_get_type_tval(duk_tval *tv);
DUK_INTERNAL_DECL duk_uint_t duk_get_type_mask_tval(duk_tval *tv);

#if defined(DUK_USE_VERBOSE_ERRORS) && defined(DUK_USE_PARANOID_ERRORS)
DUK_INTERNAL_DECL const char *duk_get_type_name(duk_hthread *thr, duk_idx_t idx);
#endif
DUK_INTERNAL_DECL duk_small_uint_t duk_get_class_number(duk_hthread *thr, duk_idx_t idx);

DUK_INTERNAL_DECL duk_tval *duk_get_tval(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL duk_tval *duk_get_tval_or_unused(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL duk_tval *duk_require_tval(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL void duk_push_tval(duk_hthread *thr, duk_tval *tv);

/* Push the current 'this' binding; throw TypeError if binding is not object
 * coercible (CheckObjectCoercible).
 */
DUK_INTERNAL_DECL void duk_push_this_check_object_coercible(duk_hthread *thr);

/* duk_push_this() + CheckObjectCoercible() + duk_to_object() */
DUK_INTERNAL_DECL duk_hobject *duk_push_this_coercible_to_object(duk_hthread *thr);

/* duk_push_this() + CheckObjectCoercible() + duk_to_string() */
DUK_INTERNAL_DECL duk_hstring *duk_push_this_coercible_to_string(duk_hthread *thr);

DUK_INTERNAL_DECL duk_hstring *duk_push_uint_to_hstring(duk_hthread *thr, duk_uint_t i);

/* Get a borrowed duk_tval pointer to the current 'this' binding.  Caller must
 * make sure there's an active callstack entry.  Note that the returned pointer
 * is unstable with regards to side effects.
 */
DUK_INTERNAL_DECL duk_tval *duk_get_borrowed_this_tval(duk_hthread *thr);

/* XXX: add fastint support? */
#define duk_push_u64(thr,val) \
    duk_push_number((thr), (duk_double_t) (val))
#define duk_push_i64(thr,val) \
    duk_push_number((thr), (duk_double_t) (val))

/* duk_push_(u)int() is guaranteed to support at least (un)signed 32-bit range */
#define duk_push_u32(thr,val) \
    duk_push_uint((thr), (duk_uint_t) (val))
#define duk_push_i32(thr,val) \
    duk_push_int((thr), (duk_int_t) (val))

/* sometimes stack and array indices need to go on the stack */
#define duk_push_idx(thr,val) \
    duk_push_int((thr), (duk_int_t) (val))
#define duk_push_uarridx(thr,val) \
    duk_push_uint((thr), (duk_uint_t) (val))
#define duk_push_size_t(thr,val) \
    duk_push_uint((thr), (duk_uint_t) (val))  /* XXX: assumed to fit for now */

DUK_INTERNAL_DECL duk_bool_t duk_is_string_notsymbol(duk_hthread *thr, duk_idx_t idx);

DUK_INTERNAL_DECL duk_bool_t duk_is_callable_tval(duk_hthread *thr, duk_tval *tv);

DUK_INTERNAL_DECL duk_bool_t duk_is_bare_object(duk_hthread *thr, duk_idx_t idx);

DUK_INTERNAL_DECL duk_hstring *duk_get_hstring(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL duk_hstring *duk_get_hstring_notsymbol(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL const char *duk_get_string_notsymbol(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL duk_hobject *duk_get_hobject(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL duk_hbuffer *duk_get_hbuffer(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL duk_hthread *duk_get_hthread(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL duk_hcompfunc *duk_get_hcompfunc(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL duk_hnatfunc *duk_get_hnatfunc(duk_hthread *thr, duk_idx_t idx);

DUK_INTERNAL_DECL void *duk_get_buffer_data_raw(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_len, duk_bool_t throw_flag, duk_bool_t *out_isbuffer);

DUK_INTERNAL_DECL duk_hobject *duk_get_hobject_with_class(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t classnum);

DUK_INTERNAL_DECL duk_hobject *duk_get_hobject_promote_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask);
DUK_INTERNAL_DECL duk_hobject *duk_require_hobject_promote_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask);
DUK_INTERNAL_DECL duk_hobject *duk_require_hobject_accept_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask);
#define duk_require_hobject_promote_lfunc(thr,idx) \
    duk_require_hobject_promote_mask((thr), (idx), DUK_TYPE_MASK_LIGHTFUNC)
#define duk_get_hobject_promote_lfunc(thr,idx) \
    duk_get_hobject_promote_mask((thr), (idx), DUK_TYPE_MASK_LIGHTFUNC)

#if 0  /*unused*/
DUK_INTERNAL_DECL void *duk_get_voidptr(duk_hthread *thr, duk_idx_t idx);
#endif

DUK_INTERNAL_DECL duk_hstring *duk_known_hstring(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL duk_hobject *duk_known_hobject(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL duk_hbuffer *duk_known_hbuffer(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL duk_hcompfunc *duk_known_hcompfunc(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL duk_hnatfunc *duk_known_hnatfunc(duk_hthread *thr, duk_idx_t idx);

DUK_INTERNAL_DECL duk_double_t duk_to_number_tval(duk_hthread *thr, duk_tval *tv);

DUK_INTERNAL_DECL duk_hstring *duk_to_hstring(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL duk_hstring *duk_to_hstring_m1(duk_hthread *thr);
DUK_INTERNAL_DECL duk_hstring *duk_to_hstring_acceptsymbol(duk_hthread *thr, duk_idx_t idx);

DUK_INTERNAL_DECL duk_hobject *duk_to_hobject(duk_hthread *thr, duk_idx_t idx);

DUK_INTERNAL_DECL duk_double_t duk_to_number_m1(duk_hthread *thr);
DUK_INTERNAL_DECL duk_double_t duk_to_number_m2(duk_hthread *thr);

DUK_INTERNAL_DECL duk_bool_t duk_to_boolean_top_pop(duk_hthread *thr);

#if defined(DUK_USE_DEBUGGER_SUPPORT)  /* only needed by debugger for now */
DUK_INTERNAL_DECL duk_hstring *duk_safe_to_hstring(duk_hthread *thr, duk_idx_t idx);
#endif
DUK_INTERNAL_DECL void duk_push_class_string_tval(duk_hthread *thr, duk_tval *tv, duk_bool_t avoid_side_effects);

DUK_INTERNAL_DECL duk_int_t duk_to_int_clamped_raw(duk_hthread *thr, duk_idx_t idx, duk_int_t minval, duk_int_t maxval, duk_bool_t *out_clamped);  /* out_clamped=NULL, RangeError if outside range */
DUK_INTERNAL_DECL duk_int_t duk_to_int_clamped(duk_hthread *thr, duk_idx_t idx, duk_int_t minval, duk_int_t maxval);
DUK_INTERNAL_DECL duk_int_t duk_to_int_check_range(duk_hthread *thr, duk_idx_t idx, duk_int_t minval, duk_int_t maxval);
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL_DECL duk_uint8_t duk_to_uint8clamped(duk_hthread *thr, duk_idx_t idx);
#endif
DUK_INTERNAL_DECL duk_hstring *duk_to_property_key_hstring(duk_hthread *thr, duk_idx_t idx);

DUK_INTERNAL_DECL duk_hstring *duk_require_hstring(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL duk_hstring *duk_require_hstring_notsymbol(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL const char *duk_require_lstring_notsymbol(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len);
DUK_INTERNAL_DECL const char *duk_require_string_notsymbol(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL duk_hobject *duk_require_hobject(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL duk_hbuffer *duk_require_hbuffer(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL duk_hthread *duk_require_hthread(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL duk_hcompfunc *duk_require_hcompfunc(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL duk_hnatfunc *duk_require_hnatfunc(duk_hthread *thr, duk_idx_t idx);

DUK_INTERNAL_DECL duk_hobject *duk_require_hobject_with_class(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t classnum);

DUK_INTERNAL_DECL void duk_push_hstring(duk_hthread *thr, duk_hstring *h);
DUK_INTERNAL_DECL void duk_push_hstring_stridx(duk_hthread *thr, duk_small_uint_t stridx);
DUK_INTERNAL_DECL void duk_push_hstring_empty(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_push_hobject(duk_hthread *thr, duk_hobject *h);
DUK_INTERNAL_DECL void duk_push_hbuffer(duk_hthread *thr, duk_hbuffer *h);
#define duk_push_hthread(thr,h) \
    duk_push_hobject((thr), (duk_hobject *) (h))
#define duk_push_hnatfunc(thr,h) \
    duk_push_hobject((thr), (duk_hobject *) (h))
DUK_INTERNAL_DECL void duk_push_hobject_bidx(duk_hthread *thr, duk_small_int_t builtin_idx);
DUK_INTERNAL_DECL duk_hobject *duk_push_object_helper(duk_hthread *thr, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx);
DUK_INTERNAL_DECL duk_hobject *duk_push_object_helper_proto(duk_hthread *thr, duk_uint_t hobject_flags_and_class, duk_hobject *proto);
DUK_INTERNAL_DECL duk_hcompfunc *duk_push_hcompfunc(duk_hthread *thr);
DUK_INTERNAL_DECL duk_hboundfunc *duk_push_hboundfunc(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_push_c_function_builtin(duk_hthread *thr, duk_c_function func, duk_int_t nargs);
DUK_INTERNAL_DECL void duk_push_c_function_builtin_noconstruct(duk_hthread *thr, duk_c_function func, duk_int_t nargs);

/* XXX: duk_push_harray() and duk_push_hcompfunc() are inconsistent with
 * duk_push_hobject() etc which don't create a new value.
 */
DUK_INTERNAL_DECL duk_harray *duk_push_harray(duk_hthread *thr);
DUK_INTERNAL_DECL duk_harray *duk_push_harray_with_size(duk_hthread *thr, duk_uint32_t size);
DUK_INTERNAL_DECL duk_tval *duk_push_harray_with_size_outptr(duk_hthread *thr, duk_uint32_t size);

DUK_INTERNAL_DECL void duk_push_string_funcptr(duk_hthread *thr, duk_uint8_t *ptr, duk_size_t sz);
DUK_INTERNAL_DECL void duk_push_lightfunc_name_raw(duk_hthread *thr, duk_c_function func, duk_small_uint_t lf_flags);
DUK_INTERNAL_DECL void duk_push_lightfunc_name(duk_hthread *thr, duk_tval *tv);
DUK_INTERNAL_DECL void duk_push_lightfunc_tostring(duk_hthread *thr, duk_tval *tv);
#if 0  /* not used yet */
DUK_INTERNAL_DECL void duk_push_hnatfunc_name(duk_hthread *thr, duk_hnatfunc *h);
#endif
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL_DECL duk_hbufobj *duk_push_bufobj_raw(duk_hthread *thr, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx);
#endif

DUK_INTERNAL_DECL void *duk_push_fixed_buffer_nozero(duk_hthread *thr, duk_size_t len);
DUK_INTERNAL_DECL void *duk_push_fixed_buffer_zero(duk_hthread *thr, duk_size_t len);

DUK_INTERNAL_DECL const char *duk_push_string_readable(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL const char *duk_push_string_tval_readable(duk_hthread *thr, duk_tval *tv);
DUK_INTERNAL_DECL const char *duk_push_string_tval_readable_error(duk_hthread *thr, duk_tval *tv);

/* The duk_xxx_prop_stridx_short() variants expect their arguments to be short
 * enough to be packed into a single 32-bit integer argument.  Argument limits
 * vary per call; typically 16 bits are assigned to the signed value stack index
 * and the stridx.  In practice these work well for footprint with constant
 * arguments and such call sites are also easiest to verify to be correct.
 */

DUK_INTERNAL_DECL duk_bool_t duk_get_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx);     /* [] -> [val] */
DUK_INTERNAL_DECL duk_bool_t duk_get_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args);
#define duk_get_prop_stridx_short(thr,obj_idx,stridx) \
    (DUK_ASSERT_EXPR((duk_int_t) (obj_idx) >= -0x8000L && (duk_int_t) (obj_idx) <= 0x7fffL), \
     DUK_ASSERT_EXPR((duk_int_t) (stridx) >= 0 && (duk_int_t) (stridx) <= 0xffffL), \
     duk_get_prop_stridx_short_raw((thr), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx))))
DUK_INTERNAL_DECL duk_bool_t duk_get_prop_stridx_boolean(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_bool_t *out_has_prop);  /* [] -> [] */

DUK_INTERNAL_DECL duk_bool_t duk_xget_owndataprop(duk_hthread *thr, duk_idx_t obj_idx);
DUK_INTERNAL_DECL duk_bool_t duk_xget_owndataprop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx);
DUK_INTERNAL_DECL duk_bool_t duk_xget_owndataprop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args);
#define duk_xget_owndataprop_stridx_short(thr,obj_idx,stridx) \
    (DUK_ASSERT_EXPR((duk_int_t) (obj_idx) >= -0x8000L && (duk_int_t) (obj_idx) <= 0x7fffL), \
     DUK_ASSERT_EXPR((duk_int_t) (stridx) >= 0 && (duk_int_t) (stridx) <= 0xffffL), \
     duk_xget_owndataprop_stridx_short_raw((thr), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx))))

DUK_INTERNAL_DECL duk_bool_t duk_put_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx);     /* [val] -> [] */
DUK_INTERNAL_DECL duk_bool_t duk_put_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args);
#define duk_put_prop_stridx_short(thr,obj_idx,stridx) \
    (DUK_ASSERT_EXPR((duk_int_t) (obj_idx) >= -0x8000L && (duk_int_t) (obj_idx) <= 0x7fffL), \
     DUK_ASSERT_EXPR((duk_int_t) (stridx) >= 0 && (duk_int_t) (stridx) <= 0xffffL), \
     duk_put_prop_stridx_short_raw((thr), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx))))

DUK_INTERNAL_DECL duk_bool_t duk_del_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx);     /* [] -> [] */
#if 0  /* Too few call sites to be useful. */
DUK_INTERNAL_DECL duk_bool_t duk_del_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args);
#define duk_del_prop_stridx_short(thr,obj_idx,stridx) \
    (DUK_ASSERT_EXPR((obj_idx) >= -0x8000L && (obj_idx) <= 0x7fffL), \
     DUK_ASSERT_EXPR((stridx) >= 0 && (stridx) <= 0xffffL), \
     duk_del_prop_stridx_short_raw((thr), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx))))
#endif
#define duk_del_prop_stridx_short(thr,obj_idx,stridx) \
    duk_del_prop_stridx((thr), (obj_idx), (stridx))

DUK_INTERNAL_DECL duk_bool_t duk_has_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx);     /* [] -> [] */
#if 0  /* Too few call sites to be useful. */
DUK_INTERNAL_DECL duk_bool_t duk_has_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args);
#define duk_has_prop_stridx_short(thr,obj_idx,stridx) \
    (DUK_ASSERT_EXPR((obj_idx) >= -0x8000L && (obj_idx) <= 0x7fffL), \
     DUK_ASSERT_EXPR((stridx) >= 0 && (stridx) <= 0xffffL), \
     duk_has_prop_stridx_short_raw((thr), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx))))
#endif
#define duk_has_prop_stridx_short(thr,obj_idx,stridx) \
    duk_has_prop_stridx((thr), (obj_idx), (stridx))

DUK_INTERNAL_DECL void duk_xdef_prop(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t desc_flags);  /* [key val] -> [] */

DUK_INTERNAL_DECL void duk_xdef_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx, duk_small_uint_t desc_flags);  /* [val] -> [] */

/* XXX: Because stridx and desc_flags have a limited range, this call could
 * always pack stridx and desc_flags into a single argument.
 */
DUK_INTERNAL_DECL void duk_xdef_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_small_uint_t desc_flags);  /* [val] -> [] */
DUK_INTERNAL_DECL void duk_xdef_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args);
#define duk_xdef_prop_stridx_short(thr,obj_idx,stridx,desc_flags) \
    (DUK_ASSERT_EXPR((duk_int_t) (obj_idx) >= -0x80L && (duk_int_t) (obj_idx) <= 0x7fL), \
     DUK_ASSERT_EXPR((duk_int_t) (stridx) >= 0 && (duk_int_t) (stridx) <= 0xffffL), \
     DUK_ASSERT_EXPR((duk_int_t) (desc_flags) >= 0 && (duk_int_t) (desc_flags) <= 0xffL), \
     duk_xdef_prop_stridx_short_raw((thr), (((duk_uint_t) (obj_idx)) << 24) + (((duk_uint_t) (stridx)) << 8) + (duk_uint_t) (desc_flags)))

#define duk_xdef_prop_wec(thr,obj_idx) \
    duk_xdef_prop((thr), (obj_idx), DUK_PROPDESC_FLAGS_WEC)
#define duk_xdef_prop_index_wec(thr,obj_idx,arr_idx) \
    duk_xdef_prop_index((thr), (obj_idx), (arr_idx), DUK_PROPDESC_FLAGS_WEC)
#define duk_xdef_prop_stridx_wec(thr,obj_idx,stridx) \
    duk_xdef_prop_stridx((thr), (obj_idx), (stridx), DUK_PROPDESC_FLAGS_WEC)
#define duk_xdef_prop_stridx_short_wec(thr,obj_idx,stridx) \
    duk_xdef_prop_stridx_short((thr), (obj_idx), (stridx), DUK_PROPDESC_FLAGS_WEC)

#if 0  /*unused*/
DUK_INTERNAL_DECL void duk_xdef_prop_stridx_builtin(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_small_int_t builtin_idx, duk_small_uint_t desc_flags);  /* [] -> [] */
#endif

DUK_INTERNAL_DECL void duk_xdef_prop_stridx_thrower(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx);  /* [] -> [] */

DUK_INTERNAL_DECL duk_bool_t duk_get_method_stridx(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t stridx);

DUK_INTERNAL_DECL void duk_pack(duk_hthread *thr, duk_idx_t count);
DUK_INTERNAL_DECL duk_idx_t duk_unpack_array_like(duk_hthread *thr, duk_idx_t idx);
#if 0
DUK_INTERNAL_DECL void duk_unpack(duk_hthread *thr);
#endif

DUK_INTERNAL_DECL void duk_push_symbol_descriptive_string(duk_hthread *thr, duk_hstring *h);

DUK_INTERNAL_DECL void duk_resolve_nonbound_function(duk_hthread *thr);

DUK_INTERNAL_DECL duk_idx_t duk_get_top_require_min(duk_hthread *thr, duk_idx_t min_top);
DUK_INTERNAL_DECL duk_idx_t duk_get_top_index_unsafe(duk_hthread *thr);

DUK_INTERNAL_DECL void duk_pop_n_unsafe(duk_hthread *thr, duk_idx_t count);
DUK_INTERNAL_DECL void duk_pop_unsafe(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_pop_2_unsafe(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_pop_3_unsafe(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_pop_n_nodecref_unsafe(duk_hthread *thr, duk_idx_t count);
DUK_INTERNAL_DECL void duk_pop_nodecref_unsafe(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_pop_2_nodecref_unsafe(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_pop_3_nodecref_unsafe(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_pop_undefined(duk_hthread *thr);

DUK_INTERNAL_DECL void duk_compact_m1(duk_hthread *thr);

DUK_INTERNAL_DECL void duk_seal_freeze_raw(duk_hthread *thr, duk_idx_t obj_idx, duk_bool_t is_freeze);

DUK_INTERNAL_DECL void duk_insert_undefined(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL void duk_insert_undefined_n(duk_hthread *thr, duk_idx_t idx, duk_idx_t count);

DUK_INTERNAL_DECL void duk_concat_2(duk_hthread *thr);

DUK_INTERNAL_DECL duk_int_t duk_pcall_method_flags(duk_hthread *thr, duk_idx_t nargs, duk_small_uint_t call_flags);

#if defined(DUK_USE_SYMBOL_BUILTIN)
DUK_INTERNAL_DECL void duk_to_primitive_ordinary(duk_hthread *thr, duk_idx_t idx, duk_int_t hint);
#endif

DUK_INTERNAL_DECL void duk_clear_prototype(duk_hthread *thr, duk_idx_t idx);

/* Raw internal valstack access macros: access is unsafe so call site
 * must have a guarantee that the index is valid.  When that is the case,
 * using these macro results in faster and smaller code than duk_get_tval().
 * Both 'ctx' and 'idx' are evaluted multiple times, but only for asserts.
 */
#define DUK_ASSERT_VALID_NEGIDX(thr,idx) \
    (DUK_ASSERT_EXPR((duk_int_t) (idx) < 0), DUK_ASSERT_EXPR(duk_is_valid_index((thr), (idx))))
#define DUK_ASSERT_VALID_POSIDX(thr,idx) \
    (DUK_ASSERT_EXPR((duk_int_t) (idx) >= 0), DUK_ASSERT_EXPR(duk_is_valid_index((thr), (idx))))
#define DUK_GET_TVAL_NEGIDX(thr,idx) \
    (DUK_ASSERT_VALID_NEGIDX((thr),(idx)), ((duk_hthread *) (thr))->valstack_top + (idx))
#define DUK_GET_TVAL_POSIDX(thr,idx) \
    (DUK_ASSERT_VALID_POSIDX((thr),(idx)), ((duk_hthread *) (thr))->valstack_bottom + (idx))
#define DUK_GET_HOBJECT_NEGIDX(thr,idx) \
    (DUK_ASSERT_VALID_NEGIDX((thr),(idx)), DUK_TVAL_GET_OBJECT(((duk_hthread *) (thr))->valstack_top + (idx)))
#define DUK_GET_HOBJECT_POSIDX(thr,idx) \
    (DUK_ASSERT_VALID_POSIDX((thr),(idx)), DUK_TVAL_GET_OBJECT(((duk_hthread *) (thr))->valstack_bottom + (idx)))

#define DUK_GET_THIS_TVAL_PTR(thr) \
    (DUK_ASSERT_EXPR((thr)->valstack_bottom > (thr)->valstack), \
     (thr)->valstack_bottom - 1)

DUK_INTERNAL_DECL duk_double_t duk_time_get_ecmascript_time(duk_hthread *thr);
DUK_INTERNAL_DECL duk_double_t duk_time_get_ecmascript_time_nofrac(duk_hthread *thr);
DUK_INTERNAL_DECL duk_double_t duk_time_get_monotonic_time(duk_hthread *thr);

#endif  /* DUK_API_INTERNAL_H_INCLUDED */
/* #include duk_hstring.h */
//#line 1 "duk_hstring.h"
/*
 *  Heap string representation.
 *
 *  Strings are byte sequences ordinarily stored in extended UTF-8 format,
 *  allowing values larger than the official UTF-8 range (used internally)
 *  and also allowing UTF-8 encoding of surrogate pairs (CESU-8 format).
 *  Strings may also be invalid UTF-8 altogether which is the case e.g. with
 *  strings used as internal property names and raw buffers converted to
 *  strings.  In such cases the 'clen' field contains an inaccurate value.
 *
 *  ECMAScript requires support for 32-bit long strings.  However, since each
 *  16-bit codepoint can take 3 bytes in CESU-8, this representation can only
 *  support about 1.4G codepoint long strings in extreme cases.  This is not
 *  really a practical issue.
 */

#if !defined(DUK_HSTRING_H_INCLUDED)
#define DUK_HSTRING_H_INCLUDED

/* Impose a maximum string length for now.  Restricted artificially to
 * ensure adding a heap header length won't overflow size_t.  The limit
 * should be synchronized with DUK_HBUFFER_MAX_BYTELEN.
 *
 * E5.1 makes provisions to support strings longer than 4G characters.
 * This limit should be eliminated on 64-bit platforms (and increased
 * closer to maximum support on 32-bit platforms).
 */

#if defined(DUK_USE_STRLEN16)
#define DUK_HSTRING_MAX_BYTELEN                     (0x0000ffffUL)
#else
#define DUK_HSTRING_MAX_BYTELEN                     (0x7fffffffUL)
#endif

/* XXX: could add flags for "is valid CESU-8" (ECMAScript compatible strings),
 * "is valid UTF-8", "is valid extended UTF-8" (internal strings are not,
 * regexp bytecode is), and "contains non-BMP characters".  These are not
 * needed right now.
 */

/* With lowmem builds the high 16 bits of duk_heaphdr are used for other
 * purposes, so this leaves 7 duk_heaphdr flags and 9 duk_hstring flags.
 */
#define DUK_HSTRING_FLAG_ASCII                      DUK_HEAPHDR_USER_FLAG(0)  /* string is ASCII, clen == blen */
#define DUK_HSTRING_FLAG_ARRIDX                     DUK_HEAPHDR_USER_FLAG(1)  /* string is a valid array index */
#define DUK_HSTRING_FLAG_SYMBOL                     DUK_HEAPHDR_USER_FLAG(2)  /* string is a symbol (invalid utf-8) */
#define DUK_HSTRING_FLAG_HIDDEN                     DUK_HEAPHDR_USER_FLAG(3)  /* string is a hidden symbol (implies symbol, Duktape 1.x internal string) */
#define DUK_HSTRING_FLAG_RESERVED_WORD              DUK_HEAPHDR_USER_FLAG(4)  /* string is a reserved word (non-strict) */
#define DUK_HSTRING_FLAG_STRICT_RESERVED_WORD       DUK_HEAPHDR_USER_FLAG(5)  /* string is a reserved word (strict) */
#define DUK_HSTRING_FLAG_EVAL_OR_ARGUMENTS          DUK_HEAPHDR_USER_FLAG(6)  /* string is 'eval' or 'arguments' */
#define DUK_HSTRING_FLAG_EXTDATA                    DUK_HEAPHDR_USER_FLAG(7)  /* string data is external (duk_hstring_external) */
#define DUK_HSTRING_FLAG_PINNED_LITERAL             DUK_HEAPHDR_USER_FLAG(8)  /* string is a literal, and pinned */

#define DUK_HSTRING_HAS_ASCII(x)                    DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_ASCII)
#define DUK_HSTRING_HAS_ARRIDX(x)                   DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_ARRIDX)
#define DUK_HSTRING_HAS_SYMBOL(x)                   DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_SYMBOL)
#define DUK_HSTRING_HAS_HIDDEN(x)                   DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_HIDDEN)
#define DUK_HSTRING_HAS_RESERVED_WORD(x)            DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_RESERVED_WORD)
#define DUK_HSTRING_HAS_STRICT_RESERVED_WORD(x)     DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_STRICT_RESERVED_WORD)
#define DUK_HSTRING_HAS_EVAL_OR_ARGUMENTS(x)        DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_EVAL_OR_ARGUMENTS)
#define DUK_HSTRING_HAS_EXTDATA(x)                  DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_EXTDATA)
#define DUK_HSTRING_HAS_PINNED_LITERAL(x)           DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_PINNED_LITERAL)

#define DUK_HSTRING_SET_ASCII(x)                    DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_ASCII)
#define DUK_HSTRING_SET_ARRIDX(x)                   DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_ARRIDX)
#define DUK_HSTRING_SET_SYMBOL(x)                   DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_SYMBOL)
#define DUK_HSTRING_SET_HIDDEN(x)                   DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_HIDDEN)
#define DUK_HSTRING_SET_RESERVED_WORD(x)            DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_RESERVED_WORD)
#define DUK_HSTRING_SET_STRICT_RESERVED_WORD(x)     DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_STRICT_RESERVED_WORD)
#define DUK_HSTRING_SET_EVAL_OR_ARGUMENTS(x)        DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_EVAL_OR_ARGUMENTS)
#define DUK_HSTRING_SET_EXTDATA(x)                  DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_EXTDATA)
#define DUK_HSTRING_SET_PINNED_LITERAL(x)           DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_PINNED_LITERAL)

#define DUK_HSTRING_CLEAR_ASCII(x)                  DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_ASCII)
#define DUK_HSTRING_CLEAR_ARRIDX(x)                 DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_ARRIDX)
#define DUK_HSTRING_CLEAR_SYMBOL(x)                 DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_SYMBOL)
#define DUK_HSTRING_CLEAR_HIDDEN(x)                 DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_HIDDEN)
#define DUK_HSTRING_CLEAR_RESERVED_WORD(x)          DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_RESERVED_WORD)
#define DUK_HSTRING_CLEAR_STRICT_RESERVED_WORD(x)   DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_STRICT_RESERVED_WORD)
#define DUK_HSTRING_CLEAR_EVAL_OR_ARGUMENTS(x)      DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_EVAL_OR_ARGUMENTS)
#define DUK_HSTRING_CLEAR_EXTDATA(x)                DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_EXTDATA)
#define DUK_HSTRING_CLEAR_PINNED_LITERAL(x)         DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_PINNED_LITERAL)

#if 0  /* Slightly smaller code without explicit flag, but explicit flag
        * is very useful when 'clen' is dropped.
        */
#define DUK_HSTRING_IS_ASCII(x)                     (DUK_HSTRING_GET_BYTELEN((x)) == DUK_HSTRING_GET_CHARLEN((x)))
#endif
#define DUK_HSTRING_IS_ASCII(x)                     DUK_HSTRING_HAS_ASCII((x))  /* lazily set! */
#define DUK_HSTRING_IS_EMPTY(x)                     (DUK_HSTRING_GET_BYTELEN((x)) == 0)

#if defined(DUK_USE_STRHASH16)
#define DUK_HSTRING_GET_HASH(x)                     ((x)->hdr.h_flags >> 16)
#define DUK_HSTRING_SET_HASH(x,v) do { \
        (x)->hdr.h_flags = ((x)->hdr.h_flags & 0x0000ffffUL) | ((v) << 16); \
    } while (0)
#else
#define DUK_HSTRING_GET_HASH(x)                     ((x)->hash)
#define DUK_HSTRING_SET_HASH(x,v) do { \
        (x)->hash = (v); \
    } while (0)
#endif

#if defined(DUK_USE_STRLEN16)
#define DUK_HSTRING_GET_BYTELEN(x)                  ((x)->hdr.h_strextra16)
#define DUK_HSTRING_SET_BYTELEN(x,v) do { \
        (x)->hdr.h_strextra16 = (v); \
    } while (0)
#if defined(DUK_USE_HSTRING_CLEN)
#define DUK_HSTRING_GET_CHARLEN(x)                  duk_hstring_get_charlen((x))
#define DUK_HSTRING_SET_CHARLEN(x,v) do { \
        (x)->clen16 = (v); \
    } while (0)
#else
#define DUK_HSTRING_GET_CHARLEN(x)                  duk_hstring_get_charlen((x))
#define DUK_HSTRING_SET_CHARLEN(x,v) do { \
        DUK_ASSERT(0);  /* should never be called */ \
    } while (0)
#endif
#else
#define DUK_HSTRING_GET_BYTELEN(x)                  ((x)->blen)
#define DUK_HSTRING_SET_BYTELEN(x,v) do { \
        (x)->blen = (v); \
    } while (0)
#define DUK_HSTRING_GET_CHARLEN(x)                  duk_hstring_get_charlen((x))
#define DUK_HSTRING_SET_CHARLEN(x,v) do { \
        (x)->clen = (v); \
    } while (0)
#endif

#if defined(DUK_USE_HSTRING_EXTDATA)
#define DUK_HSTRING_GET_EXTDATA(x) \
    ((x)->extdata)
#define DUK_HSTRING_GET_DATA(x) \
    (DUK_HSTRING_HAS_EXTDATA((x)) ? \
        DUK_HSTRING_GET_EXTDATA((const duk_hstring_external *) (x)) : ((const duk_uint8_t *) ((x) + 1)))
#else
#define DUK_HSTRING_GET_DATA(x) \
    ((const duk_uint8_t *) ((x) + 1))
#endif

#define DUK_HSTRING_GET_DATA_END(x) \
    (DUK_HSTRING_GET_DATA((x)) + (x)->blen)

/* Marker value; in E5 2^32-1 is not a valid array index (2^32-2 is highest
 * valid).
 */
#define DUK_HSTRING_NO_ARRAY_INDEX  (0xffffffffUL)

#if defined(DUK_USE_HSTRING_ARRIDX)
#define DUK_HSTRING_GET_ARRIDX_FAST(h)  ((h)->arridx)
#define DUK_HSTRING_GET_ARRIDX_SLOW(h)  ((h)->arridx)
#else
/* Get array index related to string (or return DUK_HSTRING_NO_ARRAY_INDEX);
 * avoids helper call if string has no array index value.
 */
#define DUK_HSTRING_GET_ARRIDX_FAST(h)  \
    (DUK_HSTRING_HAS_ARRIDX((h)) ? duk_js_to_arrayindex_hstring_fast_known((h)) : DUK_HSTRING_NO_ARRAY_INDEX)

/* Slower but more compact variant. */
#define DUK_HSTRING_GET_ARRIDX_SLOW(h)  \
    (duk_js_to_arrayindex_hstring_fast((h)))
#endif

/* XXX: these actually fit into duk_hstring */
#define DUK_SYMBOL_TYPE_HIDDEN 0
#define DUK_SYMBOL_TYPE_GLOBAL 1
#define DUK_SYMBOL_TYPE_LOCAL 2
#define DUK_SYMBOL_TYPE_WELLKNOWN 3

/* Assertion for duk_hstring validity. */
#if defined(DUK_USE_ASSERTIONS)
DUK_INTERNAL_DECL void duk_hstring_assert_valid(duk_hstring *h);
#define DUK_HSTRING_ASSERT_VALID(h)  do { duk_hstring_assert_valid((h)); } while (0)
#else
#define DUK_HSTRING_ASSERT_VALID(h)  do {} while (0)
#endif

/*
 *  Misc
 */

struct duk_hstring {
    /* Smaller heaphdr than for other objects, because strings are held
     * in string intern table which requires no link pointers.  Much of
     * the 32-bit flags field is unused by flags, so we can stuff a 16-bit
     * field in there.
     */
    duk_heaphdr_string hdr;

    /* String hash. */
#if defined(DUK_USE_STRHASH16)
    /* If 16-bit hash is in use, stuff it into duk_heaphdr_string flags. */
#else
    duk_uint32_t hash;
#endif

    /* Precomputed array index (or DUK_HSTRING_NO_ARRAY_INDEX). */
#if defined(DUK_USE_HSTRING_ARRIDX)
    duk_uarridx_t arridx;
#endif

    /* Length in bytes (not counting NUL term). */
#if defined(DUK_USE_STRLEN16)
    /* placed in duk_heaphdr_string */
#else
    duk_uint32_t blen;
#endif

    /* Length in codepoints (must be E5 compatible). */
#if defined(DUK_USE_STRLEN16)
#if defined(DUK_USE_HSTRING_CLEN)
    duk_uint16_t clen16;
#else
    /* computed live */
#endif
#else
    duk_uint32_t clen;
#endif

    /*
     *  String data of 'blen+1' bytes follows (+1 for NUL termination
     *  convenience for C API).  No alignment needs to be guaranteed
     *  for strings, but fields above should guarantee alignment-by-4
     *  (but not alignment-by-8).
     */
};

/* The external string struct is defined even when the feature is inactive. */
struct duk_hstring_external {
    duk_hstring str;

    /*
     *  For an external string, the NUL-terminated string data is stored
     *  externally.  The user must guarantee that data behind this pointer
     *  doesn't change while it's used.
     */

    const duk_uint8_t *extdata;
};

/*
 *  Prototypes
 */

DUK_INTERNAL_DECL duk_ucodepoint_t duk_hstring_char_code_at_raw(duk_hthread *thr, duk_hstring *h, duk_uint_t pos, duk_bool_t surrogate_aware);
DUK_INTERNAL_DECL duk_bool_t duk_hstring_equals_ascii_cstring(duk_hstring *h, const char *cstr);
DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h);
#if !defined(DUK_USE_HSTRING_LAZY_CLEN)
DUK_INTERNAL_DECL void duk_hstring_init_charlen(duk_hstring *h);
#endif

#endif  /* DUK_HSTRING_H_INCLUDED */
/* #include duk_hobject.h */
//#line 1 "duk_hobject.h"
/*
 *  Heap object representation.
 *
 *  Heap objects are used for ECMAScript objects, arrays, and functions,
 *  but also for internal control like declarative and object environment
 *  records.  Compiled functions, native functions, and threads are also
 *  objects but with an extended C struct.
 *
 *  Objects provide the required ECMAScript semantics and exotic behaviors
 *  especially for property access.
 *
 *  Properties are stored in three conceptual parts:
 *
 *    1. A linear 'entry part' contains ordered key-value-attributes triples
 *       and is the main method of string properties.
 *
 *    2. An optional linear 'array part' is used for array objects to store a
 *       (dense) range of [0,N[ array indexed entries with default attributes
 *       (writable, enumerable, configurable).  If the array part would become
 *       sparse or non-default attributes are required, the array part is
 *       abandoned and moved to the 'entry part'.
 *
 *    3. An optional 'hash part' is used to optimize lookups of the entry
 *       part; it is used only for objects with sufficiently many properties
 *       and can be abandoned without loss of information.
 *
 *  These three conceptual parts are stored in a single memory allocated area.
 *  This minimizes memory allocation overhead but also means that all three
 *  parts are resized together, and makes property access a bit complicated.
 */

#if !defined(DUK_HOBJECT_H_INCLUDED)
#define DUK_HOBJECT_H_INCLUDED

/* Object flags.  Make sure this stays in sync with debugger object
 * inspection code.
 */

/* XXX: some flags are object subtype specific (e.g. common to all function
 * subtypes, duk_harray, etc) and could be reused for different subtypes.
 */
#define DUK_HOBJECT_FLAG_EXTENSIBLE            DUK_HEAPHDR_USER_FLAG(0)   /* object is extensible */
#define DUK_HOBJECT_FLAG_CONSTRUCTABLE         DUK_HEAPHDR_USER_FLAG(1)   /* object is constructable */
#define DUK_HOBJECT_FLAG_CALLABLE              DUK_HEAPHDR_USER_FLAG(2)   /* object is callable */
#define DUK_HOBJECT_FLAG_BOUNDFUNC             DUK_HEAPHDR_USER_FLAG(3)   /* object established using Function.prototype.bind() */
#define DUK_HOBJECT_FLAG_COMPFUNC              DUK_HEAPHDR_USER_FLAG(4)   /* object is a compiled function (duk_hcompfunc) */
#define DUK_HOBJECT_FLAG_NATFUNC               DUK_HEAPHDR_USER_FLAG(5)   /* object is a native function (duk_hnatfunc) */
#define DUK_HOBJECT_FLAG_BUFOBJ                DUK_HEAPHDR_USER_FLAG(6)   /* object is a buffer object (duk_hbufobj) (always exotic) */
#define DUK_HOBJECT_FLAG_FASTREFS              DUK_HEAPHDR_USER_FLAG(7)   /* object has no fields needing DECREF/marking beyond base duk_hobject header */
#define DUK_HOBJECT_FLAG_ARRAY_PART            DUK_HEAPHDR_USER_FLAG(8)   /* object has an array part (a_size may still be 0) */
#define DUK_HOBJECT_FLAG_STRICT                DUK_HEAPHDR_USER_FLAG(9)   /* function: function object is strict */
#define DUK_HOBJECT_FLAG_NOTAIL                DUK_HEAPHDR_USER_FLAG(10)  /* function: function must not be tail called */
#define DUK_HOBJECT_FLAG_NEWENV                DUK_HEAPHDR_USER_FLAG(11)  /* function: create new environment when called (see duk_hcompfunc) */
#define DUK_HOBJECT_FLAG_NAMEBINDING           DUK_HEAPHDR_USER_FLAG(12)  /* function: create binding for func name (function templates only, used for named function expressions) */
#define DUK_HOBJECT_FLAG_CREATEARGS            DUK_HEAPHDR_USER_FLAG(13)  /* function: create an arguments object on function call */
#define DUK_HOBJECT_FLAG_HAVE_FINALIZER        DUK_HEAPHDR_USER_FLAG(14)  /* object has a callable (own) finalizer property */
#define DUK_HOBJECT_FLAG_EXOTIC_ARRAY          DUK_HEAPHDR_USER_FLAG(15)  /* 'Array' object, array length and index exotic behavior */
#define DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ      DUK_HEAPHDR_USER_FLAG(16)  /* 'String' object, array index exotic behavior */
#define DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS      DUK_HEAPHDR_USER_FLAG(17)  /* 'Arguments' object and has arguments exotic behavior (non-strict callee) */
#define DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ       DUK_HEAPHDR_USER_FLAG(18)  /* 'Proxy' object */
#define DUK_HOBJECT_FLAG_SPECIAL_CALL          DUK_HEAPHDR_USER_FLAG(19)  /* special casing in call behavior, for .call(), .apply(), etc. */

#define DUK_HOBJECT_FLAG_CLASS_BASE            DUK_HEAPHDR_USER_FLAG_NUMBER(20)
#define DUK_HOBJECT_FLAG_CLASS_BITS            5

#define DUK_HOBJECT_GET_CLASS_NUMBER(h)        \
    DUK_HEAPHDR_GET_FLAG_RANGE(&(h)->hdr, DUK_HOBJECT_FLAG_CLASS_BASE, DUK_HOBJECT_FLAG_CLASS_BITS)
#define DUK_HOBJECT_SET_CLASS_NUMBER(h,v)      \
    DUK_HEAPHDR_SET_FLAG_RANGE(&(h)->hdr, DUK_HOBJECT_FLAG_CLASS_BASE, DUK_HOBJECT_FLAG_CLASS_BITS, (v))

#define DUK_HOBJECT_GET_CLASS_MASK(h)          \
    (1UL << DUK_HEAPHDR_GET_FLAG_RANGE(&(h)->hdr, DUK_HOBJECT_FLAG_CLASS_BASE, DUK_HOBJECT_FLAG_CLASS_BITS))

/* Macro for creating flag initializer from a class number.
 * Unsigned type cast is needed to avoid warnings about coercing
 * a signed integer to an unsigned one; the largest class values
 * have the highest bit (bit 31) set which causes this.
 */
#define DUK_HOBJECT_CLASS_AS_FLAGS(v)          (((duk_uint_t) (v)) << DUK_HOBJECT_FLAG_CLASS_BASE)

/* E5 Section 8.6.2 + custom classes */
#define DUK_HOBJECT_CLASS_NONE                 0
#define DUK_HOBJECT_CLASS_OBJECT               1
#define DUK_HOBJECT_CLASS_ARRAY                2
#define DUK_HOBJECT_CLASS_FUNCTION             3
#define DUK_HOBJECT_CLASS_ARGUMENTS            4
#define DUK_HOBJECT_CLASS_BOOLEAN              5
#define DUK_HOBJECT_CLASS_DATE                 6
#define DUK_HOBJECT_CLASS_ERROR                7
#define DUK_HOBJECT_CLASS_JSON                 8
#define DUK_HOBJECT_CLASS_MATH                 9
#define DUK_HOBJECT_CLASS_NUMBER               10
#define DUK_HOBJECT_CLASS_REGEXP               11
#define DUK_HOBJECT_CLASS_STRING               12
#define DUK_HOBJECT_CLASS_GLOBAL               13
#define DUK_HOBJECT_CLASS_SYMBOL               14
#define DUK_HOBJECT_CLASS_OBJENV               15  /* custom */
#define DUK_HOBJECT_CLASS_DECENV               16  /* custom */
#define DUK_HOBJECT_CLASS_POINTER              17  /* custom */
#define DUK_HOBJECT_CLASS_THREAD               18  /* custom; implies DUK_HOBJECT_IS_THREAD */
#define DUK_HOBJECT_CLASS_BUFOBJ_MIN           19
#define DUK_HOBJECT_CLASS_ARRAYBUFFER          19  /* implies DUK_HOBJECT_IS_BUFOBJ */
#define DUK_HOBJECT_CLASS_DATAVIEW             20
#define DUK_HOBJECT_CLASS_INT8ARRAY            21
#define DUK_HOBJECT_CLASS_UINT8ARRAY           22
#define DUK_HOBJECT_CLASS_UINT8CLAMPEDARRAY    23
#define DUK_HOBJECT_CLASS_INT16ARRAY           24
#define DUK_HOBJECT_CLASS_UINT16ARRAY          25
#define DUK_HOBJECT_CLASS_INT32ARRAY           26
#define DUK_HOBJECT_CLASS_UINT32ARRAY          27
#define DUK_HOBJECT_CLASS_FLOAT32ARRAY         28
#define DUK_HOBJECT_CLASS_FLOAT64ARRAY         29
#define DUK_HOBJECT_CLASS_BUFOBJ_MAX           29
#define DUK_HOBJECT_CLASS_MAX                  29

/* Class masks. */
#define DUK_HOBJECT_CMASK_ALL                  ((1UL << (DUK_HOBJECT_CLASS_MAX + 1)) - 1UL)
#define DUK_HOBJECT_CMASK_NONE                 (1UL << DUK_HOBJECT_CLASS_NONE)
#define DUK_HOBJECT_CMASK_ARGUMENTS            (1UL << DUK_HOBJECT_CLASS_ARGUMENTS)
#define DUK_HOBJECT_CMASK_ARRAY                (1UL << DUK_HOBJECT_CLASS_ARRAY)
#define DUK_HOBJECT_CMASK_BOOLEAN              (1UL << DUK_HOBJECT_CLASS_BOOLEAN)
#define DUK_HOBJECT_CMASK_DATE                 (1UL << DUK_HOBJECT_CLASS_DATE)
#define DUK_HOBJECT_CMASK_ERROR                (1UL << DUK_HOBJECT_CLASS_ERROR)
#define DUK_HOBJECT_CMASK_FUNCTION             (1UL << DUK_HOBJECT_CLASS_FUNCTION)
#define DUK_HOBJECT_CMASK_JSON                 (1UL << DUK_HOBJECT_CLASS_JSON)
#define DUK_HOBJECT_CMASK_MATH                 (1UL << DUK_HOBJECT_CLASS_MATH)
#define DUK_HOBJECT_CMASK_NUMBER               (1UL << DUK_HOBJECT_CLASS_NUMBER)
#define DUK_HOBJECT_CMASK_OBJECT               (1UL << DUK_HOBJECT_CLASS_OBJECT)
#define DUK_HOBJECT_CMASK_REGEXP               (1UL << DUK_HOBJECT_CLASS_REGEXP)
#define DUK_HOBJECT_CMASK_STRING               (1UL << DUK_HOBJECT_CLASS_STRING)
#define DUK_HOBJECT_CMASK_GLOBAL               (1UL << DUK_HOBJECT_CLASS_GLOBAL)
#define DUK_HOBJECT_CMASK_SYMBOL               (1UL << DUK_HOBJECT_CLASS_SYMBOL)
#define DUK_HOBJECT_CMASK_OBJENV               (1UL << DUK_HOBJECT_CLASS_OBJENV)
#define DUK_HOBJECT_CMASK_DECENV               (1UL << DUK_HOBJECT_CLASS_DECENV)
#define DUK_HOBJECT_CMASK_POINTER              (1UL << DUK_HOBJECT_CLASS_POINTER)
#define DUK_HOBJECT_CMASK_ARRAYBUFFER          (1UL << DUK_HOBJECT_CLASS_ARRAYBUFFER)
#define DUK_HOBJECT_CMASK_DATAVIEW             (1UL << DUK_HOBJECT_CLASS_DATAVIEW)
#define DUK_HOBJECT_CMASK_INT8ARRAY            (1UL << DUK_HOBJECT_CLASS_INT8ARRAY)
#define DUK_HOBJECT_CMASK_UINT8ARRAY           (1UL << DUK_HOBJECT_CLASS_UINT8ARRAY)
#define DUK_HOBJECT_CMASK_UINT8CLAMPEDARRAY    (1UL << DUK_HOBJECT_CLASS_UINT8CLAMPEDARRAY)
#define DUK_HOBJECT_CMASK_INT16ARRAY           (1UL << DUK_HOBJECT_CLASS_INT16ARRAY)
#define DUK_HOBJECT_CMASK_UINT16ARRAY          (1UL << DUK_HOBJECT_CLASS_UINT16ARRAY)
#define DUK_HOBJECT_CMASK_INT32ARRAY           (1UL << DUK_HOBJECT_CLASS_INT32ARRAY)
#define DUK_HOBJECT_CMASK_UINT32ARRAY          (1UL << DUK_HOBJECT_CLASS_UINT32ARRAY)
#define DUK_HOBJECT_CMASK_FLOAT32ARRAY         (1UL << DUK_HOBJECT_CLASS_FLOAT32ARRAY)
#define DUK_HOBJECT_CMASK_FLOAT64ARRAY         (1UL << DUK_HOBJECT_CLASS_FLOAT64ARRAY)

#define DUK_HOBJECT_CMASK_ALL_BUFOBJS \
    (DUK_HOBJECT_CMASK_ARRAYBUFFER | \
     DUK_HOBJECT_CMASK_DATAVIEW | \
     DUK_HOBJECT_CMASK_INT8ARRAY | \
     DUK_HOBJECT_CMASK_UINT8ARRAY | \
     DUK_HOBJECT_CMASK_UINT8CLAMPEDARRAY | \
     DUK_HOBJECT_CMASK_INT16ARRAY | \
     DUK_HOBJECT_CMASK_UINT16ARRAY | \
     DUK_HOBJECT_CMASK_INT32ARRAY | \
     DUK_HOBJECT_CMASK_UINT32ARRAY | \
     DUK_HOBJECT_CMASK_FLOAT32ARRAY | \
     DUK_HOBJECT_CMASK_FLOAT64ARRAY)

#define DUK_HOBJECT_IS_OBJENV(h)               (DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_OBJENV)
#define DUK_HOBJECT_IS_DECENV(h)               (DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_DECENV)
#define DUK_HOBJECT_IS_ENV(h)                  (DUK_HOBJECT_IS_OBJENV((h)) || DUK_HOBJECT_IS_DECENV((h)))
#define DUK_HOBJECT_IS_ARRAY(h)                DUK_HOBJECT_HAS_EXOTIC_ARRAY((h))  /* Rely on class Array <=> exotic Array */
#define DUK_HOBJECT_IS_BOUNDFUNC(h)            DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUNDFUNC)
#define DUK_HOBJECT_IS_COMPFUNC(h)             DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPFUNC)
#define DUK_HOBJECT_IS_NATFUNC(h)              DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATFUNC)
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
#define DUK_HOBJECT_IS_BUFOBJ(h)               DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ)
#else
#define DUK_HOBJECT_IS_BUFOBJ(h)               0
#endif
#define DUK_HOBJECT_IS_THREAD(h)               (DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_THREAD)
#if defined(DUK_USE_ES6_PROXY)
#define DUK_HOBJECT_IS_PROXY(h)                DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ((h))
#else
#define DUK_HOBJECT_IS_PROXY(h)                0
#endif

#define DUK_HOBJECT_IS_NONBOUND_FUNCTION(h)    DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, \
                                                        DUK_HOBJECT_FLAG_COMPFUNC | \
                                                        DUK_HOBJECT_FLAG_NATFUNC)

#define DUK_HOBJECT_IS_FUNCTION(h)             DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, \
                                                        DUK_HOBJECT_FLAG_BOUNDFUNC | \
                                                        DUK_HOBJECT_FLAG_COMPFUNC | \
                                                        DUK_HOBJECT_FLAG_NATFUNC)

#define DUK_HOBJECT_IS_CALLABLE(h)             DUK_HOBJECT_HAS_CALLABLE((h))

/* Object has any exotic behavior(s). */
#define DUK_HOBJECT_EXOTIC_BEHAVIOR_FLAGS      (DUK_HOBJECT_FLAG_EXOTIC_ARRAY | \
                                                DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS | \
                                                DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ | \
                                                DUK_HOBJECT_FLAG_BUFOBJ | \
                                                DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ)
#define DUK_HOBJECT_HAS_EXOTIC_BEHAVIOR(h)     DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_EXOTIC_BEHAVIOR_FLAGS)

/* Object has any virtual properties (not counting Proxy behavior). */
#define DUK_HOBJECT_VIRTUAL_PROPERTY_FLAGS     (DUK_HOBJECT_FLAG_EXOTIC_ARRAY | \
                                                DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ | \
                                                DUK_HOBJECT_FLAG_BUFOBJ)
#define DUK_HOBJECT_HAS_VIRTUAL_PROPERTIES(h)  DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_VIRTUAL_PROPERTY_FLAGS)

#define DUK_HOBJECT_HAS_EXTENSIBLE(h)          DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXTENSIBLE)
#define DUK_HOBJECT_HAS_CONSTRUCTABLE(h)       DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CONSTRUCTABLE)
#define DUK_HOBJECT_HAS_CALLABLE(h)            DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CALLABLE)
#define DUK_HOBJECT_HAS_BOUNDFUNC(h)           DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUNDFUNC)
#define DUK_HOBJECT_HAS_COMPFUNC(h)            DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPFUNC)
#define DUK_HOBJECT_HAS_NATFUNC(h)             DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATFUNC)
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
#define DUK_HOBJECT_HAS_BUFOBJ(h)              DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ)
#else
#define DUK_HOBJECT_HAS_BUFOBJ(h)              0
#endif
#define DUK_HOBJECT_HAS_FASTREFS(h)            DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_FASTREFS)
#define DUK_HOBJECT_HAS_ARRAY_PART(h)          DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_ARRAY_PART)
#define DUK_HOBJECT_HAS_STRICT(h)              DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_STRICT)
#define DUK_HOBJECT_HAS_NOTAIL(h)              DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NOTAIL)
#define DUK_HOBJECT_HAS_NEWENV(h)              DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NEWENV)
#define DUK_HOBJECT_HAS_NAMEBINDING(h)         DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NAMEBINDING)
#define DUK_HOBJECT_HAS_CREATEARGS(h)          DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CREATEARGS)
#define DUK_HOBJECT_HAS_HAVE_FINALIZER(h)      DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_HAVE_FINALIZER)
#define DUK_HOBJECT_HAS_EXOTIC_ARRAY(h)        DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARRAY)
#define DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(h)    DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ)
#define DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(h)    DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS)
#if defined(DUK_USE_ES6_PROXY)
#define DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(h)     DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ)
#else
#define DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(h)     0
#endif
#define DUK_HOBJECT_HAS_SPECIAL_CALL(h)        DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_CALL)

#define DUK_HOBJECT_SET_EXTENSIBLE(h)          DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXTENSIBLE)
#define DUK_HOBJECT_SET_CONSTRUCTABLE(h)       DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CONSTRUCTABLE)
#define DUK_HOBJECT_SET_CALLABLE(h)            DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CALLABLE)
#define DUK_HOBJECT_SET_BOUNDFUNC(h)           DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUNDFUNC)
#define DUK_HOBJECT_SET_COMPFUNC(h)            DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPFUNC)
#define DUK_HOBJECT_SET_NATFUNC(h)             DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATFUNC)
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
#define DUK_HOBJECT_SET_BUFOBJ(h)              DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ)
#endif
#define DUK_HOBJECT_SET_FASTREFS(h)            DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_FASTREFS)
#define DUK_HOBJECT_SET_ARRAY_PART(h)          DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_ARRAY_PART)
#define DUK_HOBJECT_SET_STRICT(h)              DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_STRICT)
#define DUK_HOBJECT_SET_NOTAIL(h)              DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NOTAIL)
#define DUK_HOBJECT_SET_NEWENV(h)              DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NEWENV)
#define DUK_HOBJECT_SET_NAMEBINDING(h)         DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NAMEBINDING)
#define DUK_HOBJECT_SET_CREATEARGS(h)          DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CREATEARGS)
#define DUK_HOBJECT_SET_HAVE_FINALIZER(h)      DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_HAVE_FINALIZER)
#define DUK_HOBJECT_SET_EXOTIC_ARRAY(h)        DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARRAY)
#define DUK_HOBJECT_SET_EXOTIC_STRINGOBJ(h)    DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ)
#define DUK_HOBJECT_SET_EXOTIC_ARGUMENTS(h)    DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS)
#if defined(DUK_USE_ES6_PROXY)
#define DUK_HOBJECT_SET_EXOTIC_PROXYOBJ(h)     DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ)
#endif
#define DUK_HOBJECT_SET_SPECIAL_CALL(h)        DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_CALL)

#define DUK_HOBJECT_CLEAR_EXTENSIBLE(h)        DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXTENSIBLE)
#define DUK_HOBJECT_CLEAR_CONSTRUCTABLE(h)     DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CONSTRUCTABLE)
#define DUK_HOBJECT_CLEAR_CALLABLE(h)          DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CALLABLE)
#define DUK_HOBJECT_CLEAR_BOUNDFUNC(h)         DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUNDFUNC)
#define DUK_HOBJECT_CLEAR_COMPFUNC(h)          DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPFUNC)
#define DUK_HOBJECT_CLEAR_NATFUNC(h)           DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATFUNC)
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
#define DUK_HOBJECT_CLEAR_BUFOBJ(h)            DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ)
#endif
#define DUK_HOBJECT_CLEAR_FASTREFS(h)          DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_FASTREFS)
#define DUK_HOBJECT_CLEAR_ARRAY_PART(h)        DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_ARRAY_PART)
#define DUK_HOBJECT_CLEAR_STRICT(h)            DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_STRICT)
#define DUK_HOBJECT_CLEAR_NOTAIL(h)            DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NOTAIL)
#define DUK_HOBJECT_CLEAR_NEWENV(h)            DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NEWENV)
#define DUK_HOBJECT_CLEAR_NAMEBINDING(h)       DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NAMEBINDING)
#define DUK_HOBJECT_CLEAR_CREATEARGS(h)        DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CREATEARGS)
#define DUK_HOBJECT_CLEAR_HAVE_FINALIZER(h)    DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_HAVE_FINALIZER)
#define DUK_HOBJECT_CLEAR_EXOTIC_ARRAY(h)      DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARRAY)
#define DUK_HOBJECT_CLEAR_EXOTIC_STRINGOBJ(h)  DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ)
#define DUK_HOBJECT_CLEAR_EXOTIC_ARGUMENTS(h)  DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS)
#if defined(DUK_USE_ES6_PROXY)
#define DUK_HOBJECT_CLEAR_EXOTIC_PROXYOBJ(h)   DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ)
#endif
#define DUK_HOBJECT_CLEAR_SPECIAL_CALL(h)      DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_CALL)

/* Object can/cannot use FASTREFS, i.e. has no strong reference fields beyond
 * duk_hobject base header.  This is used just for asserts so doesn't need to
 * be optimized.
 */
#define DUK_HOBJECT_PROHIBITS_FASTREFS(h) \
    (DUK_HOBJECT_IS_COMPFUNC((h)) || DUK_HOBJECT_IS_DECENV((h)) || DUK_HOBJECT_IS_OBJENV((h)) || \
     DUK_HOBJECT_IS_BUFOBJ((h)) || DUK_HOBJECT_IS_THREAD((h)) || DUK_HOBJECT_IS_PROXY((h)) || \
     DUK_HOBJECT_IS_BOUNDFUNC((h)))
#define DUK_HOBJECT_ALLOWS_FASTREFS(h) (!DUK_HOBJECT_PROHIBITS_FASTREFS((h)))

/* Flags used for property attributes in duk_propdesc and packed flags.
 * Must fit into 8 bits.
 */
#define DUK_PROPDESC_FLAG_WRITABLE              (1U << 0)    /* E5 Section 8.6.1 */
#define DUK_PROPDESC_FLAG_ENUMERABLE            (1U << 1)    /* E5 Section 8.6.1 */
#define DUK_PROPDESC_FLAG_CONFIGURABLE          (1U << 2)    /* E5 Section 8.6.1 */
#define DUK_PROPDESC_FLAG_ACCESSOR              (1U << 3)    /* accessor */
#define DUK_PROPDESC_FLAG_VIRTUAL               (1U << 4)    /* property is virtual: used in duk_propdesc, never stored
                                                             * (used by e.g. buffer virtual properties)
                                                             */
#define DUK_PROPDESC_FLAGS_MASK                 (DUK_PROPDESC_FLAG_WRITABLE | \
                                                 DUK_PROPDESC_FLAG_ENUMERABLE | \
                                                 DUK_PROPDESC_FLAG_CONFIGURABLE | \
                                                 DUK_PROPDESC_FLAG_ACCESSOR)

/* Additional flags which are passed in the same flags argument as property
 * flags but are not stored in object properties.
 */
#define DUK_PROPDESC_FLAG_NO_OVERWRITE          (1U << 4)    /* internal define property: skip write silently if exists */

/* Convenience defines for property attributes. */
#define DUK_PROPDESC_FLAGS_NONE                 0
#define DUK_PROPDESC_FLAGS_W                    (DUK_PROPDESC_FLAG_WRITABLE)
#define DUK_PROPDESC_FLAGS_E                    (DUK_PROPDESC_FLAG_ENUMERABLE)
#define DUK_PROPDESC_FLAGS_C                    (DUK_PROPDESC_FLAG_CONFIGURABLE)
#define DUK_PROPDESC_FLAGS_WE                   (DUK_PROPDESC_FLAG_WRITABLE | DUK_PROPDESC_FLAG_ENUMERABLE)
#define DUK_PROPDESC_FLAGS_WC                   (DUK_PROPDESC_FLAG_WRITABLE | DUK_PROPDESC_FLAG_CONFIGURABLE)
#define DUK_PROPDESC_FLAGS_EC                   (DUK_PROPDESC_FLAG_ENUMERABLE | DUK_PROPDESC_FLAG_CONFIGURABLE)
#define DUK_PROPDESC_FLAGS_WEC                  (DUK_PROPDESC_FLAG_WRITABLE | \
                                                 DUK_PROPDESC_FLAG_ENUMERABLE | \
                                                 DUK_PROPDESC_FLAG_CONFIGURABLE)

/* Flags for duk_hobject_get_own_propdesc() and variants. */
#define DUK_GETDESC_FLAG_PUSH_VALUE          (1U << 0)  /* push value to stack */
#define DUK_GETDESC_FLAG_IGNORE_PROTOLOOP    (1U << 1)  /* don't throw for prototype loop */

/*
 *  Macro for object validity check
 *
 *  Assert for currently guaranteed relations between flags, for instance.
 */

#if defined(DUK_USE_ASSERTIONS)
DUK_INTERNAL_DECL void duk_hobject_assert_valid(duk_hobject *h);
#define DUK_HOBJECT_ASSERT_VALID(h)  do { duk_hobject_assert_valid((h)); } while (0)
#else
#define DUK_HOBJECT_ASSERT_VALID(h)  do {} while (0)
#endif

/*
 *  Macros to access the 'props' allocation.
 */

#if defined(DUK_USE_HEAPPTR16)
#define DUK_HOBJECT_GET_PROPS(heap,h) \
    ((duk_uint8_t *) DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, ((duk_heaphdr *) (h))->h_extra16))
#define DUK_HOBJECT_SET_PROPS(heap,h,x) do { \
        ((duk_heaphdr *) (h))->h_extra16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (x)); \
    } while (0)
#else
#define DUK_HOBJECT_GET_PROPS(heap,h) \
    ((h)->props)
#define DUK_HOBJECT_SET_PROPS(heap,h,x) do { \
        (h)->props = (duk_uint8_t *) (x); \
    } while (0)
#endif

#if defined(DUK_USE_HOBJECT_LAYOUT_1)
/* LAYOUT 1 */
#define DUK_HOBJECT_E_GET_KEY_BASE(heap,h) \
    ((duk_hstring **) (void *) ( \
        DUK_HOBJECT_GET_PROPS((heap), (h)) \
    ))
#define DUK_HOBJECT_E_GET_VALUE_BASE(heap,h) \
    ((duk_propvalue *) (void *) ( \
        DUK_HOBJECT_GET_PROPS((heap), (h)) + \
            DUK_HOBJECT_GET_ESIZE((h)) * sizeof(duk_hstring *) \
    ))
#define DUK_HOBJECT_E_GET_FLAGS_BASE(heap,h) \
    ((duk_uint8_t *) (void *) ( \
        DUK_HOBJECT_GET_PROPS((heap), (h)) + DUK_HOBJECT_GET_ESIZE((h)) * (sizeof(duk_hstring *) + sizeof(duk_propvalue)) \
    ))
#define DUK_HOBJECT_A_GET_BASE(heap,h) \
    ((duk_tval *) (void *) ( \
        DUK_HOBJECT_GET_PROPS((heap), (h)) + \
            DUK_HOBJECT_GET_ESIZE((h)) * (sizeof(duk_hstring *) + sizeof(duk_propvalue) + sizeof(duk_uint8_t)) \
    ))
#define DUK_HOBJECT_H_GET_BASE(heap,h) \
    ((duk_uint32_t *) (void *) ( \
        DUK_HOBJECT_GET_PROPS((heap), (h)) + \
            DUK_HOBJECT_GET_ESIZE((h)) * (sizeof(duk_hstring *) + sizeof(duk_propvalue) + sizeof(duk_uint8_t)) + \
            DUK_HOBJECT_GET_ASIZE((h)) * sizeof(duk_tval) \
    ))
#define DUK_HOBJECT_P_COMPUTE_SIZE(n_ent,n_arr,n_hash) \
    ( \
        (n_ent) * (sizeof(duk_hstring *) + sizeof(duk_propvalue) + sizeof(duk_uint8_t)) + \
        (n_arr) * sizeof(duk_tval) + \
        (n_hash) * sizeof(duk_uint32_t) \
    )
#define DUK_HOBJECT_P_SET_REALLOC_PTRS(p_base,set_e_k,set_e_pv,set_e_f,set_a,set_h,n_ent,n_arr,n_hash)  do { \
        (set_e_k) = (duk_hstring **) (void *) (p_base); \
        (set_e_pv) = (duk_propvalue *) (void *) ((set_e_k) + (n_ent)); \
        (set_e_f) = (duk_uint8_t *) (void *) ((set_e_pv) + (n_ent)); \
        (set_a) = (duk_tval *) (void *) ((set_e_f) + (n_ent)); \
        (set_h) = (duk_uint32_t *) (void *) ((set_a) + (n_arr)); \
    } while (0)
#elif defined(DUK_USE_HOBJECT_LAYOUT_2)
/* LAYOUT 2 */
#if (DUK_USE_ALIGN_BY == 4)
#define DUK_HOBJECT_E_FLAG_PADDING(e_sz) ((4 - (e_sz)) & 0x03)
#elif (DUK_USE_ALIGN_BY == 8)
#define DUK_HOBJECT_E_FLAG_PADDING(e_sz) ((8 - (e_sz)) & 0x07)
#elif (DUK_USE_ALIGN_BY == 1)
#define DUK_HOBJECT_E_FLAG_PADDING(e_sz) 0
#else
#error invalid DUK_USE_ALIGN_BY
#endif
#define DUK_HOBJECT_E_GET_KEY_BASE(heap,h) \
    ((duk_hstring **) (void *) ( \
        DUK_HOBJECT_GET_PROPS((heap), (h)) + \
            DUK_HOBJECT_GET_ESIZE((h)) * sizeof(duk_propvalue) \
    ))
#define DUK_HOBJECT_E_GET_VALUE_BASE(heap,h) \
    ((duk_propvalue *) (void *) ( \
        DUK_HOBJECT_GET_PROPS((heap), (h)) \
    ))
#define DUK_HOBJECT_E_GET_FLAGS_BASE(heap,h) \
    ((duk_uint8_t *) (void *) ( \
        DUK_HOBJECT_GET_PROPS((heap), (h)) + DUK_HOBJECT_GET_ESIZE((h)) * (sizeof(duk_hstring *) + sizeof(duk_propvalue)) \
    ))
#define DUK_HOBJECT_A_GET_BASE(heap,h) \
    ((duk_tval *) (void *) ( \
        DUK_HOBJECT_GET_PROPS((heap), (h)) + \
            DUK_HOBJECT_GET_ESIZE((h)) * (sizeof(duk_hstring *) + sizeof(duk_propvalue) + sizeof(duk_uint8_t)) + \
            DUK_HOBJECT_E_FLAG_PADDING(DUK_HOBJECT_GET_ESIZE((h))) \
    ))
#define DUK_HOBJECT_H_GET_BASE(heap,h) \
    ((duk_uint32_t *) (void *) ( \
        DUK_HOBJECT_GET_PROPS((heap), (h)) + \
            DUK_HOBJECT_GET_ESIZE((h)) * (sizeof(duk_hstring *) + sizeof(duk_propvalue) + sizeof(duk_uint8_t)) + \
            DUK_HOBJECT_E_FLAG_PADDING(DUK_HOBJECT_GET_ESIZE((h))) + \
            DUK_HOBJECT_GET_ASIZE((h)) * sizeof(duk_tval) \
    ))
#define DUK_HOBJECT_P_COMPUTE_SIZE(n_ent,n_arr,n_hash) \
    ( \
        (n_ent) * (sizeof(duk_hstring *) + sizeof(duk_propvalue) + sizeof(duk_uint8_t)) + \
        DUK_HOBJECT_E_FLAG_PADDING((n_ent)) + \
        (n_arr) * sizeof(duk_tval) + \
        (n_hash) * sizeof(duk_uint32_t) \
    )
#define DUK_HOBJECT_P_SET_REALLOC_PTRS(p_base,set_e_k,set_e_pv,set_e_f,set_a,set_h,n_ent,n_arr,n_hash)  do { \
        (set_e_pv) = (duk_propvalue *) (void *) (p_base); \
        (set_e_k) = (duk_hstring **) (void *) ((set_e_pv) + (n_ent)); \
        (set_e_f) = (duk_uint8_t *) (void *) ((set_e_k) + (n_ent)); \
        (set_a) = (duk_tval *) (void *) (((duk_uint8_t *) (set_e_f)) + \
                                         sizeof(duk_uint8_t) * (n_ent) + \
                                         DUK_HOBJECT_E_FLAG_PADDING((n_ent))); \
        (set_h) = (duk_uint32_t *) (void *) ((set_a) + (n_arr)); \
    } while (0)
#elif defined(DUK_USE_HOBJECT_LAYOUT_3)
/* LAYOUT 3 */
#define DUK_HOBJECT_E_GET_KEY_BASE(heap,h) \
    ((duk_hstring **) (void *) ( \
        DUK_HOBJECT_GET_PROPS((heap), (h)) + \
            DUK_HOBJECT_GET_ESIZE((h)) * sizeof(duk_propvalue) + \
            DUK_HOBJECT_GET_ASIZE((h)) * sizeof(duk_tval) \
    ))
#define DUK_HOBJECT_E_GET_VALUE_BASE(heap,h) \
    ((duk_propvalue *) (void *) ( \
        DUK_HOBJECT_GET_PROPS((heap), (h)) \
    ))
#define DUK_HOBJECT_E_GET_FLAGS_BASE(heap,h) \
    ((duk_uint8_t *) (void *) ( \
        DUK_HOBJECT_GET_PROPS((heap), (h)) + \
            DUK_HOBJECT_GET_ESIZE((h)) * (sizeof(duk_propvalue) + sizeof(duk_hstring *)) + \
            DUK_HOBJECT_GET_ASIZE((h)) * sizeof(duk_tval) + \
            DUK_HOBJECT_GET_HSIZE((h)) * sizeof(duk_uint32_t) \
    ))
#define DUK_HOBJECT_A_GET_BASE(heap,h) \
    ((duk_tval *) (void *) ( \
        DUK_HOBJECT_GET_PROPS((heap), (h)) + \
            DUK_HOBJECT_GET_ESIZE((h)) * sizeof(duk_propvalue) \
    ))
#define DUK_HOBJECT_H_GET_BASE(heap,h) \
    ((duk_uint32_t *) (void *) ( \
        DUK_HOBJECT_GET_PROPS((heap), (h)) + \
            DUK_HOBJECT_GET_ESIZE((h)) * (sizeof(duk_propvalue) + sizeof(duk_hstring *)) + \
            DUK_HOBJECT_GET_ASIZE((h)) * sizeof(duk_tval) \
    ))
#define DUK_HOBJECT_P_COMPUTE_SIZE(n_ent,n_arr,n_hash) \
    ( \
        (n_ent) * (sizeof(duk_propvalue) + sizeof(duk_hstring *) + sizeof(duk_uint8_t)) + \
        (n_arr) * sizeof(duk_tval) + \
        (n_hash) * sizeof(duk_uint32_t) \
    )
#define DUK_HOBJECT_P_SET_REALLOC_PTRS(p_base,set_e_k,set_e_pv,set_e_f,set_a,set_h,n_ent,n_arr,n_hash)  do { \
        (set_e_pv) = (duk_propvalue *) (void *) (p_base); \
        (set_a) = (duk_tval *) (void *) ((set_e_pv) + (n_ent)); \
        (set_e_k) = (duk_hstring **) (void *) ((set_a) + (n_arr)); \
        (set_h) = (duk_uint32_t *) (void *) ((set_e_k) + (n_ent)); \
        (set_e_f) = (duk_uint8_t *) (void *) ((set_h) + (n_hash)); \
    } while (0)
#else
#error invalid hobject layout defines
#endif  /* hobject property layout */

#define DUK_HOBJECT_P_ALLOC_SIZE(h) \
    DUK_HOBJECT_P_COMPUTE_SIZE(DUK_HOBJECT_GET_ESIZE((h)), DUK_HOBJECT_GET_ASIZE((h)), DUK_HOBJECT_GET_HSIZE((h)))

#define DUK_HOBJECT_E_GET_KEY(heap,h,i)              (DUK_HOBJECT_E_GET_KEY_BASE((heap), (h))[(i)])
#define DUK_HOBJECT_E_GET_KEY_PTR(heap,h,i)          (&DUK_HOBJECT_E_GET_KEY_BASE((heap), (h))[(i)])
#define DUK_HOBJECT_E_GET_VALUE(heap,h,i)            (DUK_HOBJECT_E_GET_VALUE_BASE((heap), (h))[(i)])
#define DUK_HOBJECT_E_GET_VALUE_PTR(heap,h,i)        (&DUK_HOBJECT_E_GET_VALUE_BASE((heap), (h))[(i)])
#define DUK_HOBJECT_E_GET_VALUE_TVAL(heap,h,i)       (DUK_HOBJECT_E_GET_VALUE((heap), (h), (i)).v)
#define DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(heap,h,i)   (&DUK_HOBJECT_E_GET_VALUE((heap), (h), (i)).v)
#define DUK_HOBJECT_E_GET_VALUE_GETTER(heap,h,i)     (DUK_HOBJECT_E_GET_VALUE((heap), (h), (i)).a.get)
#define DUK_HOBJECT_E_GET_VALUE_GETTER_PTR(heap,h,i) (&DUK_HOBJECT_E_GET_VALUE((heap), (h), (i)).a.get)
#define DUK_HOBJECT_E_GET_VALUE_SETTER(heap,h,i)     (DUK_HOBJECT_E_GET_VALUE((heap), (h), (i)).a.set)
#define DUK_HOBJECT_E_GET_VALUE_SETTER_PTR(heap,h,i) (&DUK_HOBJECT_E_GET_VALUE((heap), (h), (i)).a.set)
#define DUK_HOBJECT_E_GET_FLAGS(heap,h,i)            (DUK_HOBJECT_E_GET_FLAGS_BASE((heap), (h))[(i)])
#define DUK_HOBJECT_E_GET_FLAGS_PTR(heap,h,i)        (&DUK_HOBJECT_E_GET_FLAGS_BASE((heap), (h))[(i)])
#define DUK_HOBJECT_A_GET_VALUE(heap,h,i)            (DUK_HOBJECT_A_GET_BASE((heap), (h))[(i)])
#define DUK_HOBJECT_A_GET_VALUE_PTR(heap,h,i)        (&DUK_HOBJECT_A_GET_BASE((heap), (h))[(i)])
#define DUK_HOBJECT_H_GET_INDEX(heap,h,i)            (DUK_HOBJECT_H_GET_BASE((heap), (h))[(i)])
#define DUK_HOBJECT_H_GET_INDEX_PTR(heap,h,i)        (&DUK_HOBJECT_H_GET_BASE((heap), (h))[(i)])

#define DUK_HOBJECT_E_SET_KEY(heap,h,i,k)  do { \
        DUK_HOBJECT_E_GET_KEY((heap), (h), (i)) = (k); \
    } while (0)
#define DUK_HOBJECT_E_SET_VALUE(heap,h,i,v)  do { \
        DUK_HOBJECT_E_GET_VALUE((heap), (h), (i)) = (v); \
    } while (0)
#define DUK_HOBJECT_E_SET_VALUE_TVAL(heap,h,i,v)  do { \
        DUK_HOBJECT_E_GET_VALUE((heap), (h), (i)).v = (v); \
    } while (0)
#define DUK_HOBJECT_E_SET_VALUE_GETTER(heap,h,i,v)  do { \
        DUK_HOBJECT_E_GET_VALUE((heap), (h), (i)).a.get = (v); \
    } while (0)
#define DUK_HOBJECT_E_SET_VALUE_SETTER(heap,h,i,v)  do { \
        DUK_HOBJECT_E_GET_VALUE((heap), (h), (i)).a.set = (v); \
    } while (0)
#define DUK_HOBJECT_E_SET_FLAGS(heap,h,i,f)  do { \
        DUK_HOBJECT_E_GET_FLAGS((heap), (h), (i)) = (duk_uint8_t) (f); \
    } while (0)
#define DUK_HOBJECT_A_SET_VALUE(heap,h,i,v)  do { \
        DUK_HOBJECT_A_GET_VALUE((heap), (h), (i)) = (v); \
    } while (0)
#define DUK_HOBJECT_A_SET_VALUE_TVAL(heap,h,i,v) \
    DUK_HOBJECT_A_SET_VALUE((heap), (h), (i), (v))  /* alias for above */
#define DUK_HOBJECT_H_SET_INDEX(heap,h,i,v)  do { \
        DUK_HOBJECT_H_GET_INDEX((heap), (h), (i)) = (v); \
    } while (0)

#define DUK_HOBJECT_E_SET_FLAG_BITS(heap,h,i,mask)  do { \
        DUK_HOBJECT_E_GET_FLAGS_BASE((heap), (h))[(i)] |= (mask); \
    } while (0)

#define DUK_HOBJECT_E_CLEAR_FLAG_BITS(heap,h,i,mask)  do { \
        DUK_HOBJECT_E_GET_FLAGS_BASE((heap), (h))[(i)] &= ~(mask); \
    } while (0)

#define DUK_HOBJECT_E_SLOT_IS_WRITABLE(heap,h,i)     ((DUK_HOBJECT_E_GET_FLAGS((heap), (h), (i)) & DUK_PROPDESC_FLAG_WRITABLE) != 0)
#define DUK_HOBJECT_E_SLOT_IS_ENUMERABLE(heap,h,i)   ((DUK_HOBJECT_E_GET_FLAGS((heap), (h), (i)) & DUK_PROPDESC_FLAG_ENUMERABLE) != 0)
#define DUK_HOBJECT_E_SLOT_IS_CONFIGURABLE(heap,h,i) ((DUK_HOBJECT_E_GET_FLAGS((heap), (h), (i)) & DUK_PROPDESC_FLAG_CONFIGURABLE) != 0)
#define DUK_HOBJECT_E_SLOT_IS_ACCESSOR(heap,h,i)     ((DUK_HOBJECT_E_GET_FLAGS((heap), (h), (i)) & DUK_PROPDESC_FLAG_ACCESSOR) != 0)

#define DUK_HOBJECT_E_SLOT_SET_WRITABLE(heap,h,i)        DUK_HOBJECT_E_SET_FLAG_BITS((heap), (h), (i),DUK_PROPDESC_FLAG_WRITABLE)
#define DUK_HOBJECT_E_SLOT_SET_ENUMERABLE(heap,h,i)      DUK_HOBJECT_E_SET_FLAG_BITS((heap), (h), (i),DUK_PROPDESC_FLAG_ENUMERABLE)
#define DUK_HOBJECT_E_SLOT_SET_CONFIGURABLE(heap,h,i)    DUK_HOBJECT_E_SET_FLAG_BITS((heap), (h), (i),DUK_PROPDESC_FLAG_CONFIGURABLE)
#define DUK_HOBJECT_E_SLOT_SET_ACCESSOR(heap,h,i)        DUK_HOBJECT_E_SET_FLAG_BITS((heap), (h), (i),DUK_PROPDESC_FLAG_ACCESSOR)

#define DUK_HOBJECT_E_SLOT_CLEAR_WRITABLE(heap,h,i)      DUK_HOBJECT_E_CLEAR_FLAG_BITS((heap), (h), (i),DUK_PROPDESC_FLAG_WRITABLE)
#define DUK_HOBJECT_E_SLOT_CLEAR_ENUMERABLE(heap,h,i)    DUK_HOBJECT_E_CLEAR_FLAG_BITS((heap), (h), (i),DUK_PROPDESC_FLAG_ENUMERABLE)
#define DUK_HOBJECT_E_SLOT_CLEAR_CONFIGURABLE(heap,h,i)  DUK_HOBJECT_E_CLEAR_FLAG_BITS((heap), (h), (i),DUK_PROPDESC_FLAG_CONFIGURABLE)
#define DUK_HOBJECT_E_SLOT_CLEAR_ACCESSOR(heap,h,i)      DUK_HOBJECT_E_CLEAR_FLAG_BITS((heap), (h), (i),DUK_PROPDESC_FLAG_ACCESSOR)

#define DUK_PROPDESC_IS_WRITABLE(p)             (((p)->flags & DUK_PROPDESC_FLAG_WRITABLE) != 0)
#define DUK_PROPDESC_IS_ENUMERABLE(p)           (((p)->flags & DUK_PROPDESC_FLAG_ENUMERABLE) != 0)
#define DUK_PROPDESC_IS_CONFIGURABLE(p)         (((p)->flags & DUK_PROPDESC_FLAG_CONFIGURABLE) != 0)
#define DUK_PROPDESC_IS_ACCESSOR(p)             (((p)->flags & DUK_PROPDESC_FLAG_ACCESSOR) != 0)

#define DUK_HOBJECT_HASHIDX_UNUSED              0xffffffffUL
#define DUK_HOBJECT_HASHIDX_DELETED             0xfffffffeUL

/*
 *  Macros for accessing size fields
 */

#if defined(DUK_USE_OBJSIZES16)
#define DUK_HOBJECT_GET_ESIZE(h) ((h)->e_size16)
#define DUK_HOBJECT_SET_ESIZE(h,v) do { (h)->e_size16 = (v); } while (0)
#define DUK_HOBJECT_GET_ENEXT(h) ((h)->e_next16)
#define DUK_HOBJECT_SET_ENEXT(h,v) do { (h)->e_next16 = (v); } while (0)
#define DUK_HOBJECT_POSTINC_ENEXT(h) ((h)->e_next16++)
#define DUK_HOBJECT_GET_ASIZE(h) ((h)->a_size16)
#define DUK_HOBJECT_SET_ASIZE(h,v) do { (h)->a_size16 = (v); } while (0)
#if defined(DUK_USE_HOBJECT_HASH_PART)
#define DUK_HOBJECT_GET_HSIZE(h) ((h)->h_size16)
#define DUK_HOBJECT_SET_HSIZE(h,v) do { (h)->h_size16 = (v); } while (0)
#else
#define DUK_HOBJECT_GET_HSIZE(h) 0
#define DUK_HOBJECT_SET_HSIZE(h,v) do { DUK_ASSERT((v) == 0); } while (0)
#endif
#else
#define DUK_HOBJECT_GET_ESIZE(h) ((h)->e_size)
#define DUK_HOBJECT_SET_ESIZE(h,v) do { (h)->e_size = (v); } while (0)
#define DUK_HOBJECT_GET_ENEXT(h) ((h)->e_next)
#define DUK_HOBJECT_SET_ENEXT(h,v) do { (h)->e_next = (v); } while (0)
#define DUK_HOBJECT_POSTINC_ENEXT(h) ((h)->e_next++)
#define DUK_HOBJECT_GET_ASIZE(h) ((h)->a_size)
#define DUK_HOBJECT_SET_ASIZE(h,v) do { (h)->a_size = (v); } while (0)
#if defined(DUK_USE_HOBJECT_HASH_PART)
#define DUK_HOBJECT_GET_HSIZE(h) ((h)->h_size)
#define DUK_HOBJECT_SET_HSIZE(h,v) do { (h)->h_size = (v); } while (0)
#else
#define DUK_HOBJECT_GET_HSIZE(h) 0
#define DUK_HOBJECT_SET_HSIZE(h,v) do { DUK_ASSERT((v) == 0); } while (0)
#endif
#endif

/*
 *  Misc
 */

/* Maximum prototype traversal depth.  Sanity limit which handles e.g.
 * prototype loops (even complex ones like 1->2->3->4->2->3->4->2->3->4).
 */
#define DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY      10000L

/*
 *  ECMAScript [[Class]]
 */

/* range check not necessary because all 4-bit values are mapped */
#define DUK_HOBJECT_CLASS_NUMBER_TO_STRIDX(n)  duk_class_number_to_stridx[(n)]

#define DUK_HOBJECT_GET_CLASS_STRING(heap,h)          \
    DUK_HEAP_GET_STRING( \
        (heap), \
        DUK_HOBJECT_CLASS_NUMBER_TO_STRIDX(DUK_HOBJECT_GET_CLASS_NUMBER((h))) \
    )

/*
 *  Macros for property handling
 */

#if defined(DUK_USE_HEAPPTR16)
#define DUK_HOBJECT_GET_PROTOTYPE(heap,h) \
    ((duk_hobject *) DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (h)->prototype16))
#define DUK_HOBJECT_SET_PROTOTYPE(heap,h,x) do { \
        (h)->prototype16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (x)); \
    } while (0)
#else
#define DUK_HOBJECT_GET_PROTOTYPE(heap,h) \
    ((h)->prototype)
#define DUK_HOBJECT_SET_PROTOTYPE(heap,h,x) do { \
        (h)->prototype = (x); \
    } while (0)
#endif

/* Set prototype, DECREF earlier value, INCREF new value (tolerating NULLs). */
#define DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr,h,p)       duk_hobject_set_prototype_updref((thr), (h), (p))

/* Set initial prototype, assume NULL previous prototype, INCREF new value,
 * tolerate NULL.
 */
#define DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr,h,proto) do { \
        duk_hthread *duk__thr = (thr); \
        duk_hobject *duk__obj = (h); \
        duk_hobject *duk__proto = (proto); \
        DUK_UNREF(duk__thr); \
        DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(duk__thr->heap, duk__obj) == NULL); \
        DUK_HOBJECT_SET_PROTOTYPE(duk__thr->heap, duk__obj, duk__proto); \
        DUK_HOBJECT_INCREF_ALLOWNULL(duk__thr, duk__proto); \
    } while (0)

/*
 *  Finalizer check
 */

#if defined(DUK_USE_HEAPPTR16)
#define DUK_HOBJECT_HAS_FINALIZER_FAST(heap,h) duk_hobject_has_finalizer_fast_raw((heap), (h))
#else
#define DUK_HOBJECT_HAS_FINALIZER_FAST(heap,h) duk_hobject_has_finalizer_fast_raw((h))
#endif

/*
 *  Resizing and hash behavior
 */

/* Sanity limit on max number of properties (allocated, not necessarily used).
 * This is somewhat arbitrary, but if we're close to 2**32 properties some
 * algorithms will fail (e.g. hash size selection, next prime selection).
 * Also, we use negative array/entry table indices to indicate 'not found',
 * so anything above 0x80000000 will cause trouble now.
 */
#if defined(DUK_USE_OBJSIZES16)
#define DUK_HOBJECT_MAX_PROPERTIES       0x0000ffffUL
#else
#define DUK_HOBJECT_MAX_PROPERTIES       0x3fffffffUL   /* 2**30-1 ~= 1G properties */
#endif

/* internal align target for props allocation, must be 2*n for some n */
#if (DUK_USE_ALIGN_BY == 4)
#define DUK_HOBJECT_ALIGN_TARGET         4
#elif (DUK_USE_ALIGN_BY == 8)
#define DUK_HOBJECT_ALIGN_TARGET         8
#elif (DUK_USE_ALIGN_BY == 1)
#define DUK_HOBJECT_ALIGN_TARGET         1
#else
#error invalid DUK_USE_ALIGN_BY
#endif

/*
 *  PC-to-line constants
 */

#define DUK_PC2LINE_SKIP    64

/* maximum length for a SKIP-1 diffstream: 35 bits per entry, rounded up to bytes */
#define DUK_PC2LINE_MAX_DIFF_LENGTH    (((DUK_PC2LINE_SKIP - 1) * 35 + 7) / 8)

/*
 *  Struct defs
 */

struct duk_propaccessor {
    duk_hobject *get;
    duk_hobject *set;
};

union duk_propvalue {
    /* The get/set pointers could be 16-bit pointer compressed but it
     * would make no difference on 32-bit platforms because duk_tval is
     * 8 bytes or more anyway.
     */
    duk_tval v;
    duk_propaccessor a;
};

struct duk_propdesc {
    /* read-only values 'lifted' for ease of use */
    duk_small_uint_t flags;
    duk_hobject *get;
    duk_hobject *set;

    /* for updating (all are set to < 0 for virtual properties) */
    duk_int_t e_idx;  /* prop index in 'entry part', < 0 if not there */
    duk_int_t h_idx;  /* prop index in 'hash part', < 0 if not there */
    duk_int_t a_idx;  /* prop index in 'array part', < 0 if not there */
};

struct duk_hobject {
    duk_heaphdr hdr;

    /*
     *  'props' contains {key,value,flags} entries, optional array entries, and
     *  an optional hash lookup table for non-array entries in a single 'sliced'
     *  allocation.  There are several layout options, which differ slightly in
     *  generated code size/speed and alignment/padding; duk_features.h selects
     *  the layout used.
     *
     *  Layout 1 (DUK_USE_HOBJECT_LAYOUT_1):
     *
     *    e_size * sizeof(duk_hstring *)         bytes of   entry keys (e_next gc reachable)
     *    e_size * sizeof(duk_propvalue)         bytes of   entry values (e_next gc reachable)
     *    e_size * sizeof(duk_uint8_t)           bytes of   entry flags (e_next gc reachable)
     *    a_size * sizeof(duk_tval)              bytes of   (opt) array values (plain only) (all gc reachable)
     *    h_size * sizeof(duk_uint32_t)          bytes of   (opt) hash indexes to entries (e_size),
     *                                                      0xffffffffUL = unused, 0xfffffffeUL = deleted
     *
     *  Layout 2 (DUK_USE_HOBJECT_LAYOUT_2):
     *
     *    e_size * sizeof(duk_propvalue)         bytes of   entry values (e_next gc reachable)
     *    e_size * sizeof(duk_hstring *)         bytes of   entry keys (e_next gc reachable)
     *    e_size * sizeof(duk_uint8_t) + pad     bytes of   entry flags (e_next gc reachable)
     *    a_size * sizeof(duk_tval)              bytes of   (opt) array values (plain only) (all gc reachable)
     *    h_size * sizeof(duk_uint32_t)          bytes of   (opt) hash indexes to entries (e_size),
     *                                                      0xffffffffUL = unused, 0xfffffffeUL = deleted
     *
     *  Layout 3 (DUK_USE_HOBJECT_LAYOUT_3):
     *
     *    e_size * sizeof(duk_propvalue)         bytes of   entry values (e_next gc reachable)
     *    a_size * sizeof(duk_tval)              bytes of   (opt) array values (plain only) (all gc reachable)
     *    e_size * sizeof(duk_hstring *)         bytes of   entry keys (e_next gc reachable)
     *    h_size * sizeof(duk_uint32_t)          bytes of   (opt) hash indexes to entries (e_size),
     *                                                      0xffffffffUL = unused, 0xfffffffeUL = deleted
     *    e_size * sizeof(duk_uint8_t)           bytes of   entry flags (e_next gc reachable)
     *
     *  In layout 1, the 'e_next' count is rounded to 4 or 8 on platforms
     *  requiring 4 or 8 byte alignment.  This ensures proper alignment
     *  for the entries, at the cost of memory footprint.  However, it's
     *  probably preferable to use another layout on such platforms instead.
     *
     *  In layout 2, the key and value parts are swapped to avoid padding
     *  the key array on platforms requiring alignment by 8.  The flags part
     *  is padded to get alignment for array entries.  The 'e_next' count does
     *  not need to be rounded as in layout 1.
     *
     *  In layout 3, entry values and array values are always aligned properly,
     *  and assuming pointers are at most 8 bytes, so are the entry keys.  Hash
     *  indices will be properly aligned (assuming pointers are at least 4 bytes).
     *  Finally, flags don't need additional alignment.  This layout provides
     *  compact allocations without padding (even on platforms with alignment
     *  requirements) at the cost of a bit slower lookups.
     *
     *  Objects with few keys don't have a hash index; keys are looked up linearly,
     *  which is cache efficient because the keys are consecutive.  Larger objects
     *  have a hash index part which contains integer indexes to the entries part.
     *
     *  A single allocation reduces memory allocation overhead but requires more
     *  work when any part needs to be resized.  A sliced allocation for entries
     *  makes linear key matching faster on most platforms (more locality) and
     *  skimps on flags size (which would be followed by 3 bytes of padding in
     *  most architectures if entries were placed in a struct).
     *
     *  'props' also contains internal properties distinguished with a non-BMP
     *  prefix.  Often used properties should be placed early in 'props' whenever
     *  possible to make accessing them as fast a possible.
     */

#if defined(DUK_USE_HEAPPTR16)
    /* Located in duk_heaphdr h_extra16.  Subclasses of duk_hobject (like
     * duk_hcompfunc) are not free to use h_extra16 for this reason.
     */
#else
    duk_uint8_t *props;
#endif

    /* prototype: the only internal property lifted outside 'e' as it is so central */
#if defined(DUK_USE_HEAPPTR16)
    duk_uint16_t prototype16;
#else
    duk_hobject *prototype;
#endif

#if defined(DUK_USE_OBJSIZES16)
    duk_uint16_t e_size16;
    duk_uint16_t e_next16;
    duk_uint16_t a_size16;
#if defined(DUK_USE_HOBJECT_HASH_PART)
    duk_uint16_t h_size16;
#endif
#else
    duk_uint32_t e_size;  /* entry part size */
    duk_uint32_t e_next;  /* index for next new key ([0,e_next[ are gc reachable) */
    duk_uint32_t a_size;  /* array part size (entirely gc reachable) */
#if defined(DUK_USE_HOBJECT_HASH_PART)
    duk_uint32_t h_size;  /* hash part size or 0 if unused */
#endif
#endif
};

/*
 *  Exposed data
 */

#if !defined(DUK_SINGLE_FILE)
DUK_INTERNAL_DECL duk_uint8_t duk_class_number_to_stridx[32];
#endif  /* !DUK_SINGLE_FILE */

/*
 *  Prototypes
 */

/* alloc and init */
DUK_INTERNAL_DECL duk_hobject *duk_hobject_alloc_unchecked(duk_heap *heap, duk_uint_t hobject_flags);
DUK_INTERNAL_DECL duk_hobject *duk_hobject_alloc(duk_hthread *thr, duk_uint_t hobject_flags);
DUK_INTERNAL_DECL duk_harray *duk_harray_alloc(duk_hthread *thr, duk_uint_t hobject_flags);
DUK_INTERNAL_DECL duk_hcompfunc *duk_hcompfunc_alloc(duk_hthread *thr, duk_uint_t hobject_flags);
DUK_INTERNAL_DECL duk_hnatfunc *duk_hnatfunc_alloc(duk_hthread *thr, duk_uint_t hobject_flags);
DUK_INTERNAL_DECL duk_hboundfunc *duk_hboundfunc_alloc(duk_heap *heap, duk_uint_t hobject_flags);
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL_DECL duk_hbufobj *duk_hbufobj_alloc(duk_hthread *thr, duk_uint_t hobject_flags);
#endif
DUK_INTERNAL_DECL duk_hthread *duk_hthread_alloc_unchecked(duk_heap *heap, duk_uint_t hobject_flags);
DUK_INTERNAL_DECL duk_hthread *duk_hthread_alloc(duk_hthread *thr, duk_uint_t hobject_flags);
DUK_INTERNAL_DECL duk_hdecenv *duk_hdecenv_alloc(duk_hthread *thr, duk_uint_t hobject_flags);
DUK_INTERNAL_DECL duk_hobjenv *duk_hobjenv_alloc(duk_hthread *thr, duk_uint_t hobject_flags);
DUK_INTERNAL_DECL duk_hproxy *duk_hproxy_alloc(duk_hthread *thr, duk_uint_t hobject_flags);

/* resize */
DUK_INTERNAL_DECL void duk_hobject_realloc_props(duk_hthread *thr,
                                                 duk_hobject *obj,
                                                 duk_uint32_t new_e_size,
                                                 duk_uint32_t new_a_size,
                                                 duk_uint32_t new_h_size,
                                                 duk_bool_t abandon_array);
DUK_INTERNAL_DECL void duk_hobject_resize_entrypart(duk_hthread *thr,
                                                    duk_hobject *obj,
                                                    duk_uint32_t new_e_size);
#if 0  /*unused*/
DUK_INTERNAL_DECL void duk_hobject_resize_arraypart(duk_hthread *thr,
                                                    duk_hobject *obj,
                                                    duk_uint32_t new_a_size);
#endif

/* low-level property functions */
DUK_INTERNAL_DECL duk_bool_t duk_hobject_find_entry(duk_heap *heap, duk_hobject *obj, duk_hstring *key, duk_int_t *e_idx, duk_int_t *h_idx);
DUK_INTERNAL_DECL duk_tval *duk_hobject_find_entry_tval_ptr(duk_heap *heap, duk_hobject *obj, duk_hstring *key);
DUK_INTERNAL_DECL duk_tval *duk_hobject_find_entry_tval_ptr_stridx(duk_heap *heap, duk_hobject *obj, duk_small_uint_t stridx);
DUK_INTERNAL_DECL duk_tval *duk_hobject_find_entry_tval_ptr_and_attrs(duk_heap *heap, duk_hobject *obj, duk_hstring *key, duk_uint_t *out_attrs);
DUK_INTERNAL_DECL duk_tval *duk_hobject_find_array_entry_tval_ptr(duk_heap *heap, duk_hobject *obj, duk_uarridx_t i);
DUK_INTERNAL_DECL duk_bool_t duk_hobject_get_own_propdesc(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_propdesc *out_desc, duk_small_uint_t flags);

/* core property functions */
DUK_INTERNAL_DECL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key);
DUK_INTERNAL_DECL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, duk_tval *tv_val, duk_bool_t throw_flag);
DUK_INTERNAL_DECL duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, duk_bool_t throw_flag);
DUK_INTERNAL_DECL duk_bool_t duk_hobject_hasprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key);

/* internal property functions */
#define DUK_DELPROP_FLAG_THROW  (1U << 0)
#define DUK_DELPROP_FLAG_FORCE  (1U << 1)
DUK_INTERNAL_DECL duk_bool_t duk_hobject_delprop_raw(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t flags);
DUK_INTERNAL_DECL duk_bool_t duk_hobject_hasprop_raw(duk_hthread *thr, duk_hobject *obj, duk_hstring *key);
DUK_INTERNAL_DECL void duk_hobject_define_property_internal(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t flags);
DUK_INTERNAL_DECL void duk_hobject_define_property_internal_arridx(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t arr_idx, duk_small_uint_t flags);
DUK_INTERNAL_DECL duk_size_t duk_hobject_get_length(duk_hthread *thr, duk_hobject *obj);
#if defined(DUK_USE_HEAPPTR16)
DUK_INTERNAL_DECL duk_bool_t duk_hobject_has_finalizer_fast_raw(duk_heap *heap, duk_hobject *obj);
#else
DUK_INTERNAL_DECL duk_bool_t duk_hobject_has_finalizer_fast_raw(duk_hobject *obj);
#endif

/* helpers for defineProperty() and defineProperties() */
DUK_INTERNAL_DECL void duk_hobject_prepare_property_descriptor(duk_hthread *thr,
                                                               duk_idx_t idx_in,
                                                               duk_uint_t *out_defprop_flags,
                                                               duk_idx_t *out_idx_value,
                                                               duk_hobject **out_getter,
                                                               duk_hobject **out_setter);
DUK_INTERNAL_DECL duk_bool_t duk_hobject_define_property_helper(duk_hthread *thr,
                                                                duk_uint_t defprop_flags,
                                                                duk_hobject *obj,
                                                                duk_hstring *key,
                                                                duk_idx_t idx_value,
                                                                duk_hobject *get,
                                                                duk_hobject *set,
                                                                duk_bool_t throw_flag);

/* Object built-in methods */
DUK_INTERNAL_DECL void duk_hobject_object_get_own_property_descriptor(duk_hthread *thr, duk_idx_t obj_idx);
DUK_INTERNAL_DECL void duk_hobject_object_seal_freeze_helper(duk_hthread *thr, duk_hobject *obj, duk_bool_t is_freeze);
DUK_INTERNAL_DECL duk_bool_t duk_hobject_object_is_sealed_frozen_helper(duk_hthread *thr, duk_hobject *obj, duk_bool_t is_frozen);
DUK_INTERNAL_DECL duk_bool_t duk_hobject_object_ownprop_helper(duk_hthread *thr, duk_small_uint_t required_desc_flags);

/* internal properties */
DUK_INTERNAL_DECL duk_tval *duk_hobject_get_internal_value_tval_ptr(duk_heap *heap, duk_hobject *obj);
DUK_INTERNAL_DECL duk_hstring *duk_hobject_get_internal_value_string(duk_heap *heap, duk_hobject *obj);
DUK_INTERNAL_DECL duk_harray *duk_hobject_get_formals(duk_hthread *thr, duk_hobject *obj);
DUK_INTERNAL_DECL duk_hobject *duk_hobject_get_varmap(duk_hthread *thr, duk_hobject *obj);

/* hobject management functions */
DUK_INTERNAL_DECL void duk_hobject_compact_props(duk_hthread *thr, duk_hobject *obj);

/* ES2015 proxy */
#if defined(DUK_USE_ES6_PROXY)
DUK_INTERNAL_DECL duk_bool_t duk_hobject_proxy_check(duk_hobject *obj, duk_hobject **out_target, duk_hobject **out_handler);
DUK_INTERNAL_DECL duk_hobject *duk_hobject_resolve_proxy_target(duk_hobject *obj);
#endif

/* enumeration */
DUK_INTERNAL_DECL void duk_hobject_enumerator_create(duk_hthread *thr, duk_small_uint_t enum_flags);
DUK_INTERNAL_DECL duk_ret_t duk_hobject_get_enumerated_keys(duk_hthread *thr, duk_small_uint_t enum_flags);
DUK_INTERNAL_DECL duk_bool_t duk_hobject_enumerator_next(duk_hthread *thr, duk_bool_t get_value);

/* macros */
DUK_INTERNAL_DECL void duk_hobject_set_prototype_updref(duk_hthread *thr, duk_hobject *h, duk_hobject *p);

/* pc2line */
#if defined(DUK_USE_PC2LINE)
DUK_INTERNAL_DECL void duk_hobject_pc2line_pack(duk_hthread *thr, duk_compiler_instr *instrs, duk_uint_fast32_t length);
DUK_INTERNAL_DECL duk_uint_fast32_t duk_hobject_pc2line_query(duk_hthread *thr, duk_idx_t idx_func, duk_uint_fast32_t pc);
#endif

/* misc */
DUK_INTERNAL_DECL duk_bool_t duk_hobject_prototype_chain_contains(duk_hthread *thr, duk_hobject *h, duk_hobject *p, duk_bool_t ignore_loop);

#if !defined(DUK_USE_OBJECT_BUILTIN)
/* These declarations are needed when related built-in is disabled and
 * genbuiltins.py won't automatically emit the declerations.
 */
DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_to_string(duk_hthread *thr);
DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype(duk_hthread *thr);
#endif

#endif  /* DUK_HOBJECT_H_INCLUDED */
/* #include duk_hcompfunc.h */
//#line 1 "duk_hcompfunc.h"
/*
 *  Heap compiled function (ECMAScript function) representation.
 *
 *  There is a single data buffer containing the ECMAScript function's
 *  bytecode, constants, and inner functions.
 */

#if !defined(DUK_HCOMPFUNC_H_INCLUDED)
#define DUK_HCOMPFUNC_H_INCLUDED

/*
 *  Field accessor macros
 */

/* XXX: casts could be improved, especially for GET/SET DATA */

#if defined(DUK_USE_HEAPPTR16)
#define DUK_HCOMPFUNC_GET_DATA(heap,h) \
    ((duk_hbuffer_fixed *) (void *) DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (h)->data16))
#define DUK_HCOMPFUNC_SET_DATA(heap,h,v) do { \
        (h)->data16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (v)); \
    } while (0)
#define DUK_HCOMPFUNC_GET_FUNCS(heap,h)  \
    ((duk_hobject **) (void *) (DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (h)->funcs16)))
#define DUK_HCOMPFUNC_SET_FUNCS(heap,h,v)  do { \
        (h)->funcs16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (v)); \
    } while (0)
#define DUK_HCOMPFUNC_GET_BYTECODE(heap,h)  \
    ((duk_instr_t *) (void *) (DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (h)->bytecode16)))
#define DUK_HCOMPFUNC_SET_BYTECODE(heap,h,v)  do { \
        (h)->bytecode16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (v)); \
    } while (0)
#define DUK_HCOMPFUNC_GET_LEXENV(heap,h)  \
    ((duk_hobject *) (void *) (DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (h)->lex_env16)))
#define DUK_HCOMPFUNC_SET_LEXENV(heap,h,v)  do { \
        (h)->lex_env16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (v)); \
    } while (0)
#define DUK_HCOMPFUNC_GET_VARENV(heap,h)  \
    ((duk_hobject *) (void *) (DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (h)->var_env16)))
#define DUK_HCOMPFUNC_SET_VARENV(heap,h,v)  do { \
        (h)->var_env16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (v)); \
    } while (0)
#else
#define DUK_HCOMPFUNC_GET_DATA(heap,h)  ((duk_hbuffer_fixed *) (void *) (h)->data)
#define DUK_HCOMPFUNC_SET_DATA(heap,h,v) do { \
        (h)->data = (duk_hbuffer *) (v); \
    } while (0)
#define DUK_HCOMPFUNC_GET_FUNCS(heap,h)  ((h)->funcs)
#define DUK_HCOMPFUNC_SET_FUNCS(heap,h,v)  do { \
        (h)->funcs = (v); \
    } while (0)
#define DUK_HCOMPFUNC_GET_BYTECODE(heap,h)  ((h)->bytecode)
#define DUK_HCOMPFUNC_SET_BYTECODE(heap,h,v)  do { \
        (h)->bytecode = (v); \
    } while (0)
#define DUK_HCOMPFUNC_GET_LEXENV(heap,h)  ((h)->lex_env)
#define DUK_HCOMPFUNC_SET_LEXENV(heap,h,v)  do { \
        (h)->lex_env = (v); \
    } while (0)
#define DUK_HCOMPFUNC_GET_VARENV(heap,h)  ((h)->var_env)
#define DUK_HCOMPFUNC_SET_VARENV(heap,h,v)  do { \
        (h)->var_env = (v); \
    } while (0)
#endif

/*
 *  Accessor macros for function specific data areas
 */

/* Note: assumes 'data' is always a fixed buffer */
#define DUK_HCOMPFUNC_GET_BUFFER_BASE(heap,h)  \
    DUK_HBUFFER_FIXED_GET_DATA_PTR((heap), DUK_HCOMPFUNC_GET_DATA((heap), (h)))

#define DUK_HCOMPFUNC_GET_CONSTS_BASE(heap,h)  \
    ((duk_tval *) (void *) DUK_HCOMPFUNC_GET_BUFFER_BASE((heap), (h)))

#define DUK_HCOMPFUNC_GET_FUNCS_BASE(heap,h)  \
    DUK_HCOMPFUNC_GET_FUNCS((heap), (h))

#define DUK_HCOMPFUNC_GET_CODE_BASE(heap,h)  \
    DUK_HCOMPFUNC_GET_BYTECODE((heap), (h))

#define DUK_HCOMPFUNC_GET_CONSTS_END(heap,h)  \
    ((duk_tval *) (void *) DUK_HCOMPFUNC_GET_FUNCS((heap), (h)))

#define DUK_HCOMPFUNC_GET_FUNCS_END(heap,h)  \
    ((duk_hobject **) (void *) DUK_HCOMPFUNC_GET_BYTECODE((heap), (h)))

/* XXX: double evaluation of DUK_HCOMPFUNC_GET_DATA() */
#define DUK_HCOMPFUNC_GET_CODE_END(heap,h)  \
    ((duk_instr_t *) (void *) (DUK_HBUFFER_FIXED_GET_DATA_PTR((heap), DUK_HCOMPFUNC_GET_DATA((heap), (h))) + \
                    DUK_HBUFFER_GET_SIZE((duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA((heap), h))))

#define DUK_HCOMPFUNC_GET_CONSTS_SIZE(heap,h)  \
    ( \
     (duk_size_t) \
     ( \
       ((const duk_uint8_t *) DUK_HCOMPFUNC_GET_CONSTS_END((heap), (h))) - \
       ((const duk_uint8_t *) DUK_HCOMPFUNC_GET_CONSTS_BASE((heap), (h))) \
     ) \
    )

#define DUK_HCOMPFUNC_GET_FUNCS_SIZE(heap,h)  \
    ( \
     (duk_size_t) \
     ( \
       ((const duk_uint8_t *) DUK_HCOMPFUNC_GET_FUNCS_END((heap), (h))) - \
       ((const duk_uint8_t *) DUK_HCOMPFUNC_GET_FUNCS_BASE((heap), (h))) \
     ) \
    )

#define DUK_HCOMPFUNC_GET_CODE_SIZE(heap,h)  \
    ( \
     (duk_size_t) \
     ( \
       ((const duk_uint8_t *) DUK_HCOMPFUNC_GET_CODE_END((heap),(h))) - \
       ((const duk_uint8_t *) DUK_HCOMPFUNC_GET_CODE_BASE((heap),(h))) \
     ) \
    )

#define DUK_HCOMPFUNC_GET_CONSTS_COUNT(heap,h)  \
    ((duk_size_t) (DUK_HCOMPFUNC_GET_CONSTS_SIZE((heap), (h)) / sizeof(duk_tval)))

#define DUK_HCOMPFUNC_GET_FUNCS_COUNT(heap,h)  \
    ((duk_size_t) (DUK_HCOMPFUNC_GET_FUNCS_SIZE((heap), (h)) / sizeof(duk_hobject *)))

#define DUK_HCOMPFUNC_GET_CODE_COUNT(heap,h)  \
    ((duk_size_t) (DUK_HCOMPFUNC_GET_CODE_SIZE((heap), (h)) / sizeof(duk_instr_t)))

/*
 *  Validity assert
 */

#if defined(DUK_USE_ASSERTIONS)
DUK_INTERNAL_DECL void duk_hcompfunc_assert_valid(duk_hcompfunc *h);
#define DUK_HCOMPFUNC_ASSERT_VALID(h)  do { duk_hcompfunc_assert_valid((h)); } while (0)
#else
#define DUK_HCOMPFUNC_ASSERT_VALID(h)  do {} while (0)
#endif

/*
 *  Main struct
 */

struct duk_hcompfunc {
    /* shared object part */
    duk_hobject obj;

    /*
     *  Pointers to function data area for faster access.  Function
     *  data is a buffer shared between all closures of the same
     *  "template" function.  The data buffer is always fixed (non-
     *  dynamic, hence stable), with a layout as follows:
     *
     *    constants (duk_tval)
     *    inner functions (duk_hobject *)
     *    bytecode (duk_instr_t)
     *
     *  Note: bytecode end address can be computed from 'data' buffer
     *  size.  It is not strictly necessary functionally, assuming
     *  bytecode never jumps outside its allocated area.  However,
     *  it's a safety/robustness feature for avoiding the chance of
     *  executing random data as bytecode due to a compiler error.
     *
     *  Note: values in the data buffer must be incref'd (they will
     *  be decref'd on release) for every compiledfunction referring
     *  to the 'data' element.
     */

    /* Data area, fixed allocation, stable data ptrs. */
#if defined(DUK_USE_HEAPPTR16)
    duk_uint16_t data16;
#else
    duk_hbuffer *data;
#endif

    /* No need for constants pointer (= same as data).
     *
     * When using 16-bit packing alignment to 4 is nice.  'funcs' will be
     * 4-byte aligned because 'constants' are duk_tvals.  For now the
     * inner function pointers are not compressed, so that 'bytecode' will
     * also be 4-byte aligned.
     */
#if defined(DUK_USE_HEAPPTR16)
    duk_uint16_t funcs16;
    duk_uint16_t bytecode16;
#else
    duk_hobject **funcs;
    duk_instr_t *bytecode;
#endif

    /* Lexenv: lexical environment of closure, NULL for templates.
     * Varenv: variable environment of closure, NULL for templates.
     */
#if defined(DUK_USE_HEAPPTR16)
    duk_uint16_t lex_env16;
    duk_uint16_t var_env16;
#else
    duk_hobject *lex_env;
    duk_hobject *var_env;
#endif

    /*
     *  'nregs' registers are allocated on function entry, at most 'nargs'
     *  are initialized to arguments, and the rest to undefined.  Arguments
     *  above 'nregs' are not mapped to registers.  All registers in the
     *  active stack range must be initialized because they are GC reachable.
     *  'nargs' is needed so that if the function is given more than 'nargs'
     *  arguments, the additional arguments do not 'clobber' registers
     *  beyond 'nregs' which must be consistently initialized to undefined.
     *
     *  Usually there is no need to know which registers are mapped to
     *  local variables.  Registers may be allocated to variable in any
     *  way (even including gaps).  However, a register-variable mapping
     *  must be the same for the duration of the function execution and
     *  the register cannot be used for anything else.
     *
     *  When looking up variables by name, the '_Varmap' map is used.
     *  When an activation closes, registers mapped to arguments are
     *  copied into the environment record based on the same map.  The
     *  reverse map (from register to variable) is not currently needed
     *  at run time, except for debugging, so it is not maintained.
     */

    duk_uint16_t nregs;                /* regs to allocate */
    duk_uint16_t nargs;                /* number of arguments allocated to regs */

    /*
     *  Additional control information is placed into the object itself
     *  as internal properties to avoid unnecessary fields for the
     *  majority of functions.  The compiler tries to omit internal
     *  control fields when possible.
     *
     *  Function templates:
     *
     *    {
     *      name: "func",    // declaration, named function expressions
     *      fileName: <debug info for creating nice errors>
     *      _Varmap: { "arg1": 0, "arg2": 1, "varname": 2 },
     *      _Formals: [ "arg1", "arg2" ],
     *      _Source: "function func(arg1, arg2) { ... }",
     *      _Pc2line: <debug info for pc-to-line mapping>,
     *    }
     *
     *  Function instances:
     *
     *    {
     *      length: 2,
     *      prototype: { constructor: <func> },
     *      caller: <thrower>,
     *      arguments: <thrower>,
     *      name: "func",    // declaration, named function expressions
     *      fileName: <debug info for creating nice errors>
     *      _Varmap: { "arg1": 0, "arg2": 1, "varname": 2 },
     *      _Formals: [ "arg1", "arg2" ],
     *      _Source: "function func(arg1, arg2) { ... }",
     *      _Pc2line: <debug info for pc-to-line mapping>,
     *    }
     *
     *  More detailed description of these properties can be found
     *  in the documentation.
     */

#if defined(DUK_USE_DEBUGGER_SUPPORT)
    /* Line number range for function.  Needed during debugging to
     * determine active breakpoints.
     */
    duk_uint32_t start_line;
    duk_uint32_t end_line;
#endif
};

#endif  /* DUK_HCOMPFUNC_H_INCLUDED */
/* #include duk_hnatfunc.h */
//#line 1 "duk_hnatfunc.h"
/*
 *  Heap native function representation.
 */

#if !defined(DUK_HNATFUNC_H_INCLUDED)
#define DUK_HNATFUNC_H_INCLUDED

#if defined(DUK_USE_ASSERTIONS)
DUK_INTERNAL_DECL void duk_hnatfunc_assert_valid(duk_hnatfunc *h);
#define DUK_HNATFUNC_ASSERT_VALID(h)  do { duk_hnatfunc_assert_valid((h)); } while (0)
#else
#define DUK_HNATFUNC_ASSERT_VALID(h)  do {} while (0)
#endif

#define DUK_HNATFUNC_NARGS_VARARGS  ((duk_int16_t) -1)
#define DUK_HNATFUNC_NARGS_MAX      ((duk_int16_t) 0x7fff)

struct duk_hnatfunc {
    /* shared object part */
    duk_hobject obj;

    duk_c_function func;
    duk_int16_t nargs;
    duk_int16_t magic;

    /* The 'magic' field allows an opaque 16-bit field to be accessed by the
     * Duktape/C function.  This allows, for instance, the same native function
     * to be used for a set of very similar functions, with the 'magic' field
     * providing the necessary non-argument flags / values to guide the behavior
     * of the native function.  The value is signed on purpose: it is easier to
     * convert a signed value to unsigned (simply AND with 0xffff) than vice
     * versa.
     *
     * Note: cannot place nargs/magic into the heaphdr flags, because
     * duk_hobject takes almost all flags already.
     */
};

#endif  /* DUK_HNATFUNC_H_INCLUDED */
/* #include duk_hboundfunc.h */
//#line 1 "duk_hboundfunc.h"
/*
 *  Bound function representation.
 */

#if !defined(DUK_HBOUNDFUNC_H_INCLUDED)
#define DUK_HBOUNDFUNC_H_INCLUDED

/* Artificial limit for args length.  Ensures arithmetic won't overflow
 * 32 bits when combining bound functions.
 */
#define DUK_HBOUNDFUNC_MAX_ARGS 0x20000000UL

#if defined(DUK_USE_ASSERTIONS)
DUK_INTERNAL_DECL void duk_hboundfunc_assert_valid(duk_hboundfunc *h);
#define DUK_HBOUNDFUNC_ASSERT_VALID(h)  do { duk_hboundfunc_assert_valid((h)); } while (0)
#else
#define DUK_HBOUNDFUNC_ASSERT_VALID(h)  do {} while (0)
#endif

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

    /* Final target function, stored as duk_tval so that lightfunc can be
     * represented too.
     */
    duk_tval target;

    /* This binding. */
    duk_tval this_binding;

    /* Arguments to prepend. */
    duk_tval *args;  /* Separate allocation. */
    duk_idx_t nargs;
};

#endif  /* DUK_HBOUNDFUNC_H_INCLUDED */
/* #include duk_hbufobj.h */
//#line 1 "duk_hbufobj.h"
/*
 *  Heap Buffer object representation.  Used for all Buffer variants.
 */

#if !defined(DUK_HBUFOBJ_H_INCLUDED)
#define DUK_HBUFOBJ_H_INCLUDED

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)

/* All element accessors are host endian now (driven by TypedArray spec). */
#define DUK_HBUFOBJ_ELEM_UINT8           0
#define DUK_HBUFOBJ_ELEM_UINT8CLAMPED    1
#define DUK_HBUFOBJ_ELEM_INT8            2
#define DUK_HBUFOBJ_ELEM_UINT16          3
#define DUK_HBUFOBJ_ELEM_INT16           4
#define DUK_HBUFOBJ_ELEM_UINT32          5
#define DUK_HBUFOBJ_ELEM_INT32           6
#define DUK_HBUFOBJ_ELEM_FLOAT32         7
#define DUK_HBUFOBJ_ELEM_FLOAT64         8
#define DUK_HBUFOBJ_ELEM_MAX             8

#if defined(DUK_USE_ASSERTIONS)
DUK_INTERNAL_DECL void duk_hbufobj_assert_valid(duk_hbufobj *h);
#define DUK_HBUFOBJ_ASSERT_VALID(h)  do { duk_hbufobj_assert_valid((h)); } while (0)
#else
#define DUK_HBUFOBJ_ASSERT_VALID(h)  do {} while (0)
#endif

/* Get the current data pointer (caller must ensure buf != NULL) as a
 * duk_uint8_t ptr.  Note that the result may be NULL if the underlying
 * buffer has zero size and is not a fixed buffer.
 */
#define DUK_HBUFOBJ_GET_SLICE_BASE(heap,h) \
    (DUK_ASSERT_EXPR((h) != NULL), DUK_ASSERT_EXPR((h)->buf != NULL), \
    (((duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR((heap), (h)->buf)) + (h)->offset))

/* True if slice is full, i.e. offset is zero and length covers the entire
 * 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);

#else  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/* nothing */

#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
 */

#define DUK_ACT_FLAG_STRICT             (1U << 0)  /* function executes in strict mode */
#define DUK_ACT_FLAG_TAILCALLED         (1U << 1)  /* activation has tail called one or more times */
#define DUK_ACT_FLAG_CONSTRUCT          (1U << 2)  /* function executes as a constructor (called via "new") */
#define DUK_ACT_FLAG_PREVENT_YIELD      (1U << 3)  /* activation prevents yield (native call or "new") */
#define DUK_ACT_FLAG_DIRECT_EVAL        (1U << 4)  /* activation is a direct eval call */
#define DUK_ACT_FLAG_CONSTRUCT_PROXY    (1U << 5)  /* activation is for Proxy 'construct' call, special return value handling */
#define DUK_ACT_FLAG_BREAKPOINT_ACTIVE  (1U << 6)  /* activation has active breakpoint(s) */

#define DUK_ACT_GET_FUNC(act)           ((act)->func)

/*
 *  Flags for __FILE__ / __LINE__ registered into tracedata
 */

#define DUK_TB_FLAG_NOBLAME_FILELINE    (1U << 0)  /* don't report __FILE__ / __LINE__ as fileName/lineNumber */

/*
 *  Catcher defines
 */

/* XXX: remove catcher type entirely */

/* flags field: LLLLLLFT, L = label (24 bits), F = flags (4 bits), T = type (4 bits) */
#define DUK_CAT_TYPE_MASK            0x0000000fUL
#define DUK_CAT_TYPE_BITS            4
#define DUK_CAT_LABEL_MASK           0xffffff00UL
#define DUK_CAT_LABEL_BITS           24
#define DUK_CAT_LABEL_SHIFT          8

#define DUK_CAT_FLAG_CATCH_ENABLED          (1U << 4)   /* catch part will catch */
#define DUK_CAT_FLAG_FINALLY_ENABLED        (1U << 5)   /* finally part will catch */
#define DUK_CAT_FLAG_CATCH_BINDING_ENABLED  (1U << 6)   /* request to create catch binding */
#define DUK_CAT_FLAG_LEXENV_ACTIVE          (1U << 7)   /* catch or with binding is currently active */

#define DUK_CAT_TYPE_UNKNOWN         0
#define DUK_CAT_TYPE_TCF             1
#define DUK_CAT_TYPE_LABEL           2

#define DUK_CAT_GET_TYPE(c)          ((c)->flags & DUK_CAT_TYPE_MASK)
#define DUK_CAT_GET_LABEL(c)         (((c)->flags & DUK_CAT_LABEL_MASK) >> DUK_CAT_LABEL_SHIFT)

#define DUK_CAT_HAS_CATCH_ENABLED(c)           ((c)->flags & DUK_CAT_FLAG_CATCH_ENABLED)
#define DUK_CAT_HAS_FINALLY_ENABLED(c)         ((c)->flags & DUK_CAT_FLAG_FINALLY_ENABLED)
#define DUK_CAT_HAS_CATCH_BINDING_ENABLED(c)   ((c)->flags & DUK_CAT_FLAG_CATCH_BINDING_ENABLED)
#define DUK_CAT_HAS_LEXENV_ACTIVE(c)           ((c)->flags & DUK_CAT_FLAG_LEXENV_ACTIVE)

#define DUK_CAT_SET_CATCH_ENABLED(c)    do { \
        (c)->flags |= DUK_CAT_FLAG_CATCH_ENABLED; \
    } while (0)
#define DUK_CAT_SET_FINALLY_ENABLED(c)  do { \
        (c)->flags |= DUK_CAT_FLAG_FINALLY_ENABLED; \
    } while (0)
#define DUK_CAT_SET_CATCH_BINDING_ENABLED(c)    do { \
        (c)->flags |= DUK_CAT_FLAG_CATCH_BINDING_ENABLED; \
    } while (0)
#define DUK_CAT_SET_LEXENV_ACTIVE(c)    do { \
        (c)->flags |= DUK_CAT_FLAG_LEXENV_ACTIVE; \
    } while (0)

#define DUK_CAT_CLEAR_CATCH_ENABLED(c)    do { \
        (c)->flags &= ~DUK_CAT_FLAG_CATCH_ENABLED; \
    } while (0)
#define DUK_CAT_CLEAR_FINALLY_ENABLED(c)  do { \
        (c)->flags &= ~DUK_CAT_FLAG_FINALLY_ENABLED; \
    } while (0)
#define DUK_CAT_CLEAR_CATCH_BINDING_ENABLED(c)    do { \
        (c)->flags &= ~DUK_CAT_FLAG_CATCH_BINDING_ENABLED; \
    } while (0)
#define DUK_CAT_CLEAR_LEXENV_ACTIVE(c)    do { \
        (c)->flags &= ~DUK_CAT_FLAG_LEXENV_ACTIVE; \
    } while (0)

/*
 *  Thread defines
 */

#if defined(DUK_USE_ROM_STRINGS)
#define DUK_HTHREAD_GET_STRING(thr,idx) \
    ((duk_hstring *) DUK_LOSE_CONST(duk_rom_strings_stridx[(idx)]))
#else  /* DUK_USE_ROM_STRINGS */
#if defined(DUK_USE_HEAPPTR16)
#define DUK_HTHREAD_GET_STRING(thr,idx) \
    ((duk_hstring *) DUK_USE_HEAPPTR_DEC16((thr)->heap->heap_udata, (thr)->strs16[(idx)]))
#else
#define DUK_HTHREAD_GET_STRING(thr,idx) \
    ((thr)->strs[(idx)])
#endif
#endif  /* DUK_USE_ROM_STRINGS */

/* values for the state field */
#define DUK_HTHREAD_STATE_INACTIVE     1   /* thread not currently running */
#define DUK_HTHREAD_STATE_RUNNING      2   /* thread currently running (only one at a time) */
#define DUK_HTHREAD_STATE_RESUMED      3   /* thread resumed another thread (active but not running) */
#define DUK_HTHREAD_STATE_YIELDED      4   /* thread has yielded */
#define DUK_HTHREAD_STATE_TERMINATED   5   /* thread has terminated */

/* Executor interrupt default interval when nothing else requires a
 * smaller value.  The default interval must be small enough to allow
 * for reasonable execution timeout checking but large enough to keep
 * impact on execution performance low.
 */
#if defined(DUK_USE_INTERRUPT_COUNTER)
#define DUK_HTHREAD_INTCTR_DEFAULT     (256L * 1024L)
#endif

/*
 *  Assert context is valid: non-NULL pointer, fields look sane.
 *
 *  This is used by public API call entrypoints to catch invalid 'ctx' pointers
 *  as early as possible; invalid 'ctx' pointers cause very odd and difficult to
 *  diagnose behavior so it's worth checking even when the check is not 100%.
 */

#if defined(DUK_USE_ASSERTIONS)
/* Assertions for internals. */
DUK_INTERNAL_DECL void duk_hthread_assert_valid(duk_hthread *thr);
#define DUK_HTHREAD_ASSERT_VALID(thr)  do { duk_hthread_assert_valid((thr)); } while (0)

/* Assertions for public API calls; a bit stronger. */
DUK_INTERNAL_DECL void duk_ctx_assert_valid(duk_hthread *thr);
#define DUK_CTX_ASSERT_VALID(thr)  do { duk_ctx_assert_valid((thr)); } while (0)
#else
#define DUK_HTHREAD_ASSERT_VALID(thr)  do {} while (0)
#define DUK_CTX_ASSERT_VALID(thr)  do {} while (0)
#endif

/* Assertions for API call entry specifically.  Checks 'ctx' but also may
 * check internal state (e.g. not in a debugger transport callback).
 */
#define DUK_ASSERT_API_ENTRY(thr) do { \
        DUK_CTX_ASSERT_VALID((thr)); \
        DUK_ASSERT((thr)->heap != NULL); \
        DUK_ASSERT((thr)->heap->dbg_calling_transport == 0); \
    } while (0)

/*
 *  Assertion helpers.
 */

#define DUK_ASSERT_STRIDX_VALID(val) \
    DUK_ASSERT((duk_uint_t) (val) < DUK_HEAP_NUM_STRINGS)

#define DUK_ASSERT_BIDX_VALID(val) \
    DUK_ASSERT((duk_uint_t) (val) < DUK_NUM_BUILTINS)

/*
 *  Misc
 */

/* Fast access to 'this' binding.  Assumes there's a call in progress. */
#define DUK_HTHREAD_THIS_PTR(thr) \
    (DUK_ASSERT_EXPR((thr) != NULL), \
     DUK_ASSERT_EXPR((thr)->valstack_bottom > (thr)->valstack), \
     (thr)->valstack_bottom - 1)

/*
 *  Struct defines
 */

/* Fields are ordered for alignment/packing. */
struct duk_activation {
    duk_tval tv_func;       /* borrowed: full duk_tval for function being executed; for lightfuncs */
    duk_hobject *func;      /* borrowed: function being executed; for bound function calls, this is the final, real function, NULL for lightfuncs */
    duk_activation *parent; /* previous (parent) activation (or NULL if none) */
    duk_hobject *var_env;   /* current variable environment (may be NULL if delayed) */
    duk_hobject *lex_env;   /* current lexical environment (may be NULL if delayed) */
    duk_catcher *cat;       /* current catcher (or NULL) */

#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
    /* Previous value of 'func' caller, restored when unwound.  Only in use
     * when 'func' is non-strict.
     */
    duk_hobject *prev_caller;
#endif

    duk_instr_t *curr_pc;   /* next instruction to execute (points to 'func' bytecode, stable pointer), NULL for native calls */

    /* bottom_byteoff and retval_byteoff are only used for book-keeping
     * of ECMAScript-initiated calls, to allow returning to an ECMAScript
     * function properly.
     */

    /* Bottom of valstack for this activation, used to reset
     * valstack_bottom on return; offset is absolute.  There's
     * no need to track 'top' because native call handling deals
     * with that using locals, and for ECMAScript returns 'nregs'
     * indicates the necessary top.
     */
    duk_size_t bottom_byteoff;

    /* Return value when returning to this activation (points to caller
     * reg, not callee reg); offset is absolute (only set if activation is
     * not topmost).
     *
     * Note: bottom_byteoff is always set, while retval_byteoff is only
     * applicable for activations below the topmost one.  Currently
     * retval_byteoff for the topmost activation is considered garbage
     * (and it not initialized on entry or cleared on return; may contain
     * previous or garbage values).
     */
    duk_size_t retval_byteoff;

    /* Current 'this' binding is the value just below bottom.
     * Previously, 'this' binding was handled with an index to the
     * (calling) valstack.  This works for everything except tail
     * calls, which must not "accumulate" valstack temps.
     */

    /* Value stack reserve (valstack_end) byte offset to be restored
     * when returning to this activation.  Only used by the bytecode
     * executor.
     */
    duk_size_t reserve_byteoff;

#if defined(DUK_USE_DEBUGGER_SUPPORT)
    duk_uint32_t prev_line; /* needed for stepping */
#endif

    duk_small_uint_t flags;
};

struct duk_catcher {
    duk_catcher *parent;            /* previous (parent) catcher (or NULL if none) */
    duk_hstring *h_varname;         /* borrowed reference to catch variable name (or NULL if none) */
                                    /* (reference is valid as long activation exists) */
    duk_instr_t *pc_base;           /* resume execution from pc_base or pc_base+1 (points to 'func' bytecode, stable pointer) */
    duk_size_t idx_base;            /* idx_base and idx_base+1 get completion value and type */
    duk_uint32_t flags;             /* type and control flags, label number */
    /* XXX: could pack 'flags' and 'idx_base' to same value in practice,
     * on 32-bit targets this would make duk_catcher 16 bytes.
     */
};

struct duk_hthread {
    /* Shared object part */
    duk_hobject obj;

    /* Pointer to bytecode executor's 'curr_pc' variable.  Used to copy
     * the current PC back into the topmost activation when activation
     * state is about to change (or "syncing" is otherwise needed).  This
     * is rather awkward but important for performance, see execution.rst.
     */
    duk_instr_t **ptr_curr_pc;

    /* Backpointers. */
    duk_heap *heap;

    /* Current strictness flag: affects API calls. */
    duk_uint8_t strict;

    /* Thread state. */
    duk_uint8_t state;
    duk_uint8_t unused1;
    duk_uint8_t unused2;

    /* XXX: Valstack and callstack are currently assumed to have non-NULL
     * pointers.  Relaxing this would not lead to big benefits (except
     * perhaps for terminated threads).
     */

    /* Value stack: these are expressed as pointers for faster stack
     * manipulation.  [valstack,valstack_top[ is GC-reachable,
     * [valstack_top,valstack_alloc_end[ is not GC-reachable but kept
     * initialized as 'undefined'.  [valstack,valstack_end[ is the
     * guaranteed/reserved space and the valstack cannot be resized to
     * a smaller size.  [valstack_end,valstack_alloc_end[ is currently
     * allocated slack that can be used to grow the current guaranteed
     * space but may be shrunk away without notice.
     *
     *
     * <----------------------- guaranteed --->
     *                                        <---- slack --->
     *               <--- frame --->
     * .-------------+=============+----------+--------------.
     * |xxxxxxxxxxxxx|yyyyyyyyyyyyy|uuuuuuuuuu|uuuuuuuuuuuuuu|
     * `-------------+=============+----------+--------------'
     *
     * ^             ^             ^          ^              ^
     * |             |             |          |              |
     * valstack      bottom        top        end            alloc_end
     *
     *     xxx = arbitrary values, below current frame
     *     yyy = arbitrary values, inside current frame
     *     uuu = outside active value stack, initialized to 'undefined'
     */
    duk_tval *valstack;                     /* start of valstack allocation */
    duk_tval *valstack_end;                 /* end of valstack reservation/guarantee (exclusive) */
    duk_tval *valstack_alloc_end;           /* end of valstack allocation */
    duk_tval *valstack_bottom;              /* bottom of current frame */
    duk_tval *valstack_top;                 /* top of current frame (exclusive) */

    /* Call stack, represented as a linked list starting from the current
     * activation (or NULL if nothing is active).
     */
    duk_activation *callstack_curr;         /* current activation (or NULL if none) */
    duk_size_t callstack_top;               /* number of activation records in callstack (0 if none) */
    duk_size_t callstack_preventcount;      /* number of activation records in callstack preventing a yield */

    /* Yield/resume book-keeping. */
    duk_hthread *resumer;                   /* who resumed us (if any) */

    /* Current compiler state (if any), used for augmenting SyntaxErrors. */
    duk_compiler_ctx *compile_ctx;

#if defined(DUK_USE_INTERRUPT_COUNTER)
    /* Interrupt counter for triggering a slow path check for execution
     * timeout, debugger interaction such as breakpoints, etc.  The value
     * is valid for the current running thread, and both the init and
     * counter values are copied whenever a thread switch occurs.  It's
     * important for the counter to be conveniently accessible for the
     * bytecode executor inner loop for performance reasons.
     */
    duk_int_t interrupt_counter;    /* countdown state */
    duk_int_t interrupt_init;       /* start value for current countdown */
#endif

    /* Builtin-objects; may or may not be shared with other threads,
     * threads existing in different "compartments" will have different
     * built-ins.  Must be stored on a per-thread basis because there
     * is no intermediate structure for a thread group / compartment.
     * This takes quite a lot of space, currently 43x4 = 172 bytes on
     * 32-bit platforms.
     *
     * In some cases the builtins array could be ROM based, but it's
     * sometimes edited (e.g. for sandboxing) so it's better to keep
     * this array in RAM.
     */
    duk_hobject *builtins[DUK_NUM_BUILTINS];

    /* Convenience copies from heap/vm for faster access. */
#if defined(DUK_USE_ROM_STRINGS)
    /* No field needed when strings are in ROM. */
#else
#if defined(DUK_USE_HEAPPTR16)
    duk_uint16_t *strs16;
#else
    duk_hstring **strs;
#endif
#endif
};

/*
 *  Prototypes
 */

DUK_INTERNAL_DECL void duk_hthread_copy_builtin_objects(duk_hthread *thr_from, duk_hthread *thr_to);
DUK_INTERNAL_DECL void duk_hthread_create_builtin_objects(duk_hthread *thr);
DUK_INTERNAL_DECL duk_bool_t duk_hthread_init_stacks(duk_heap *heap, duk_hthread *thr);
DUK_INTERNAL_DECL void duk_hthread_terminate(duk_hthread *thr);

DUK_INTERNAL_DECL duk_activation *duk_hthread_activation_alloc(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_hthread_activation_free(duk_hthread *thr, duk_activation *act);
DUK_INTERNAL_DECL void duk_hthread_activation_unwind_norz(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_hthread_activation_unwind_reuse_norz(duk_hthread *thr);
DUK_INTERNAL_DECL duk_activation *duk_hthread_get_activation_for_level(duk_hthread *thr, duk_int_t level);

DUK_INTERNAL_DECL duk_catcher *duk_hthread_catcher_alloc(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_hthread_catcher_free(duk_hthread *thr, duk_catcher *cat);
DUK_INTERNAL_DECL void duk_hthread_catcher_unwind_norz(duk_hthread *thr, duk_activation *act);
DUK_INTERNAL_DECL void duk_hthread_catcher_unwind_nolexenv_norz(duk_hthread *thr, duk_activation *act);

#if defined(DUK_USE_FINALIZER_TORTURE)
DUK_INTERNAL_DECL void duk_hthread_valstack_torture_realloc(duk_hthread *thr);
#endif

DUK_INTERNAL_DECL void *duk_hthread_get_valstack_ptr(duk_heap *heap, void *ud);  /* indirect allocs */

#if defined(DUK_USE_DEBUGGER_SUPPORT)
DUK_INTERNAL_DECL duk_uint_fast32_t duk_hthread_get_act_curr_pc(duk_hthread *thr, duk_activation *act);
#endif
DUK_INTERNAL_DECL duk_uint_fast32_t duk_hthread_get_act_prev_pc(duk_hthread *thr, duk_activation *act);
DUK_INTERNAL_DECL void duk_hthread_sync_currpc(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_hthread_sync_and_null_currpc(duk_hthread *thr);

#endif  /* DUK_HTHREAD_H_INCLUDED */
/* #include duk_harray.h */
//#line 1 "duk_harray.h"
/*
 *  Array object representation, used for actual Array instances.
 *
 *  All objects with the exotic array behavior (which must coincide with having
 *  internal class array) MUST be duk_harrays.  No other object can be a
 *  duk_harray.  However, duk_harrays may not always have an array part.
 */

#if !defined(DUK_HARRAY_H_INCLUDED)
#define DUK_HARRAY_H_INCLUDED

#if defined(DUK_USE_ASSERTIONS)
DUK_INTERNAL_DECL void duk_harray_assert_valid(duk_harray *h);
#define DUK_HARRAY_ASSERT_VALID(h)  do { duk_harray_assert_valid((h)); } while (0)
#else
#define DUK_HARRAY_ASSERT_VALID(h)  do {} while (0)
#endif

#define DUK_HARRAY_LENGTH_WRITABLE(h)         (!(h)->length_nonwritable)
#define DUK_HARRAY_LENGTH_NONWRITABLE(h)      ((h)->length_nonwritable)
#define DUK_HARRAY_SET_LENGTH_WRITABLE(h)     do { (h)->length_nonwritable = 0; } while (0)
#define DUK_HARRAY_SET_LENGTH_NONWRITABLE(h)  do { (h)->length_nonwritable = 1; } while (0)

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

    /* Array .length.
     *
     * At present Array .length may be smaller, equal, or even larger
     * than the allocated underlying array part.  Fast path code must
     * always take this into account carefully.
     */
    duk_uint32_t length;

    /* Array .length property attributes.  The property is always
     * non-enumerable and non-configurable.  It's initially writable
     * but per Object.defineProperty() rules it can be made non-writable
     * even if it is non-configurable.  Thus we need to track the
     * writability explicitly.
     *
     * XXX: this field to be eliminated and moved into duk_hobject
     * flags field to save space.
     */
    duk_bool_t length_nonwritable;
};

#endif  /* DUK_HARRAY_H_INCLUDED */
/* #include duk_henv.h */
//#line 1 "duk_henv.h"
/*
 *  Environment object representation.
 */

#if !defined(DUK_HENV_H_INCLUDED)
#define DUK_HENV_H_INCLUDED

#if defined(DUK_USE_ASSERTIONS)
DUK_INTERNAL_DECL void duk_hdecenv_assert_valid(duk_hdecenv *h);
DUK_INTERNAL_DECL void duk_hobjenv_assert_valid(duk_hobjenv *h);
#define DUK_HDECENV_ASSERT_VALID(h)  do { duk_hdecenv_assert_valid((h)); } while (0)
#define DUK_HOBJENV_ASSERT_VALID(h)  do { duk_hobjenv_assert_valid((h)); } while (0)
#else
#define DUK_HDECENV_ASSERT_VALID(h)  do {} while (0)
#define DUK_HOBJENV_ASSERT_VALID(h)  do {} while (0)
#endif

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

    /* These control variables provide enough information to access live
     * variables for a closure that is still open.  If thread == NULL,
     * the record is closed and the identifiers are in the property table.
     */
    duk_hthread *thread;
    duk_hobject *varmap;
    duk_size_t regbase_byteoff;
};

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

    /* Target object and 'this' binding for object binding. */
    duk_hobject *target;

    /* The 'target' object is used as a this binding in only some object
     * environments.  For example, the global environment does not provide
     * a this binding, but a with statement does.
     */
    duk_bool_t has_this;
};

#endif  /* DUK_HENV_H_INCLUDED */
/* #include duk_hbuffer.h */
//#line 1 "duk_hbuffer.h"
/*
 *  Heap buffer representation.
 *
 *  Heap allocated user data buffer which is either:
 *
 *    1. A fixed size buffer (data follows header statically)
 *    2. A dynamic size buffer (data pointer follows header)
 *
 *  The data pointer for a variable size buffer of zero size may be NULL.
 */

#if !defined(DUK_HBUFFER_H_INCLUDED)
#define DUK_HBUFFER_H_INCLUDED

/*
 *  Flags
 *
 *  Fixed buffer:     0
 *  Dynamic buffer:   DUK_HBUFFER_FLAG_DYNAMIC
 *  External buffer:  DUK_HBUFFER_FLAG_DYNAMIC | DUK_HBUFFER_FLAG_EXTERNAL
 */

#define DUK_HBUFFER_FLAG_DYNAMIC                  DUK_HEAPHDR_USER_FLAG(0)    /* buffer is behind a pointer, dynamic or external */
#define DUK_HBUFFER_FLAG_EXTERNAL                 DUK_HEAPHDR_USER_FLAG(1)    /* buffer pointer is to an externally allocated buffer */

#define DUK_HBUFFER_HAS_DYNAMIC(x)                DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HBUFFER_FLAG_DYNAMIC)
#define DUK_HBUFFER_HAS_EXTERNAL(x)               DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HBUFFER_FLAG_EXTERNAL)

#define DUK_HBUFFER_SET_DYNAMIC(x)                DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HBUFFER_FLAG_DYNAMIC)
#define DUK_HBUFFER_SET_EXTERNAL(x)               DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HBUFFER_FLAG_EXTERNAL)

#define DUK_HBUFFER_CLEAR_DYNAMIC(x)              DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HBUFFER_FLAG_DYNAMIC)
#define DUK_HBUFFER_CLEAR_EXTERNAL(x)             DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HBUFFER_FLAG_EXTERNAL)

/*
 *  Misc defines
 */

/* Impose a maximum buffer length for now.  Restricted artificially to
 * ensure resize computations or adding a heap header length won't
 * overflow size_t and that a signed duk_int_t can hold a buffer
 * length.  The limit should be synchronized with DUK_HSTRING_MAX_BYTELEN.
 */

#if defined(DUK_USE_BUFLEN16)
#define DUK_HBUFFER_MAX_BYTELEN                   (0x0000ffffUL)
#else
/* Intentionally not 0x7fffffffUL; at least JSON code expects that
 * 2*len + 2 fits in 32 bits.
 */
#define DUK_HBUFFER_MAX_BYTELEN                   (0x7ffffffeUL)
#endif

/*
 *  Field access
 */

#if defined(DUK_USE_BUFLEN16)
/* size stored in duk_heaphdr unused flag bits */
#define DUK_HBUFFER_GET_SIZE(x)     ((x)->hdr.h_flags >> 16)
#define DUK_HBUFFER_SET_SIZE(x,v)   do { \
        duk_size_t duk__v; \
        duk__v = (v); \
        DUK_ASSERT(duk__v <= 0xffffUL); \
        (x)->hdr.h_flags = ((x)->hdr.h_flags & 0x0000ffffUL) | (((duk_uint32_t) duk__v) << 16); \
    } while (0)
#define DUK_HBUFFER_ADD_SIZE(x,dv)  do { \
        (x)->hdr.h_flags += ((dv) << 16); \
    } while (0)
#define DUK_HBUFFER_SUB_SIZE(x,dv)  do { \
        (x)->hdr.h_flags -= ((dv) << 16); \
    } while (0)
#else
#define DUK_HBUFFER_GET_SIZE(x)     (((duk_hbuffer *) (x))->size)
#define DUK_HBUFFER_SET_SIZE(x,v)   do { \
        ((duk_hbuffer *) (x))->size = (v); \
    } while (0)
#define DUK_HBUFFER_ADD_SIZE(x,dv)  do { \
        (x)->size += (dv); \
    } while (0)
#define DUK_HBUFFER_SUB_SIZE(x,dv)  do { \
        (x)->size -= (dv); \
    } while (0)
#endif

#define DUK_HBUFFER_FIXED_GET_SIZE(x)       DUK_HBUFFER_GET_SIZE((duk_hbuffer *) (x))
#define DUK_HBUFFER_FIXED_SET_SIZE(x,v)     DUK_HBUFFER_SET_SIZE((duk_hbuffer *) (x))

#define DUK_HBUFFER_DYNAMIC_GET_SIZE(x)     DUK_HBUFFER_GET_SIZE((duk_hbuffer *) (x))
#define DUK_HBUFFER_DYNAMIC_SET_SIZE(x,v)   DUK_HBUFFER_SET_SIZE((duk_hbuffer *) (x), (v))
#define DUK_HBUFFER_DYNAMIC_ADD_SIZE(x,dv)  DUK_HBUFFER_ADD_SIZE((duk_hbuffer *) (x), (dv))
#define DUK_HBUFFER_DYNAMIC_SUB_SIZE(x,dv)  DUK_HBUFFER_SUB_SIZE((duk_hbuffer *) (x), (dv))

#define DUK_HBUFFER_EXTERNAL_GET_SIZE(x)    DUK_HBUFFER_GET_SIZE((duk_hbuffer *) (x))
#define DUK_HBUFFER_EXTERNAL_SET_SIZE(x,v)  DUK_HBUFFER_SET_SIZE((duk_hbuffer *) (x), (v))

#define DUK_HBUFFER_FIXED_GET_DATA_PTR(heap,x)    ((duk_uint8_t *) (((duk_hbuffer_fixed *) (void *) (x)) + 1))

#if defined(DUK_USE_HEAPPTR16)
#define DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap,x) \
    ((void *) DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, ((duk_heaphdr *) (x))->h_extra16))
#define DUK_HBUFFER_DYNAMIC_SET_DATA_PTR(heap,x,v)     do { \
        ((duk_heaphdr *) (x))->h_extra16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (v)); \
    } while (0)
#define DUK_HBUFFER_DYNAMIC_SET_DATA_PTR_NULL(heap,x)  do { \
        ((duk_heaphdr *) (x))->h_extra16 = 0;  /* assume 0 <=> NULL */ \
    } while (0)
#else
#define DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap,x)       ((x)->curr_alloc)
#define DUK_HBUFFER_DYNAMIC_SET_DATA_PTR(heap,x,v)     do { \
        (x)->curr_alloc = (void *) (v); \
    } while (0)
#define DUK_HBUFFER_DYNAMIC_SET_DATA_PTR_NULL(heap,x)  do { \
        (x)->curr_alloc = (void *) NULL; \
    } while (0)
#endif

/* No pointer compression because pointer is potentially outside of
 * Duktape heap.
 */
#if defined(DUK_USE_HEAPPTR16)
#define DUK_HBUFFER_EXTERNAL_GET_DATA_PTR(heap,x) \
    ((void *) (x)->curr_alloc)
#define DUK_HBUFFER_EXTERNAL_SET_DATA_PTR(heap,x,v)     do { \
        (x)->curr_alloc = (void *) (v); \
    } while (0)
#define DUK_HBUFFER_EXTERNAL_SET_DATA_PTR_NULL(heap,x)  do { \
        (x)->curr_alloc = (void *) NULL; \
    } while (0)
#else
#define DUK_HBUFFER_EXTERNAL_GET_DATA_PTR(heap,x) \
    ((void *) (x)->curr_alloc)
#define DUK_HBUFFER_EXTERNAL_SET_DATA_PTR(heap,x,v)     do { \
        (x)->curr_alloc = (void *) (v); \
    } while (0)
#define DUK_HBUFFER_EXTERNAL_SET_DATA_PTR_NULL(heap,x)  do { \
        (x)->curr_alloc = (void *) NULL; \
    } while (0)
#endif

/* Get a pointer to the current buffer contents (matching current allocation
 * size).  May be NULL for zero size dynamic/external buffer.
 */
#if defined(DUK_USE_HEAPPTR16)
#define DUK_HBUFFER_GET_DATA_PTR(heap,x)  ( \
    DUK_HBUFFER_HAS_DYNAMIC((x)) ? \
        ( \
            DUK_HBUFFER_HAS_EXTERNAL((x)) ? \
                DUK_HBUFFER_EXTERNAL_GET_DATA_PTR((heap), (duk_hbuffer_external *) (x)) : \
                DUK_HBUFFER_DYNAMIC_GET_DATA_PTR((heap), (duk_hbuffer_dynamic *) (x)) \
        ) : \
        DUK_HBUFFER_FIXED_GET_DATA_PTR((heap), (duk_hbuffer_fixed *) (void *) (x)) \
    )
#else
/* Without heap pointer compression duk_hbuffer_dynamic and duk_hbuffer_external
 * have the same layout so checking for fixed vs. dynamic (or external) is enough.
 */
#define DUK_HBUFFER_GET_DATA_PTR(heap,x)  ( \
    DUK_HBUFFER_HAS_DYNAMIC((x)) ? \
        DUK_HBUFFER_DYNAMIC_GET_DATA_PTR((heap), (duk_hbuffer_dynamic *) (x)) : \
        DUK_HBUFFER_FIXED_GET_DATA_PTR((heap), (duk_hbuffer_fixed *) (void *) (x)) \
    )
#endif

/* Validity assert. */
#if defined(DUK_USE_ASSERTIONS)
DUK_INTERNAL_DECL void duk_hbuffer_assert_valid(duk_hbuffer *h);
#define DUK_HBUFFER_ASSERT_VALID(h)  do { duk_hbuffer_assert_valid((h)); } while (0)
#else
#define DUK_HBUFFER_ASSERT_VALID(h)  do {} while (0)
#endif

/*
 *  Structs
 */

/* Shared prefix for all buffer types. */
struct duk_hbuffer {
    duk_heaphdr hdr;

    /* It's not strictly necessary to track the current size, but
     * it is useful for writing robust native code.
     */

    /* Current size. */
#if defined(DUK_USE_BUFLEN16)
    /* Stored in duk_heaphdr unused flags. */
#else
    duk_size_t size;
#endif

    /*
     *  Data following the header depends on the DUK_HBUFFER_FLAG_DYNAMIC
     *  flag.
     *
     *  If the flag is clear (the buffer is a fixed size one), the buffer
     *  data follows the header directly, consisting of 'size' bytes.
     *
     *  If the flag is set, the actual buffer is allocated separately, and
     *  a few control fields follow the header.  Specifically:
     *
     *    - a "void *" pointing to the current allocation
     *    - a duk_size_t indicating the full allocated size (always >= 'size')
     *
     *  If DUK_HBUFFER_FLAG_EXTERNAL is set, the buffer has been allocated
     *  by user code, so that Duktape won't be able to resize it and won't
     *  free it.  This allows buffers to point to e.g. an externally
     *  allocated structure such as a frame buffer.
     *
     *  Unlike strings, no terminator byte (NUL) is guaranteed after the
     *  data.  This would be convenient, but would pad aligned user buffers
     *  unnecessarily upwards in size.  For instance, if user code requested
     *  a 64-byte dynamic buffer, 65 bytes would actually be allocated which
     *  would then potentially round upwards to perhaps 68 or 72 bytes.
     */
};

/* Fixed buffer; data follows struct, with proper alignment guaranteed by
 * struct size.
 */
#if (DUK_USE_ALIGN_BY == 8) && defined(DUK_USE_PACK_MSVC_PRAGMA)
#pragma pack(push, 8)
#endif
struct duk_hbuffer_fixed {
    /* A union is used here as a portable struct size / alignment trick:
     * by adding a 32-bit or a 64-bit (unused) union member, the size of
     * the struct is effectively forced to be a multiple of 4 or 8 bytes
     * (respectively) without increasing the size of the struct unless
     * necessary.
     */
    union {
        struct {
            duk_heaphdr hdr;
#if defined(DUK_USE_BUFLEN16)
            /* Stored in duk_heaphdr unused flags. */
#else
            duk_size_t size;
#endif
        } s;
#if (DUK_USE_ALIGN_BY == 4)
        duk_uint32_t dummy_for_align4;
#elif (DUK_USE_ALIGN_BY == 8)
        duk_double_t dummy_for_align8_1;
#if defined(DUK_USE_64BIT_OPS)
        duk_uint64_t dummy_for_align8_2;
#endif
#elif (DUK_USE_ALIGN_BY == 1)
        /* no extra padding */
#else
#error invalid DUK_USE_ALIGN_BY
#endif
    } u;

    /*
     *  Data follows the struct header.  The struct size is padded by the
     *  compiler based on the struct members.  This guarantees that the
     *  buffer data will be aligned-by-4 but not necessarily aligned-by-8.
     *
     *  On platforms where alignment does not matter, the struct padding
     *  could be removed (if there is any).  On platforms where alignment
     *  by 8 is required, the struct size must be forced to be a multiple
     *  of 8 by some means.  Without it, some user code may break, and also
     *  Duktape itself breaks (e.g. the compiler stores duk_tvals in a
     *  dynamic buffer).
     */
}
#if (DUK_USE_ALIGN_BY == 8) && defined(DUK_USE_PACK_GCC_ATTR)
__attribute__ ((aligned (8)))
#elif (DUK_USE_ALIGN_BY == 8) && defined(DUK_USE_PACK_CLANG_ATTR)
__attribute__ ((aligned (8)))
#endif
;
#if (DUK_USE_ALIGN_BY == 8) && defined(DUK_USE_PACK_MSVC_PRAGMA)
#pragma pack(pop)
#endif

/* Dynamic buffer with 'curr_alloc' pointing to a dynamic area allocated using
 * heap allocation primitives.  Also used for external buffers when low memory
 * options are not used.
 */
struct duk_hbuffer_dynamic {
    duk_heaphdr hdr;

#if defined(DUK_USE_BUFLEN16)
    /* Stored in duk_heaphdr unused flags. */
#else
    duk_size_t size;
#endif

#if defined(DUK_USE_HEAPPTR16)
    /* Stored in duk_heaphdr h_extra16. */
#else
    void *curr_alloc;  /* may be NULL if alloc_size == 0 */
#endif

    /*
     *  Allocation size for 'curr_alloc' is alloc_size.  There is no
     *  automatic NUL terminator for buffers (see above for rationale).
     *
     *  'curr_alloc' is explicitly allocated with heap allocation
     *  primitives and will thus always have alignment suitable for
     *  e.g. duk_tval and an IEEE double.
     */
};

/* External buffer with 'curr_alloc' managed by user code and pointing to an
 * arbitrary address.  When heap pointer compression is not used, this struct
 * has the same layout as duk_hbuffer_dynamic.
 */
struct duk_hbuffer_external {
    duk_heaphdr hdr;

#if defined(DUK_USE_BUFLEN16)
    /* Stored in duk_heaphdr unused flags. */
#else
    duk_size_t size;
#endif

    /* Cannot be compressed as a heap pointer because may point to
     * an arbitrary address.
     */
    void *curr_alloc;  /* may be NULL if alloc_size == 0 */
};

/*
 *  Prototypes
 */

DUK_INTERNAL_DECL duk_hbuffer *duk_hbuffer_alloc(duk_heap *heap, duk_size_t size, duk_small_uint_t flags, void **out_bufdata);
DUK_INTERNAL_DECL void *duk_hbuffer_get_dynalloc_ptr(duk_heap *heap, void *ud);  /* indirect allocs */

/* dynamic buffer ops */
DUK_INTERNAL_DECL void duk_hbuffer_resize(duk_hthread *thr, duk_hbuffer_dynamic *buf, duk_size_t new_size);
DUK_INTERNAL_DECL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic *buf);

#endif  /* DUK_HBUFFER_H_INCLUDED */
/* #include duk_hproxy.h */
//#line 1 "duk_hproxy.h"
/*
 *  Proxy object representation.
 */

#if !defined(DUK_HPROXY_H_INCLUDED)
#define DUK_HPROXY_H_INCLUDED

#if defined(DUK_USE_ASSERTIONS)
DUK_INTERNAL_DECL void duk_hproxy_assert_valid(duk_hproxy *h);
#define DUK_HPROXY_ASSERT_VALID(h)  do { duk_hproxy_assert_valid((h)); } while (0)
#else
#define DUK_HPROXY_ASSERT_VALID(h)  do {} while (0)
#endif

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

    /* Proxy target object. */
    duk_hobject *target;

    /* Proxy handlers (traps). */
    duk_hobject *handler;
};

#endif  /* DUK_HPROXY_H_INCLUDED */
/* #include duk_heap.h */
//#line 1 "duk_heap.h"
/*
 *  Heap structure.
 *
 *  Heap contains allocated heap objects, interned strings, and built-in
 *  strings for one or more threads.
 */

#if !defined(DUK_HEAP_H_INCLUDED)
#define DUK_HEAP_H_INCLUDED

/* alloc function typedefs in duktape.h */

/*
 *  Heap flags
 */

#define DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED            (1U << 0)  /* mark-and-sweep marking reached a recursion limit and must use multi-pass marking */
#define DUK_HEAP_FLAG_INTERRUPT_RUNNING                        (1U << 1)  /* executor interrupt running (used to avoid nested interrupts) */
#define DUK_HEAP_FLAG_FINALIZER_NORESCUE                       (1U << 2)  /* heap destruction ongoing, finalizer rescue no longer possible */
#define DUK_HEAP_FLAG_DEBUGGER_PAUSED                          (1U << 3)  /* debugger is paused: talk with debug client until step/resume */

#define DUK__HEAP_HAS_FLAGS(heap,bits)               ((heap)->flags & (bits))
#define DUK__HEAP_SET_FLAGS(heap,bits)  do { \
        (heap)->flags |= (bits); \
    } while (0)
#define DUK__HEAP_CLEAR_FLAGS(heap,bits)  do { \
        (heap)->flags &= ~(bits); \
    } while (0)

#define DUK_HEAP_HAS_MARKANDSWEEP_RECLIMIT_REACHED(heap)   DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED)
#define DUK_HEAP_HAS_INTERRUPT_RUNNING(heap)               DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_INTERRUPT_RUNNING)
#define DUK_HEAP_HAS_FINALIZER_NORESCUE(heap)              DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_FINALIZER_NORESCUE)
#define DUK_HEAP_HAS_DEBUGGER_PAUSED(heap)                 DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_DEBUGGER_PAUSED)

#define DUK_HEAP_SET_MARKANDSWEEP_RECLIMIT_REACHED(heap)   DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED)
#define DUK_HEAP_SET_INTERRUPT_RUNNING(heap)               DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_INTERRUPT_RUNNING)
#define DUK_HEAP_SET_FINALIZER_NORESCUE(heap)              DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_FINALIZER_NORESCUE)
#define DUK_HEAP_SET_DEBUGGER_PAUSED(heap)                 DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_DEBUGGER_PAUSED)

#define DUK_HEAP_CLEAR_MARKANDSWEEP_RECLIMIT_REACHED(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED)
#define DUK_HEAP_CLEAR_INTERRUPT_RUNNING(heap)             DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_INTERRUPT_RUNNING)
#define DUK_HEAP_CLEAR_FINALIZER_NORESCUE(heap)            DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_FINALIZER_NORESCUE)
#define DUK_HEAP_CLEAR_DEBUGGER_PAUSED(heap)               DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_DEBUGGER_PAUSED)

/*
 *  Longjmp types, also double as identifying continuation type for a rethrow (in 'finally')
 */

#define DUK_LJ_TYPE_UNKNOWN      0    /* unused */
#define DUK_LJ_TYPE_THROW        1    /* value1 -> error object */
#define DUK_LJ_TYPE_YIELD        2    /* value1 -> yield value, iserror -> error / normal */
#define DUK_LJ_TYPE_RESUME       3    /* value1 -> resume value, value2 -> resumee thread, iserror -> error/normal */
#define DUK_LJ_TYPE_BREAK        4    /* value1 -> label number, pseudo-type to indicate a break continuation (for ENDFIN) */
#define DUK_LJ_TYPE_CONTINUE     5    /* value1 -> label number, pseudo-type to indicate a continue continuation (for ENDFIN) */
#define DUK_LJ_TYPE_RETURN       6    /* value1 -> return value, pseudo-type to indicate a return continuation (for ENDFIN) */
#define DUK_LJ_TYPE_NORMAL       7    /* no value, pseudo-type to indicate a normal continuation (for ENDFIN) */

/*
 *  Mark-and-sweep flags
 *
 *  These are separate from heap level flags now but could be merged.
 *  The heap structure only contains a 'base mark-and-sweep flags'
 *  field and the GC caller can impose further flags.
 */

/* Emergency mark-and-sweep: try extra hard, even at the cost of
 * performance.
 */
#define DUK_MS_FLAG_EMERGENCY                (1U << 0)

/* Postpone rescue decisions for reachable objects with FINALIZED set.
 * Used during finalize_list processing to avoid incorrect rescue
 * decisions due to finalize_list being a reachability root.
 */
#define DUK_MS_FLAG_POSTPONE_RESCUE          (1U << 1)

/* Don't compact objects; needed during object property table resize
 * to prevent a recursive resize.  It would suffice to protect only the
 * current object being resized, but this is not yet implemented.
 */
#define DUK_MS_FLAG_NO_OBJECT_COMPACTION     (1U << 2)

/*
 *  Thread switching
 *
 *  To switch heap->curr_thread, use the macro below so that interrupt counters
 *  get updated correctly.  The macro allows a NULL target thread because that
 *  happens e.g. in call handling.
 */

#if defined(DUK_USE_INTERRUPT_COUNTER)
#define DUK_HEAP_SWITCH_THREAD(heap,newthr)  duk_heap_switch_thread((heap), (newthr))
#else
#define DUK_HEAP_SWITCH_THREAD(heap,newthr)  do { \
        (heap)->curr_thread = (newthr); \
    } while (0)
#endif

/*
 *  Stats
 */

#if defined(DUK_USE_DEBUG)
#define DUK_STATS_INC(heap,fieldname) do { \
        (heap)->fieldname += 1; \
    } while (0)
#else
#define DUK_STATS_INC(heap,fieldname) do {} while (0)
#endif

/*
 *  Other heap related defines
 */

/* Mark-and-sweep interval is relative to combined count of objects and
 * strings kept in the heap during the latest mark-and-sweep pass.
 * Fixed point .8 multiplier and .0 adder.  Trigger count (interval) is
 * decreased by each (re)allocation attempt (regardless of size), and each
 * refzero processed object.
 *
 * 'SKIP' indicates how many (re)allocations to wait until a retry if
 * GC is skipped because there is no thread do it with yet (happens
 * only during init phases).
 */
#if defined(DUK_USE_REFERENCE_COUNTING)
#define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_MULT              12800L  /* 50x heap size */
#define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_ADD               1024L
#define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_SKIP              256L
#else
#define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_MULT              256L    /* 1x heap size */
#define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_ADD               1024L
#define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_SKIP              256L
#endif

/* GC torture. */
#if defined(DUK_USE_GC_TORTURE)
#define DUK_GC_TORTURE(heap) do { duk_heap_mark_and_sweep((heap), 0); } while (0)
#else
#define DUK_GC_TORTURE(heap) do { } while (0)
#endif

/* Stringcache is used for speeding up char-offset-to-byte-offset
 * translations for non-ASCII strings.
 */
#define DUK_HEAP_STRCACHE_SIZE                            4
#define DUK_HEAP_STRINGCACHE_NOCACHE_LIMIT                16  /* strings up to the this length are not cached */

/* Some list management macros. */
#define DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap,hdr)     duk_heap_insert_into_heap_allocated((heap), (hdr))
#if defined(DUK_USE_REFERENCE_COUNTING)
#define DUK_HEAP_REMOVE_FROM_HEAP_ALLOCATED(heap,hdr)     duk_heap_remove_from_heap_allocated((heap), (hdr))
#endif
#if defined(DUK_USE_FINALIZER_SUPPORT)
#define DUK_HEAP_INSERT_INTO_FINALIZE_LIST(heap,hdr)      duk_heap_insert_into_finalize_list((heap), (hdr))
#define DUK_HEAP_REMOVE_FROM_FINALIZE_LIST(heap,hdr)      duk_heap_remove_from_finalize_list((heap), (hdr))
#endif

/*
 *  Built-in strings
 */

/* heap string indices are autogenerated in duk_strings.h */
#if defined(DUK_USE_ROM_STRINGS)
#define DUK_HEAP_GET_STRING(heap,idx) \
    ((duk_hstring *) DUK_LOSE_CONST(duk_rom_strings_stridx[(idx)]))
#else  /* DUK_USE_ROM_STRINGS */
#if defined(DUK_USE_HEAPPTR16)
#define DUK_HEAP_GET_STRING(heap,idx) \
    ((duk_hstring *) DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (heap)->strs16[(idx)]))
#else
#define DUK_HEAP_GET_STRING(heap,idx) \
    ((heap)->strs[(idx)])
#endif
#endif  /* DUK_USE_ROM_STRINGS */

/*
 *  Raw memory calls: relative to heap, but no GC interaction
 */

#define DUK_ALLOC_RAW(heap,size) \
    ((heap)->alloc_func((heap)->heap_udata, (size)))

#define DUK_REALLOC_RAW(heap,ptr,newsize) \
    ((heap)->realloc_func((heap)->heap_udata, (void *) (ptr), (newsize)))

#define DUK_FREE_RAW(heap,ptr) \
    ((heap)->free_func((heap)->heap_udata, (void *) (ptr)))

/*
 *  Memory calls: relative to heap, GC interaction, but no error throwing.
 *
 *  XXX: Currently a mark-and-sweep triggered by memory allocation will run
 *  using the heap->heap_thread.  This thread is also used for running
 *  mark-and-sweep finalization; this is not ideal because it breaks the
 *  isolation between multiple global environments.
 *
 *  Notes:
 *
 *    - DUK_FREE() is required to ignore NULL and any other possible return
 *      value of a zero-sized alloc/realloc (same as ANSI C free()).
 *
 *    - There is no DUK_REALLOC_ZEROED because we don't assume to know the
 *      old size.  Caller must zero the reallocated memory.
 *
 *    - DUK_REALLOC_INDIRECT() must be used when a mark-and-sweep triggered
 *      by an allocation failure might invalidate the original 'ptr', thus
 *      causing a realloc retry to use an invalid pointer.  Example: we're
 *      reallocating the value stack and a finalizer resizes the same value
 *      stack during mark-and-sweep.  The indirect variant requests for the
 *      current location of the pointer being reallocated using a callback
 *      right before every realloc attempt; this circuitous approach is used
 *      to avoid strict aliasing issues in a more straightforward indirect
 *      pointer (void **) approach.  Note: the pointer in the storage
 *      location is read but is NOT updated; the caller must do that.
 */

/* callback for indirect reallocs, request for current pointer */
typedef void *(*duk_mem_getptr)(duk_heap *heap, void *ud);

#define DUK_ALLOC(heap,size)                            duk_heap_mem_alloc((heap), (size))
#define DUK_ALLOC_ZEROED(heap,size)                     duk_heap_mem_alloc_zeroed((heap), (size))
#define DUK_REALLOC(heap,ptr,newsize)                   duk_heap_mem_realloc((heap), (ptr), (newsize))
#define DUK_REALLOC_INDIRECT(heap,cb,ud,newsize)        duk_heap_mem_realloc_indirect((heap), (cb), (ud), (newsize))
#define DUK_FREE(heap,ptr)                              duk_heap_mem_free((heap), (ptr))

/*
 *  Checked allocation, relative to a thread
 *
 *  DUK_FREE_CHECKED() doesn't actually throw, but accepts a 'thr' argument
 *  for convenience.
 */

#define DUK_ALLOC_CHECKED(thr,size)                     duk_heap_mem_alloc_checked((thr), (size))
#define DUK_ALLOC_CHECKED_ZEROED(thr,size)              duk_heap_mem_alloc_checked_zeroed((thr), (size))
#define DUK_FREE_CHECKED(thr,ptr)                       duk_heap_mem_free((thr)->heap, (ptr))

/*
 *  Memory constants
 */

#define DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_LIMIT           10  /* Retry allocation after mark-and-sweep for this
                                                              * many times.  A single mark-and-sweep round is
                                                              * not guaranteed to free all unreferenced memory
                                                              * because of finalization (in fact, ANY number of
                                                              * rounds is strictly not enough).
                                                              */

#define DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_EMERGENCY_LIMIT  3  /* Starting from this round, use emergency mode
                                                              * for mark-and-sweep.
                                                              */

/*
 *  Debugger support
 */

/* Maximum number of breakpoints.  Only breakpoints that are set are
 * consulted so increasing this has no performance impact.
 */
#define DUK_HEAP_MAX_BREAKPOINTS          16

/* Opcode interval for a Date-based status/peek rate limit check.  Only
 * relevant when debugger is attached.  Requesting a timestamp may be a
 * slow operation on some platforms so this shouldn't be too low.  On the
 * other hand a high value makes Duktape react to a pause request slowly.
 */
#define DUK_HEAP_DBG_RATELIMIT_OPCODES    4000

/* Milliseconds between status notify and transport peeks. */
#define DUK_HEAP_DBG_RATELIMIT_MILLISECS  200

/* Debugger pause flags. */
#define DUK_PAUSE_FLAG_ONE_OPCODE        (1U << 0)   /* pause when a single opcode has been executed */
#define DUK_PAUSE_FLAG_ONE_OPCODE_ACTIVE (1U << 1)   /* one opcode pause actually active; artifact of current implementation */
#define DUK_PAUSE_FLAG_LINE_CHANGE       (1U << 2)   /* pause when current line number changes */
#define DUK_PAUSE_FLAG_FUNC_ENTRY        (1U << 3)   /* pause when entering a function */
#define DUK_PAUSE_FLAG_FUNC_EXIT         (1U << 4)   /* pause when exiting current function */
#define DUK_PAUSE_FLAG_CAUGHT_ERROR      (1U << 5)   /* pause when about to throw an error that is caught */
#define DUK_PAUSE_FLAG_UNCAUGHT_ERROR    (1U << 6)   /* pause when about to throw an error that won't be caught */

struct duk_breakpoint {
    duk_hstring *filename;
    duk_uint32_t line;
};

/*
 *  String cache should ideally be at duk_hthread level, but that would
 *  cause string finalization to slow down relative to the number of
 *  threads; string finalization must check the string cache for "weak"
 *  references to the string being finalized to avoid dead pointers.
 *
 *  Thus, string caches are now at the heap level now.
 */

struct duk_strcache_entry {
    duk_hstring *h;
    duk_uint32_t bidx;
    duk_uint32_t cidx;
};

/*
 *  Longjmp state, contains the information needed to perform a longjmp.
 *  Longjmp related values are written to value1, value2, and iserror.
 */

struct duk_ljstate {
    duk_jmpbuf *jmpbuf_ptr;   /* current setjmp() catchpoint */
    duk_small_uint_t type;    /* longjmp type */
    duk_bool_t iserror;       /* isError flag for yield */
    duk_tval value1;          /* 1st related value (type specific) */
    duk_tval value2;          /* 2nd related value (type specific) */
};

#define DUK_ASSERT_LJSTATE_UNSET(heap) do { \
        DUK_ASSERT(heap != NULL); \
        DUK_ASSERT(heap->lj.type == DUK_LJ_TYPE_UNKNOWN); \
        DUK_ASSERT(heap->lj.iserror == 0); \
        DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&heap->lj.value1)); \
        DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&heap->lj.value2)); \
    } while (0)
#define DUK_ASSERT_LJSTATE_SET(heap) do { \
        DUK_ASSERT(heap != NULL); \
        DUK_ASSERT(heap->lj.type != DUK_LJ_TYPE_UNKNOWN); \
    } while (0)

/*
 *  Literal intern cache
 */

struct duk_litcache_entry {
    const duk_uint8_t *addr;
    duk_hstring *h;
};

/*
 *  Main heap structure
 */

#if defined(DUK_USE_ASSERTIONS)
DUK_INTERNAL_DECL void duk_heap_assert_valid(duk_heap *heap);
#define DUK_HEAP_ASSERT_VALID(heap)  do { duk_heap_assert_valid((heap)); } while (0)
#else
#define DUK_HEAP_ASSERT_VALID(heap)  do {} while (0)
#endif

struct duk_heap {
    duk_small_uint_t flags;

    /* Allocator functions. */
    duk_alloc_function alloc_func;
    duk_realloc_function realloc_func;
    duk_free_function free_func;

    /* Heap udata, used for allocator functions but also for other heap
     * level callbacks like fatal function, pointer compression, etc.
     */
    void *heap_udata;

    /* Fatal error handling, called e.g. when a longjmp() is needed but
     * lj.jmpbuf_ptr is NULL.  fatal_func must never return; it's not
     * declared as "noreturn" because doing that for typedefs is a bit
     * challenging portability-wise.
     */
    duk_fatal_function fatal_func;

    /* Main list of allocated heap objects.  Objects are either here,
     * in finalize_list waiting for processing, or in refzero_list
     * temporarily while a DECREF refzero cascade finishes.
     */
    duk_heaphdr *heap_allocated;

    /* Temporary work list for freeing a cascade of objects when a DECREF
     * (or DECREF_NORZ) encounters a zero refcount.  Using a work list
     * allows fixed C stack size when refcounts go to zero for a chain of
     * objects.  Outside of DECREF this is always a NULL because DECREF is
     * processed without side effects (only memory free calls).
     */
#if defined(DUK_USE_REFERENCE_COUNTING)
    duk_heaphdr *refzero_list;
#endif

#if defined(DUK_USE_FINALIZER_SUPPORT)
    /* Work list for objects to be finalized. */
    duk_heaphdr *finalize_list;
#if defined(DUK_USE_ASSERTIONS)
    /* Object whose finalizer is executing right now (no nesting). */
    duk_heaphdr *currently_finalizing;
#endif
#endif

    /* Freelist for duk_activations and duk_catchers. */
#if defined(DUK_USE_CACHE_ACTIVATION)
    duk_activation *activation_free;
#endif
#if defined(DUK_USE_CACHE_CATCHER)
    duk_catcher *catcher_free;
#endif

    /* Voluntary mark-and-sweep trigger counter.  Intentionally signed
     * because we continue decreasing the value when voluntary GC cannot
     * run.
     */
#if defined(DUK_USE_VOLUNTARY_GC)
    duk_int_t ms_trigger_counter;
#endif

    /* Mark-and-sweep recursion control: too deep recursion causes
     * multi-pass processing to avoid growing C stack without bound.
     */
    duk_uint_t ms_recursion_depth;

    /* Mark-and-sweep flags automatically active (used for critical sections). */
    duk_small_uint_t ms_base_flags;

    /* Mark-and-sweep running flag.  Prevents re-entry, and also causes
     * refzero events to be ignored (= objects won't be queued to refzero_list).
     *
     * 0: mark-and-sweep not running
     * 1: mark-and-sweep is running
     * 2: heap destruction active or debugger active, prevent mark-and-sweep
     *    and refzero processing (but mark-and-sweep not itself running)
     */
    duk_uint_t ms_running;

    /* Mark-and-sweep prevent count, stacking.  Used to avoid M&S side
     * effects (besides finalizers which are controlled separately) such
     * as compacting the string table or object property tables.  This
     * is also bumped when ms_running is set to prevent recursive re-entry.
     * Can also be bumped when mark-and-sweep is not running.
     */
    duk_uint_t ms_prevent_count;

    /* Finalizer processing prevent count, stacking.  Bumped when finalizers
     * are processed to prevent recursive finalizer processing (first call site
     * processing finalizers handles all finalizers until the list is empty).
     * Can also be bumped explicitly to prevent finalizer execution.
     */
    duk_uint_t pf_prevent_count;

    /* When processing finalize_list, don't actually run finalizers but
     * queue finalizable objects back to heap_allocated as is.  This is
     * used during heap destruction to deal with finalizers that keep
     * on creating more finalizable garbage.
     */
    duk_uint_t pf_skip_finalizers;

#if defined(DUK_USE_ASSERTIONS)
    /* Set when we're in a critical path where an error throw would cause
     * e.g. sandboxing/protected call violations or state corruption.  This
     * is just used for asserts.
     */
    duk_bool_t error_not_allowed;
#endif

#if defined(DUK_USE_ASSERTIONS)
    /* Set when heap is still being initialized, helps with writing
     * some assertions.
     */
    duk_bool_t heap_initializing;
#endif

    /* Marker for detecting internal "double faults", errors thrown when
     * we're trying to create an error object, see duk_error_throw.c.
     */
    duk_bool_t creating_error;

    /* Marker for indicating we're calling a user error augmentation
     * (errCreate/errThrow) function.  Errors created/thrown during
     * such a call are not augmented.
     */
#if defined(DUK_USE_AUGMENT_ERROR_THROW) || defined(DUK_USE_AUGMENT_ERROR_CREATE)
    duk_bool_t augmenting_error;
#endif

    /* Longjmp state. */
    duk_ljstate lj;

    /* Heap thread, used internally and for finalization. */
    duk_hthread *heap_thread;

    /* Current running thread. */
    duk_hthread *curr_thread;

    /* Heap level "stash" object (e.g., various reachability roots). */
    duk_hobject *heap_object;

    /* duk_handle_call / duk_handle_safe_call recursion depth limiting */
    duk_int_t call_recursion_depth;
    duk_int_t call_recursion_limit;

    /* Mix-in value for computing string hashes; should be reasonably unpredictable. */
    duk_uint32_t hash_seed;

    /* Random number state for duk_util_tinyrandom.c. */
#if !defined(DUK_USE_GET_RANDOM_DOUBLE)
#if defined(DUK_USE_PREFER_SIZE) || !defined(DUK_USE_64BIT_OPS)
    duk_uint32_t rnd_state;  /* State for Shamir's three-op algorithm */
#else
    duk_uint64_t rnd_state[2];  /* State for xoroshiro128+ */
#endif
#endif

    /* Counter for unique local symbol creation. */
    /* XXX: When 64-bit types are available, it would be more efficient to
     * use a duk_uint64_t at least for incrementing but maybe also for
     * string formatting in the Symbol constructor.
     */
    duk_uint32_t sym_counter[2];

    /* For manual debugging: instruction count based on executor and
     * interrupt counter book-keeping.  Inspect debug logs to see how
     * they match up.
     */
#if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG)
    duk_int_t inst_count_exec;
    duk_int_t inst_count_interrupt;
#endif

    /* Debugger state. */
#if defined(DUK_USE_DEBUGGER_SUPPORT)
    /* Callbacks and udata; dbg_read_cb != NULL is used to indicate attached state. */
    duk_debug_read_function dbg_read_cb;                /* required, NULL implies detached */
    duk_debug_write_function dbg_write_cb;              /* required */
    duk_debug_peek_function dbg_peek_cb;
    duk_debug_read_flush_function dbg_read_flush_cb;
    duk_debug_write_flush_function dbg_write_flush_cb;
    duk_debug_request_function dbg_request_cb;
    duk_debug_detached_function dbg_detached_cb;
    void *dbg_udata;

    /* The following are only relevant when debugger is attached. */
    duk_bool_t dbg_processing;              /* currently processing messages or breakpoints: don't enter message processing recursively (e.g. no breakpoints when processing debugger eval) */
    duk_bool_t dbg_state_dirty;             /* resend state next time executor is about to run */
    duk_bool_t dbg_force_restart;           /* force executor restart to recheck breakpoints; used to handle function returns (see GH-303) */
    duk_bool_t dbg_detaching;               /* debugger detaching; used to avoid calling detach handler recursively */
    duk_small_uint_t dbg_pause_flags;       /* flags for automatic pause behavior */
    duk_activation *dbg_pause_act;          /* activation related to pause behavior (pause on line change, function entry/exit) */
    duk_uint32_t dbg_pause_startline;       /* starting line number for line change related pause behavior */
    duk_breakpoint dbg_breakpoints[DUK_HEAP_MAX_BREAKPOINTS];  /* breakpoints: [0,breakpoint_count[ gc reachable */
    duk_small_uint_t dbg_breakpoint_count;
    duk_breakpoint *dbg_breakpoints_active[DUK_HEAP_MAX_BREAKPOINTS + 1];  /* currently active breakpoints: NULL term, borrowed pointers */
    /* XXX: make active breakpoints actual copies instead of pointers? */

    /* These are for rate limiting Status notifications and transport peeking. */
    duk_uint_t dbg_exec_counter;            /* cumulative opcode execution count (overflows are OK) */
    duk_uint_t dbg_last_counter;            /* value of dbg_exec_counter when we last did a Date-based check */
    duk_double_t dbg_last_time;             /* time when status/peek was last done (Date-based rate limit) */

    /* Used to support single-byte stream lookahead. */
    duk_bool_t dbg_have_next_byte;
    duk_uint8_t dbg_next_byte;
#endif  /* DUK_USE_DEBUGGER_SUPPORT */
#if defined(DUK_USE_ASSERTIONS)
    duk_bool_t dbg_calling_transport;       /* transport call in progress, calling into Duktape forbidden */
#endif

    /* String intern table (weak refs). */
#if defined(DUK_USE_STRTAB_PTRCOMP)
    duk_uint16_t *strtable16;
#else
    duk_hstring **strtable;
#endif
    duk_uint32_t st_mask;    /* mask for lookup, st_size - 1 */
    duk_uint32_t st_size;    /* stringtable size */
#if (DUK_USE_STRTAB_MINSIZE != DUK_USE_STRTAB_MAXSIZE)
    duk_uint32_t st_count;   /* string count for resize load factor checks */
#endif
    duk_bool_t st_resizing;  /* string table is being resized; avoid recursive resize */

    /* String access cache (codepoint offset -> byte offset) for fast string
     * character looping; 'weak' reference which needs special handling in GC.
     */
    duk_strcache_entry strcache[DUK_HEAP_STRCACHE_SIZE];

#if defined(DUK_USE_LITCACHE_SIZE)
    /* Literal intern cache.  When enabled, strings interned as literals
     * (e.g. duk_push_literal()) will be pinned and cached for the lifetime
     * of the heap.
     */
    duk_litcache_entry litcache[DUK_USE_LITCACHE_SIZE];
#endif

    /* Built-in strings. */
#if defined(DUK_USE_ROM_STRINGS)
    /* No field needed when strings are in ROM. */
#else
#if defined(DUK_USE_HEAPPTR16)
    duk_uint16_t strs16[DUK_HEAP_NUM_STRINGS];
#else
    duk_hstring *strs[DUK_HEAP_NUM_STRINGS];
#endif
#endif

    /* Stats. */
#if defined(DUK_USE_DEBUG)
    duk_int_t stats_exec_opcodes;
    duk_int_t stats_exec_interrupt;
    duk_int_t stats_exec_throw;
    duk_int_t stats_call_all;
    duk_int_t stats_call_tailcall;
    duk_int_t stats_call_ecmatoecma;
    duk_int_t stats_safecall_all;
    duk_int_t stats_safecall_nothrow;
    duk_int_t stats_safecall_throw;
    duk_int_t stats_ms_try_count;
    duk_int_t stats_ms_skip_count;
    duk_int_t stats_ms_emergency_count;
    duk_int_t stats_strtab_intern_hit;
    duk_int_t stats_strtab_intern_miss;
    duk_int_t stats_strtab_resize_check;
    duk_int_t stats_strtab_resize_grow;
    duk_int_t stats_strtab_resize_shrink;
    duk_int_t stats_strtab_litcache_hit;
    duk_int_t stats_strtab_litcache_miss;
    duk_int_t stats_strtab_litcache_pin;
    duk_int_t stats_object_realloc_props;
    duk_int_t stats_object_abandon_array;
    duk_int_t stats_getownpropdesc_count;
    duk_int_t stats_getownpropdesc_hit;
    duk_int_t stats_getownpropdesc_miss;
    duk_int_t stats_getpropdesc_count;
    duk_int_t stats_getpropdesc_hit;
    duk_int_t stats_getpropdesc_miss;
    duk_int_t stats_getprop_all;
    duk_int_t stats_getprop_arrayidx;
    duk_int_t stats_getprop_bufobjidx;
    duk_int_t stats_getprop_bufferidx;
    duk_int_t stats_getprop_bufferlen;
    duk_int_t stats_getprop_stringidx;
    duk_int_t stats_getprop_stringlen;
    duk_int_t stats_getprop_proxy;
    duk_int_t stats_getprop_arguments;
    duk_int_t stats_putprop_all;
    duk_int_t stats_putprop_arrayidx;
    duk_int_t stats_putprop_bufobjidx;
    duk_int_t stats_putprop_bufferidx;
    duk_int_t stats_putprop_proxy;
    duk_int_t stats_getvar_all;
    duk_int_t stats_putvar_all;
    duk_int_t stats_envrec_delayedcreate;
    duk_int_t stats_envrec_create;
    duk_int_t stats_envrec_newenv;
    duk_int_t stats_envrec_oldenv;
    duk_int_t stats_envrec_pushclosure;
#endif
};

/*
 *  Prototypes
 */

DUK_INTERNAL_DECL
duk_heap *duk_heap_alloc(duk_alloc_function alloc_func,
                         duk_realloc_function realloc_func,
                         duk_free_function free_func,
                         void *heap_udata,
                         duk_fatal_function fatal_func);
DUK_INTERNAL_DECL void duk_heap_free(duk_heap *heap);
DUK_INTERNAL_DECL void duk_free_hobject(duk_heap *heap, duk_hobject *h);
DUK_INTERNAL_DECL void duk_free_hbuffer(duk_heap *heap, duk_hbuffer *h);
DUK_INTERNAL_DECL void duk_free_hstring(duk_heap *heap, duk_hstring *h);
DUK_INTERNAL_DECL void duk_heap_free_heaphdr_raw(duk_heap *heap, duk_heaphdr *hdr);

DUK_INTERNAL_DECL void duk_heap_insert_into_heap_allocated(duk_heap *heap, duk_heaphdr *hdr);
#if defined(DUK_USE_REFERENCE_COUNTING)
DUK_INTERNAL_DECL void duk_heap_remove_from_heap_allocated(duk_heap *heap, duk_heaphdr *hdr);
#endif
#if defined(DUK_USE_FINALIZER_SUPPORT)
DUK_INTERNAL_DECL void duk_heap_insert_into_finalize_list(duk_heap *heap, duk_heaphdr *hdr);
DUK_INTERNAL_DECL void duk_heap_remove_from_finalize_list(duk_heap *heap, duk_heaphdr *hdr);
#endif
#if defined(DUK_USE_ASSERTIONS)
DUK_INTERNAL_DECL duk_bool_t duk_heap_in_heap_allocated(duk_heap *heap, duk_heaphdr *ptr);
#endif
#if defined(DUK_USE_INTERRUPT_COUNTER)
DUK_INTERNAL_DECL void duk_heap_switch_thread(duk_heap *heap, duk_hthread *new_thr);
#endif

DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen);
DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern_checked(duk_hthread *thr, const duk_uint8_t *str, duk_uint32_t len);
#if defined(DUK_USE_LITCACHE_SIZE)
DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern_literal_checked(duk_hthread *thr, const duk_uint8_t *str, duk_uint32_t blen);
#endif
DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern_u32(duk_heap *heap, duk_uint32_t val);
DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern_u32_checked(duk_hthread *thr, duk_uint32_t val);
#if defined(DUK_USE_REFERENCE_COUNTING)
DUK_INTERNAL_DECL void duk_heap_strtable_unlink(duk_heap *heap, duk_hstring *h);
#endif
DUK_INTERNAL_DECL void duk_heap_strtable_unlink_prev(duk_heap *heap, duk_hstring *h, duk_hstring *prev);
DUK_INTERNAL_DECL void duk_heap_strtable_force_resize(duk_heap *heap);
DUK_INTERNAL void duk_heap_strtable_free(duk_heap *heap);
#if defined(DUK_USE_DEBUG)
DUK_INTERNAL void duk_heap_strtable_dump(duk_heap *heap);
#endif

DUK_INTERNAL_DECL void duk_heap_strcache_string_remove(duk_heap *heap, duk_hstring *h);
DUK_INTERNAL_DECL duk_uint_fast32_t duk_heap_strcache_offset_char2byte(duk_hthread *thr, duk_hstring *h, duk_uint_fast32_t char_offset);

#if defined(DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS)
DUK_INTERNAL_DECL void *duk_default_alloc_function(void *udata, duk_size_t size);
DUK_INTERNAL_DECL void *duk_default_realloc_function(void *udata, void *ptr, duk_size_t newsize);
DUK_INTERNAL_DECL void duk_default_free_function(void *udata, void *ptr);
#endif

DUK_INTERNAL_DECL void *duk_heap_mem_alloc(duk_heap *heap, duk_size_t size);
DUK_INTERNAL_DECL void *duk_heap_mem_alloc_zeroed(duk_heap *heap, duk_size_t size);
DUK_INTERNAL_DECL void *duk_heap_mem_alloc_checked(duk_hthread *thr, duk_size_t size);
DUK_INTERNAL_DECL void *duk_heap_mem_alloc_checked_zeroed(duk_hthread *thr, duk_size_t size);
DUK_INTERNAL_DECL void *duk_heap_mem_realloc(duk_heap *heap, void *ptr, duk_size_t newsize);
DUK_INTERNAL_DECL void *duk_heap_mem_realloc_indirect(duk_heap *heap, duk_mem_getptr cb, void *ud, duk_size_t newsize);
DUK_INTERNAL_DECL void duk_heap_mem_free(duk_heap *heap, void *ptr);

DUK_INTERNAL_DECL void duk_heap_free_freelists(duk_heap *heap);

#if defined(DUK_USE_FINALIZER_SUPPORT)
DUK_INTERNAL_DECL void duk_heap_run_finalizer(duk_heap *heap, duk_hobject *obj);
DUK_INTERNAL_DECL void duk_heap_process_finalize_list(duk_heap *heap);
#endif  /* DUK_USE_FINALIZER_SUPPORT */

DUK_INTERNAL_DECL void duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t flags);

DUK_INTERNAL_DECL duk_uint32_t duk_heap_hashstring(duk_heap *heap, const duk_uint8_t *str, duk_size_t len);

#endif  /* DUK_HEAP_H_INCLUDED */
/* #include duk_debugger.h */
//#line 1 "duk_debugger.h"
#if !defined(DUK_DEBUGGER_H_INCLUDED)
#define DUK_DEBUGGER_H_INCLUDED

/* Debugger protocol version is defined in the public API header. */

/* Initial bytes for markers. */
#define DUK_DBG_IB_EOM                   0x00
#define DUK_DBG_IB_REQUEST               0x01
#define DUK_DBG_IB_REPLY                 0x02
#define DUK_DBG_IB_ERROR                 0x03
#define DUK_DBG_IB_NOTIFY                0x04

/* Other initial bytes. */
#define DUK_DBG_IB_INT4                  0x10
#define DUK_DBG_IB_STR4                  0x11
#define DUK_DBG_IB_STR2                  0x12
#define DUK_DBG_IB_BUF4                  0x13
#define DUK_DBG_IB_BUF2                  0x14
#define DUK_DBG_IB_UNUSED                0x15
#define DUK_DBG_IB_UNDEFINED             0x16
#define DUK_DBG_IB_NULL                  0x17
#define DUK_DBG_IB_TRUE                  0x18
#define DUK_DBG_IB_FALSE                 0x19
#define DUK_DBG_IB_NUMBER                0x1a
#define DUK_DBG_IB_OBJECT                0x1b
#define DUK_DBG_IB_POINTER               0x1c
#define DUK_DBG_IB_LIGHTFUNC             0x1d
#define DUK_DBG_IB_HEAPPTR               0x1e
/* The short string/integer initial bytes starting from 0x60 don't have
 * defines now.
 */

/* Error codes. */
#define DUK_DBG_ERR_UNKNOWN              0x00
#define DUK_DBG_ERR_UNSUPPORTED          0x01
#define DUK_DBG_ERR_TOOMANY              0x02
#define DUK_DBG_ERR_NOTFOUND             0x03
#define DUK_DBG_ERR_APPLICATION          0x04

/* Commands and notifys initiated by Duktape. */
#define DUK_DBG_CMD_STATUS               0x01
#define DUK_DBG_CMD_UNUSED_2             0x02  /* Duktape 1.x: print notify */
#define DUK_DBG_CMD_UNUSED_3             0x03  /* Duktape 1.x: alert notify */
#define DUK_DBG_CMD_UNUSED_4             0x04  /* Duktape 1.x: log notify */
#define DUK_DBG_CMD_THROW                0x05
#define DUK_DBG_CMD_DETACHING            0x06
#define DUK_DBG_CMD_APPNOTIFY            0x07

/* Commands initiated by debug client. */
#define DUK_DBG_CMD_BASICINFO            0x10
#define DUK_DBG_CMD_TRIGGERSTATUS        0x11
#define DUK_DBG_CMD_PAUSE                0x12
#define DUK_DBG_CMD_RESUME               0x13
#define DUK_DBG_CMD_STEPINTO             0x14
#define DUK_DBG_CMD_STEPOVER             0x15
#define DUK_DBG_CMD_STEPOUT              0x16
#define DUK_DBG_CMD_LISTBREAK            0x17
#define DUK_DBG_CMD_ADDBREAK             0x18
#define DUK_DBG_CMD_DELBREAK             0x19
#define DUK_DBG_CMD_GETVAR               0x1a
#define DUK_DBG_CMD_PUTVAR               0x1b
#define DUK_DBG_CMD_GETCALLSTACK         0x1c
#define DUK_DBG_CMD_GETLOCALS            0x1d
#define DUK_DBG_CMD_EVAL                 0x1e
#define DUK_DBG_CMD_DETACH               0x1f
#define DUK_DBG_CMD_DUMPHEAP             0x20
#define DUK_DBG_CMD_GETBYTECODE          0x21
#define DUK_DBG_CMD_APPREQUEST           0x22
#define DUK_DBG_CMD_GETHEAPOBJINFO       0x23
#define DUK_DBG_CMD_GETOBJPROPDESC       0x24
#define DUK_DBG_CMD_GETOBJPROPDESCRANGE  0x25

/* The low 8 bits map directly to duk_hobject.h DUK_PROPDESC_FLAG_xxx.
 * The remaining flags are specific to the debugger.
 */
#define DUK_DBG_PROPFLAG_SYMBOL          (1U << 8)
#define DUK_DBG_PROPFLAG_HIDDEN          (1U << 9)

#if defined(DUK_USE_DEBUGGER_SUPPORT)
DUK_INTERNAL_DECL void duk_debug_do_detach(duk_heap *heap);

DUK_INTERNAL_DECL duk_bool_t duk_debug_read_peek(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_debug_write_flush(duk_hthread *thr);

DUK_INTERNAL_DECL void duk_debug_skip_bytes(duk_hthread *thr, duk_size_t length);
DUK_INTERNAL_DECL void duk_debug_skip_byte(duk_hthread *thr);

DUK_INTERNAL_DECL void duk_debug_read_bytes(duk_hthread *thr, duk_uint8_t *data, duk_size_t length);
DUK_INTERNAL_DECL duk_uint8_t duk_debug_read_byte(duk_hthread *thr);
DUK_INTERNAL_DECL duk_int32_t duk_debug_read_int(duk_hthread *thr);
DUK_INTERNAL_DECL duk_hstring *duk_debug_read_hstring(duk_hthread *thr);
/* XXX: exposed duk_debug_read_pointer */
/* XXX: exposed duk_debug_read_buffer */
/* XXX: exposed duk_debug_read_hbuffer */
#if 0
DUK_INTERNAL_DECL duk_heaphdr *duk_debug_read_heapptr(duk_hthread *thr);
#endif
#if defined(DUK_USE_DEBUGGER_INSPECT)
DUK_INTERNAL_DECL duk_heaphdr *duk_debug_read_any_ptr(duk_hthread *thr);
#endif
DUK_INTERNAL_DECL duk_tval *duk_debug_read_tval(duk_hthread *thr);

DUK_INTERNAL_DECL void duk_debug_write_bytes(duk_hthread *thr, const duk_uint8_t *data, duk_size_t length);
DUK_INTERNAL_DECL void duk_debug_write_byte(duk_hthread *thr, duk_uint8_t x);
DUK_INTERNAL_DECL void duk_debug_write_unused(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_debug_write_undefined(duk_hthread *thr);
#if defined(DUK_USE_DEBUGGER_INSPECT)
DUK_INTERNAL_DECL void duk_debug_write_null(duk_hthread *thr);
#endif
DUK_INTERNAL_DECL void duk_debug_write_boolean(duk_hthread *thr, duk_uint_t val);
DUK_INTERNAL_DECL void duk_debug_write_int(duk_hthread *thr, duk_int32_t x);
DUK_INTERNAL_DECL void duk_debug_write_uint(duk_hthread *thr, duk_uint32_t x);
DUK_INTERNAL_DECL void duk_debug_write_string(duk_hthread *thr, const char *data, duk_size_t length);
DUK_INTERNAL_DECL void duk_debug_write_cstring(duk_hthread *thr, const char *data);
DUK_INTERNAL_DECL void duk_debug_write_hstring(duk_hthread *thr, duk_hstring *h);
DUK_INTERNAL_DECL void duk_debug_write_buffer(duk_hthread *thr, const char *data, duk_size_t length);
DUK_INTERNAL_DECL void duk_debug_write_hbuffer(duk_hthread *thr, duk_hbuffer *h);
DUK_INTERNAL_DECL void duk_debug_write_pointer(duk_hthread *thr, void *ptr);
#if defined(DUK_USE_DEBUGGER_DUMPHEAP) || defined(DUK_USE_DEBUGGER_INSPECT)
DUK_INTERNAL_DECL void duk_debug_write_heapptr(duk_hthread *thr, duk_heaphdr *h);
#endif
DUK_INTERNAL_DECL void duk_debug_write_hobject(duk_hthread *thr, duk_hobject *obj);
DUK_INTERNAL_DECL void duk_debug_write_tval(duk_hthread *thr, duk_tval *tv);
#if 0  /* unused */
DUK_INTERNAL_DECL void duk_debug_write_request(duk_hthread *thr, duk_small_uint_t command);
#endif
DUK_INTERNAL_DECL void duk_debug_write_reply(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_debug_write_error_eom(duk_hthread *thr, duk_small_uint_t err_code, const char *msg);
DUK_INTERNAL_DECL void duk_debug_write_notify(duk_hthread *thr, duk_small_uint_t command);
DUK_INTERNAL_DECL void duk_debug_write_eom(duk_hthread *thr);

DUK_INTERNAL_DECL duk_uint_fast32_t duk_debug_curr_line(duk_hthread *thr);
DUK_INTERNAL_DECL void duk_debug_send_status(duk_hthread *thr);
#if defined(DUK_USE_DEBUGGER_THROW_NOTIFY)
DUK_INTERNAL_DECL void duk_debug_send_throw(duk_hthread *thr, duk_bool_t fatal);
#endif

DUK_INTERNAL_DECL void duk_debug_halt_execution(duk_hthread *thr, duk_bool_t use_prev_pc);
DUK_INTERNAL_DECL duk_bool_t duk_debug_process_messages(duk_hthread *thr, duk_bool_t no_block);

DUK_INTERNAL_DECL duk_small_int_t duk_debug_add_breakpoint(duk_hthread *thr, duk_hstring *filename, duk_uint32_t line);
DUK_INTERNAL_DECL duk_bool_t duk_debug_remove_breakpoint(duk_hthread *thr, duk_small_uint_t breakpoint_index);

DUK_INTERNAL_DECL duk_bool_t duk_debug_is_attached(duk_heap *heap);
DUK_INTERNAL_DECL duk_bool_t duk_debug_is_paused(duk_heap *heap);
DUK_INTERNAL_DECL void duk_debug_set_paused(duk_heap *heap);
DUK_INTERNAL_DECL void duk_debug_clear_paused(duk_heap *heap);
DUK_INTERNAL_DECL void duk_debug_clear_pause_state(duk_heap *heap);
#endif  /* DUK_USE_DEBUGGER_SUPPORT */

#endif  /* DUK_DEBUGGER_H_INCLUDED */
/* #include duk_debug.h */
//#line 1 "duk_debug.h"
/*
 *  Debugging macros, DUK_DPRINT() and its variants in particular.
 *
 *  DUK_DPRINT() allows formatted debug prints, and supports standard
 *  and Duktape specific formatters.  See duk_debug_vsnprintf.c for details.
 *
 *  DUK_D(x), DUK_DD(x), and DUK_DDD(x) are used together with log macros
 *  for technical reasons.  They are concretely used to hide 'x' from the
 *  compiler when the corresponding log level is disabled.  This allows
 *  clean builds on non-C99 compilers, at the cost of more verbose code.
 *  Examples:
 *
 *    DUK_D(DUK_DPRINT("foo"));
 *    DUK_DD(DUK_DDPRINT("foo"));
 *    DUK_DDD(DUK_DDDPRINT("foo"));
 *
 *  This approach is preferable to the old "double parentheses" hack because
 *  double parentheses make the C99 solution worse: __FILE__ and __LINE__ can
 *  no longer be added transparently without going through globals, which
 *  works poorly with threading.
 */

#if !defined(DUK_DEBUG_H_INCLUDED)
#define DUK_DEBUG_H_INCLUDED

#if defined(DUK_USE_DEBUG)

#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 0)
#define DUK_D(x) x
#else
#define DUK_D(x) do { } while (0) /* omit */
#endif

#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 1)
#define DUK_DD(x) x
#else
#define DUK_DD(x) do { } while (0) /* omit */
#endif

#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)
#define DUK_DDD(x) x
#else
#define DUK_DDD(x) do { } while (0) /* omit */
#endif

/*
 *  Exposed debug macros: debugging enabled
 */

#if defined(DUK_USE_VARIADIC_MACROS)

/* Note: combining __FILE__, __LINE__, and __func__ into fmt would be
 * possible compile time, but waste some space with shared function names.
 */
#define DUK__DEBUG_LOG(lev,...)  duk_debug_log((duk_int_t) (lev), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, DUK_FUNC_MACRO, __VA_ARGS__);

#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 0)
#define DUK_DPRINT(...)          DUK__DEBUG_LOG(DUK_LEVEL_DEBUG, __VA_ARGS__)
#else
#define DUK_DPRINT(...)
#endif

#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 1)
#define DUK_DDPRINT(...)         DUK__DEBUG_LOG(DUK_LEVEL_DDEBUG, __VA_ARGS__)
#else
#define DUK_DDPRINT(...)
#endif

#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)
#define DUK_DDDPRINT(...)        DUK__DEBUG_LOG(DUK_LEVEL_DDDEBUG, __VA_ARGS__)
#else
#define DUK_DDDPRINT(...)
#endif

#else  /* DUK_USE_VARIADIC_MACROS */

#define DUK__DEBUG_STASH(lev)    \
    (void) DUK_SNPRINTF(duk_debug_file_stash, DUK_DEBUG_STASH_SIZE, "%s", (const char *) DUK_FILE_MACRO), \
    (void) (duk_debug_file_stash[DUK_DEBUG_STASH_SIZE - 1] = (char) 0), \
    (void) (duk_debug_line_stash = (duk_int_t) DUK_LINE_MACRO), \
    (void) DUK_SNPRINTF(duk_debug_func_stash, DUK_DEBUG_STASH_SIZE, "%s", (const char *) DUK_FUNC_MACRO), \
    (void) (duk_debug_func_stash[DUK_DEBUG_STASH_SIZE - 1] = (char) 0), \
    (void) (duk_debug_level_stash = (lev))

/* Without variadic macros resort to comma expression trickery to handle debug
 * prints.  This generates a lot of harmless warnings.  These hacks are not
 * needed normally because DUK_D() and friends will hide the entire debug log
 * statement from the compiler.
 */

#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 0)
#define DUK_DPRINT  DUK__DEBUG_STASH(DUK_LEVEL_DEBUG), (void) duk_debug_log  /* args go here in parens */
#else
#define DUK_DPRINT  0 && /* args go here as a comma expression in parens */
#endif

#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 1)
#define DUK_DDPRINT  DUK__DEBUG_STASH(DUK_LEVEL_DDEBUG), (void) duk_debug_log  /* args go here in parens */
#else
#define DUK_DDPRINT  0 && /* args */
#endif

#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)
#define DUK_DDDPRINT  DUK__DEBUG_STASH(DUK_LEVEL_DDDEBUG), (void) duk_debug_log  /* args go here in parens */
#else
#define DUK_DDDPRINT  0 && /* args */
#endif

#endif  /* DUK_USE_VARIADIC_MACROS */

#else  /* DUK_USE_DEBUG */

/*
 *  Exposed debug macros: debugging disabled
 */

#define DUK_D(x) do { } while (0) /* omit */
#define DUK_DD(x) do { } while (0) /* omit */
#define DUK_DDD(x) do { } while (0) /* omit */

#if defined(DUK_USE_VARIADIC_MACROS)

#define DUK_DPRINT(...)
#define DUK_DDPRINT(...)
#define DUK_DDDPRINT(...)

#else  /* DUK_USE_VARIADIC_MACROS */

#define DUK_DPRINT    0 && /* args go here as a comma expression in parens */
#define DUK_DDPRINT   0 && /* args */
#define DUK_DDDPRINT  0 && /* args */

#endif  /* DUK_USE_VARIADIC_MACROS */

#endif  /* DUK_USE_DEBUG */

/*
 *  Structs
 */

#if defined(DUK_USE_DEBUG)
struct duk_fixedbuffer {
    duk_uint8_t *buffer;
    duk_size_t length;
    duk_size_t offset;
    duk_bool_t truncated;
};
#endif

/*
 *  Prototypes
 */

#if defined(DUK_USE_DEBUG)
DUK_INTERNAL_DECL duk_int_t duk_debug_vsnprintf(char *str, duk_size_t size, const char *format, va_list ap);
#if 0  /*unused*/
DUK_INTERNAL_DECL duk_int_t duk_debug_snprintf(char *str, duk_size_t size, const char *format, ...);
#endif
DUK_INTERNAL_DECL void duk_debug_format_funcptr(char *buf, duk_size_t buf_size, duk_uint8_t *fptr, duk_size_t fptr_size);

#if defined(DUK_USE_VARIADIC_MACROS)
DUK_INTERNAL_DECL void duk_debug_log(duk_int_t level, const char *file, duk_int_t line, const char *func, const char *fmt, ...);
#else  /* DUK_USE_VARIADIC_MACROS */
/* parameter passing, not thread safe */
#define DUK_DEBUG_STASH_SIZE  128
#if !defined(DUK_SINGLE_FILE)
DUK_INTERNAL_DECL char duk_debug_file_stash[DUK_DEBUG_STASH_SIZE];
DUK_INTERNAL_DECL duk_int_t duk_debug_line_stash;
DUK_INTERNAL_DECL char duk_debug_func_stash[DUK_DEBUG_STASH_SIZE];
DUK_INTERNAL_DECL duk_int_t duk_debug_level_stash;
#endif
DUK_INTERNAL_DECL void duk_debug_log(const char *fmt, ...);
#endif  /* DUK_USE_VARIADIC_MACROS */

DUK_INTERNAL_DECL void duk_fb_put_bytes(duk_fixedbuffer *fb, const duk_uint8_t *buffer, duk_size_t length);
DUK_INTERNAL_DECL void duk_fb_put_byte(duk_fixedbuffer *fb, duk_uint8_t x);
DUK_INTERNAL_DECL void duk_fb_put_cstring(duk_fixedbuffer *fb, const char *x);
DUK_INTERNAL_DECL void duk_fb_sprintf(duk_fixedbuffer *fb, const char *fmt, ...);
DUK_INTERNAL_DECL void duk_fb_put_funcptr(duk_fixedbuffer *fb, duk_uint8_t *fptr, duk_size_t fptr_size);
DUK_INTERNAL_DECL duk_bool_t duk_fb_is_full(duk_fixedbuffer *fb);

#endif  /* DUK_USE_DEBUG */

#endif  /* DUK_DEBUG_H_INCLUDED */
/* #include duk_error.h */
//#line 1 "duk_error.h"
/*
 *  Error handling macros, assertion macro, error codes.
 *
 *  There are three types of 'errors':
 *
 *    1. Ordinary errors relative to a thread, cause a longjmp, catchable.
 *    2. Fatal errors relative to a heap, cause fatal handler to be called.
 *    3. Fatal errors without context, cause the default (not heap specific)
 *       fatal handler to be called.
 *
 *  Fatal errors without context are used by debug code such as assertions.
 *  By providing a fatal error handler for a Duktape heap, user code can
 *  avoid fatal errors without context in non-debug builds.
 */

#if !defined(DUK_ERROR_H_INCLUDED)
#define DUK_ERROR_H_INCLUDED

/*
 *  Error codes: defined in duktape.h
 *
 *  Error codes are used as a shorthand to throw exceptions from inside
 *  the implementation.  The appropriate ECMAScript object is constructed
 *  based on the code.  ECMAScript code throws objects directly.  The error
 *  codes are defined in the public API header because they are also used
 *  by calling code.
 */

/*
 *  Normal error
 *
 *  Normal error is thrown with a longjmp() through the current setjmp()
 *  catchpoint record in the duk_heap.  The 'curr_thread' of the duk_heap
 *  identifies the throwing thread.
 *
 *  Error formatting is usually unnecessary.  The error macros provide a
 *  zero argument version (no formatting) and separate macros for small
 *  argument counts.  Variadic macros are not used to avoid portability
 *  issues and avoid the need for stash-based workarounds when they're not
 *  available.  Vararg calls are avoided for non-formatted error calls
 *  because vararg call sites are larger than normal, and there are a lot
 *  of call sites with no formatting.
 *
 *  Note that special formatting provided by debug macros is NOT available.
 *
 *  The _RAW variants allow the caller to specify file and line.  This makes
 *  it easier to write checked calls which want to use the call site of the
 *  checked function, not the error macro call inside the checked function.
 */

#if defined(DUK_USE_VERBOSE_ERRORS)

/* Because there are quite many call sites, pack error code (require at most
 * 8-bit) into a single argument.
 */
#define DUK_ERROR(thr,err,msg) do { \
        duk_errcode_t duk__err = (err); duk_int_t duk__line = (duk_int_t) DUK_LINE_MACRO; \
        DUK_ASSERT(duk__err >= 0 && duk__err <= 0xff); DUK_ASSERT(duk__line >= 0 && duk__line <= 0x00ffffffL); \
        duk_err_handle_error((thr), DUK_FILE_MACRO, (((duk_uint_t) duk__err) << 24) | ((duk_uint_t) duk__line), (msg)); \
    } while (0)
#define DUK_ERROR_RAW(thr,file,line,err,msg) do { \
        duk_errcode_t duk__err = (err); duk_int_t duk__line = (duk_int_t) (line); \
        DUK_ASSERT(duk__err >= 0 && duk__err <= 0xff); DUK_ASSERT(duk__line >= 0 && duk__line <= 0x00ffffffL); \
        duk_err_handle_error((thr), (file), (((duk_uint_t) duk__err) << 24) | ((duk_uint_t) duk__line), (msg)); \
    } while (0)

#define DUK_ERROR_FMT1(thr,err,fmt,arg1) do { \
        duk_errcode_t duk__err = (err); duk_int_t duk__line = (duk_int_t) DUK_LINE_MACRO; \
        DUK_ASSERT(duk__err >= 0 && duk__err <= 0xff); DUK_ASSERT(duk__line >= 0 && duk__line <= 0x00ffffffL); \
        duk_err_handle_error_fmt((thr), DUK_FILE_MACRO, (((duk_uint_t) duk__err) << 24) | ((duk_uint_t) duk__line), (fmt), (arg1)); \
    } while (0)
#define DUK_ERROR_RAW_FMT1(thr,file,line,err,fmt,arg1) do { \
        duk_errcode_t duk__err = (err); duk_int_t duk__line = (duk_int_t) (line); \
        DUK_ASSERT(duk__err >= 0 && duk__err <= 0xff); DUK_ASSERT(duk__line >= 0 && duk__line <= 0x00ffffffL); \
        duk_err_handle_error_fmt((thr), (file), (((duk_uint_t) duk__err) << 24) | ((duk_uint_t) duk__line), (fmt), (arg1)); \
    } while (0)

#define DUK_ERROR_FMT2(thr,err,fmt,arg1,arg2) do { \
        duk_errcode_t duk__err = (err); duk_int_t duk__line = (duk_int_t) DUK_LINE_MACRO; \
        DUK_ASSERT(duk__err >= 0 && duk__err <= 0xff); DUK_ASSERT(duk__line >= 0 && duk__line <= 0x00ffffffL); \
        duk_err_handle_error_fmt((thr), DUK_FILE_MACRO, (((duk_uint_t) duk__err) << 24) | ((duk_uint_t) duk__line), (fmt), (arg1), (arg2)); \
    } while (0)
#define DUK_ERROR_RAW_FMT2(thr,file,line,err,fmt,arg1,arg2) do { \
        duk_errcode_t duk__err = (err); duk_int_t duk__line = (duk_int_t) (line); \
        DUK_ASSERT(duk__err >= 0 && duk__err <= 0xff); DUK_ASSERT(duk__line >= 0 && duk__line <= 0x00ffffffL); \
        duk_err_handle_error_fmt((thr), (file), (((duk_uint_t) duk__err) << 24) | ((duk_uint_t) duk__line), (fmt), (arg1), (arg2)); \
    } while (0)

#define DUK_ERROR_FMT3(thr,err,fmt,arg1,arg2,arg3) do { \
        duk_errcode_t duk__err = (err); duk_int_t duk__line = (duk_int_t) DUK_LINE_MACRO; \
        DUK_ASSERT(duk__err >= 0 && duk__err <= 0xff); DUK_ASSERT(duk__line >= 0 && duk__line <= 0x00ffffffL); \
        duk_err_handle_error_fmt((thr), DUK_FILE_MACRO, (((duk_uint_t) duk__err) << 24) | ((duk_uint_t) duk__line), (fmt), (arg1), (arg2), (arg3)); \
    } while (0)
#define DUK_ERROR_RAW_FMT3(thr,file,line,err,fmt,arg1,arg2,arg3) do { \
        duk_errcode_t duk__err = (err); duk_int_t duk__line = (duk_int_t) (line); \
        DUK_ASSERT(duk__err >= 0 && duk__err <= 0xff); DUK_ASSERT(duk__line >= 0 && duk__line <= 0x00ffffffL); \
        duk_err_handle_error_fmt((thr), (file), (((duk_uint_t) duk__err) << 24) | ((duk_uint_t) duk__line), (fmt), (arg1), (arg2), (arg3)); \
    } while (0)

#define DUK_ERROR_FMT4(thr,err,fmt,arg1,arg2,arg3,arg4) do { \
        duk_errcode_t duk__err = (err); duk_int_t duk__line = (duk_int_t) DUK_LINE_MACRO; \
        DUK_ASSERT(duk__err >= 0 && duk__err <= 0xff); DUK_ASSERT(duk__line >= 0 && duk__line <= 0x00ffffffL); \
        duk_err_handle_error_fmt((thr), DUK_FILE_MACRO, (((duk_uint_t) duk__err) << 24) | ((duk_uint_t) duk__line), (fmt), (arg1), (arg2), (arg3), (arg4)); \
    } while (0)
#define DUK_ERROR_RAW_FMT4(thr,file,line,err,fmt,arg1,arg2,arg3,arg4) do { \
        duk_errcode_t duk__err = (err); duk_int_t duk__line = (duk_int_t) (line); \
        DUK_ASSERT(duk__err >= 0 && duk__err <= 0xff); DUK_ASSERT(duk__line >= 0 && duk__line <= 0x00ffffffL); \
        duk_err_handle_error_fmt((thr), (file), (((duk_uint_t) duk__err) << 24) | ((duk_uint_t) duk__line), (fmt), (arg1), (arg2), (arg3), (arg4)); \
    } while (0)

#else  /* DUK_USE_VERBOSE_ERRORS */

#define DUK_ERROR(thr,err,msg)                    duk_err_handle_error((thr), (err))
#define DUK_ERROR_RAW(thr,file,line,err,msg)      duk_err_handle_error((thr), (err))

#define DUK_ERROR_FMT1(thr,err,fmt,arg1) DUK_ERROR((thr),(err),(fmt))
#define DUK_ERROR_RAW_FMT1(thr,file,line,err,fmt,arg1) DUK_ERROR_RAW((thr),(file),(line),(err),(fmt))

#define DUK_ERROR_FMT2(thr,err,fmt,arg1,arg2) DUK_ERROR((thr),(err),(fmt))
#define DUK_ERROR_RAW_FMT2(thr,file,line,err,fmt,arg1,arg2) DUK_ERROR_RAW((thr),(file),(line),(err),(fmt))

#define DUK_ERROR_FMT3(thr,err,fmt,arg1,arg2,arg3) DUK_ERROR((thr),(err),(fmt))
#define DUK_ERROR_RAW_FMT3(thr,file,line,err,fmt,arg1,arg2,arg3) DUK_ERROR_RAW((thr),(file),(line),(err),(fmt))

#define DUK_ERROR_FMT4(thr,err,fmt,arg1,arg2,arg3,arg4) DUK_ERROR((thr),(err),(fmt))
#define DUK_ERROR_RAW_FMT4(thr,file,line,err,fmt,arg1,arg2,arg3,arg4) DUK_ERROR_RAW((thr),(file),(line),(err),(fmt))

#endif  /* DUK_USE_VERBOSE_ERRORS */

/*
 *  Fatal error without context
 *
 *  The macro is an expression to make it compatible with DUK_ASSERT_EXPR().
 */

#define DUK_FATAL_WITHOUT_CONTEXT(msg) \
    duk_default_fatal_handler(NULL, (msg))

/*
 *  Error throwing helpers
 *
 *  The goal is to provide verbose and configurable error messages.  Call
 *  sites should be clean in source code and compile to a small footprint.
 *  Small footprint is also useful for performance because small cold paths
 *  reduce code cache pressure.  Adding macros here only makes sense if there
 *  are enough call sites to get concrete benefits.
 *
 *  DUK_ERROR_xxx() macros are generic and can be used anywhere.
 *
 *  DUK_DCERROR_xxx() macros can only be used in Duktape/C functions where
 *  the "return DUK_RET_xxx;" shorthand is available for low memory targets.
 *  The DUK_DCERROR_xxx() macros always either throw or perform a
 *  'return DUK_RET_xxx' from the calling function.
 */

#if defined(DUK_USE_VERBOSE_ERRORS)
/* Verbose errors with key/value summaries (non-paranoid) or without key/value
 * summaries (paranoid, for some security sensitive environments), the paranoid
 * vs. non-paranoid distinction affects only a few specific errors.
 */
#if defined(DUK_USE_PARANOID_ERRORS)
#define DUK_ERROR_REQUIRE_TYPE_INDEX(thr,idx,expectname,lowmemstr) do { \
        duk_err_require_type_index((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, (idx), (expectname)); \
    } while (0)
#else  /* DUK_USE_PARANOID_ERRORS */
#define DUK_ERROR_REQUIRE_TYPE_INDEX(thr,idx,expectname,lowmemstr) do { \
        duk_err_require_type_index((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, (idx), (expectname)); \
    } while (0)
#endif  /* DUK_USE_PARANOID_ERRORS */

#define DUK_ERROR_INTERNAL(thr) do { \
        duk_err_error_internal((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \
    } while (0)
#define DUK_DCERROR_INTERNAL(thr) do { \
        DUK_ERROR_INTERNAL((thr)); \
        return 0; \
    } while (0)
#define DUK_ERROR_ALLOC_FAILED(thr) do { \
        duk_err_error_alloc_failed((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \
    } while (0)
#define DUK_ERROR_UNSUPPORTED(thr) do { \
        DUK_ERROR((thr), DUK_ERR_ERROR, DUK_STR_UNSUPPORTED); \
    } while (0)
#define DUK_DCERROR_UNSUPPORTED(thr) do { \
        DUK_ERROR_UNSUPPORTED((thr)); \
        return 0; \
    } while (0)
#define DUK_ERROR_ERROR(thr,msg) do { \
        duk_err_error((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, (msg)); \
    } while (0)
#define DUK_ERROR_RANGE_INDEX(thr,idx) do { \
        duk_err_range_index((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, (idx)); \
    } while (0)
#define DUK_ERROR_RANGE_PUSH_BEYOND(thr) do { \
        duk_err_range_push_beyond((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \
    } while (0)
#define DUK_ERROR_RANGE_INVALID_ARGS(thr) do { \
        DUK_ERROR_RANGE((thr), DUK_STR_INVALID_ARGS); \
    } while (0)
#define DUK_DCERROR_RANGE_INVALID_ARGS(thr) do { \
        DUK_ERROR_RANGE_INVALID_ARGS((thr)); \
        return 0; \
    } while (0)
#define DUK_ERROR_RANGE_INVALID_COUNT(thr) do { \
        DUK_ERROR_RANGE((thr), DUK_STR_INVALID_COUNT); \
    } while (0)
#define DUK_DCERROR_RANGE_INVALID_COUNT(thr) do { \
        DUK_ERROR_RANGE_INVALID_COUNT((thr)); \
        return 0; \
    } while (0)
#define DUK_ERROR_RANGE_INVALID_LENGTH(thr) do { \
        DUK_ERROR_RANGE((thr), DUK_STR_INVALID_LENGTH); \
    } while (0)
#define DUK_DCERROR_RANGE_INVALID_LENGTH(thr) do { \
        DUK_ERROR_RANGE_INVALID_LENGTH((thr)); \
        return 0; \
    } while (0)
#define DUK_ERROR_RANGE(thr,msg) do { \
        duk_err_range((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, (msg)); \
    } while (0)
#define DUK_ERROR_EVAL(thr,msg) do { \
        DUK_ERROR((thr), DUK_ERR_EVAL_ERROR, (msg)); \
    } while (0)
#define DUK_ERROR_REFERENCE(thr,msg) do { \
        DUK_ERROR((thr), DUK_ERR_REFERENCE_ERROR, (msg)); \
    } while (0)
#define DUK_ERROR_SYNTAX(thr,msg) do { \
        DUK_ERROR((thr), DUK_ERR_SYNTAX_ERROR, (msg)); \
    } while (0)
#define DUK_ERROR_TYPE_INVALID_ARGS(thr) do { \
        duk_err_type_invalid_args((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \
    } while (0)
#define DUK_DCERROR_TYPE_INVALID_ARGS(thr) do { \
        DUK_ERROR_TYPE_INVALID_ARGS((thr)); \
        return 0; \
    } while (0)
#define DUK_ERROR_TYPE_INVALID_STATE(thr) do { \
        duk_err_type_invalid_state((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \
    } while (0)
#define DUK_DCERROR_TYPE_INVALID_STATE(thr) do { \
        DUK_ERROR_TYPE_INVALID_STATE((thr)); \
        return 0; \
    } while (0)
#define DUK_ERROR_TYPE_INVALID_TRAP_RESULT(thr) do { \
        duk_err_type_invalid_trap_result((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \
    } while (0)
#define DUK_DCERROR_TYPE_INVALID_TRAP_RESULT(thr) do { \
        DUK_ERROR_TYPE((thr), DUK_STR_INVALID_TRAP_RESULT); \
    } while (0)
#define DUK_ERROR_TYPE(thr,msg) do { \
        DUK_ERROR((thr), DUK_ERR_TYPE_ERROR, (msg)); \
    } while (0)
#define DUK_ERROR_URI(thr,msg) do { \
        DUK_ERROR((thr), DUK_ERR_URI_ERROR, (msg)); \
    } while (0)
#else  /* DUK_USE_VERBOSE_ERRORS */
/* Non-verbose errors for low memory targets: no file, line, or message. */

#define DUK_ERROR_REQUIRE_TYPE_INDEX(thr,idx,expectname,lowmemstr) do { \
        duk_err_type((thr)); \
    } while (0)

#define DUK_ERROR_INTERNAL(thr) do { \
        duk_err_error((thr)); \
    } while (0)
#define DUK_DCERROR_INTERNAL(thr) do { \
        DUK_UNREF((thr)); \
        return DUK_RET_ERROR; \
    } while (0)
#define DUK_ERROR_ALLOC_FAILED(thr) do { \
        duk_err_error((thr)); \
    } while (0)
#define DUK_ERROR_UNSUPPORTED(thr) do { \
        duk_err_error((thr)); \
    } while (0)
#define DUK_DCERROR_UNSUPPORTED(thr) do { \
        DUK_UNREF((thr)); \
        return DUK_RET_ERROR; \
    } while (0)
#define DUK_ERROR_ERROR(thr,msg) do { \
        duk_err_error((thr)); \
    } while (0)
#define DUK_ERROR_RANGE_INDEX(thr,idx) do { \
        duk_err_range((thr)); \
    } while (0)
#define DUK_ERROR_RANGE_PUSH_BEYOND(thr) do { \
        duk_err_range((thr)); \
    } while (0)
#define DUK_ERROR_RANGE_INVALID_ARGS(thr) do { \
        duk_err_range((thr)); \
    } while (0)
#define DUK_DCERROR_RANGE_INVALID_ARGS(thr) do { \
        DUK_UNREF((thr)); \
        return DUK_RET_RANGE_ERROR; \
    } while (0)
#define DUK_ERROR_RANGE_INVALID_COUNT(thr) do { \
        duk_err_range((thr)); \
    } while (0)
#define DUK_DCERROR_RANGE_INVALID_COUNT(thr) do { \
        DUK_UNREF((thr)); \
        return DUK_RET_RANGE_ERROR; \
    } while (0)
#define DUK_ERROR_RANGE_INVALID_LENGTH(thr) do { \
        duk_err_range((thr)); \
    } while (0)
#define DUK_DCERROR_RANGE_INVALID_LENGTH(thr) do { \
        DUK_UNREF((thr)); \
        return DUK_RET_RANGE_ERROR; \
    } while (0)
#define DUK_ERROR_RANGE(thr,msg) do { \
        duk_err_range((thr)); \
    } while (0)
#define DUK_ERROR_EVAL(thr,msg) do { \
        duk_err_eval((thr)); \
    } while (0)
#define DUK_ERROR_REFERENCE(thr,msg) do { \
        duk_err_reference((thr)); \
    } while (0)
#define DUK_ERROR_SYNTAX(thr,msg) do { \
        duk_err_syntax((thr)); \
    } while (0)
#define DUK_ERROR_TYPE_INVALID_ARGS(thr) do { \
        duk_err_type((thr)); \
    } while (0)
#define DUK_DCERROR_TYPE_INVALID_ARGS(thr) do { \
        DUK_UNREF((thr)); \
        return DUK_RET_TYPE_ERROR; \
    } while (0)
#define DUK_ERROR_TYPE_INVALID_STATE(thr) do { \
        duk_err_type((thr)); \
    } while (0)
#define DUK_DCERROR_TYPE_INVALID_STATE(thr) do { \
        duk_err_type((thr)); \
    } while (0)
#define DUK_ERROR_TYPE_INVALID_TRAP_RESULT(thr) do { \
        duk_err_type((thr)); \
    } while (0)
#define DUK_DCERROR_TYPE_INVALID_TRAP_RESULT(thr) do { \
        DUK_UNREF((thr)); \
        return DUK_RET_TYPE_ERROR; \
    } while (0)
#define DUK_ERROR_TYPE_INVALID_TRAP_RESULT(thr) do { \
        duk_err_type((thr)); \
    } while (0)
#define DUK_ERROR_TYPE(thr,msg) do { \
        duk_err_type((thr)); \
    } while (0)
#define DUK_ERROR_URI(thr,msg) do { \
        duk_err_uri((thr)); \
    } while (0)
#endif  /* DUK_USE_VERBOSE_ERRORS */

/*
 *  Assert macro: failure causes a fatal error.
 *
 *  NOTE: since the assert macro doesn't take a heap/context argument, there's
 *  no way to look up a heap/context specific fatal error handler which may have
 *  been given by the application.  Instead, assertion failures always use the
 *  internal default fatal error handler; it can be replaced via duk_config.h
 *  and then applies to all Duktape heaps.
 */

#if defined(DUK_USE_ASSERTIONS)

/* The message should be a compile time constant without formatting (less risk);
 * we don't care about assertion text size because they're not used in production
 * builds.
 */
#define DUK_ASSERT(x)  do { \
    if (!(x)) { \
        DUK_FATAL_WITHOUT_CONTEXT("assertion failed: " #x \
            " (" DUK_FILE_MACRO ":" DUK_MACRO_STRINGIFY(DUK_LINE_MACRO) ")"); \
    } \
    } while (0)

/* Assertion compatible inside a comma expression, evaluates to void. */
#define DUK_ASSERT_EXPR(x) \
    ((void) ((x) ? 0 : (DUK_FATAL_WITHOUT_CONTEXT("assertion failed: " #x \
                " (" DUK_FILE_MACRO ":" DUK_MACRO_STRINGIFY(DUK_LINE_MACRO) ")"), 0)))

#else  /* DUK_USE_ASSERTIONS */

#define DUK_ASSERT(x)  do { /* assertion omitted */ } while (0)

#define DUK_ASSERT_EXPR(x)  ((void) 0)

#endif  /* DUK_USE_ASSERTIONS */

/* this variant is used when an assert would generate a compile warning by
 * being always true (e.g. >= 0 comparison for an unsigned value
 */
#define DUK_ASSERT_DISABLE(x)  do { /* assertion disabled */ } while (0)

/*
 *  Assertion helpers
 */

#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING)
#define DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(h)  do { \
        DUK_ASSERT((h) == NULL || DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) (h)) > 0); \
    } while (0)
#define DUK_ASSERT_REFCOUNT_NONZERO_TVAL(tv)  do { \
        if ((tv) != NULL && DUK_TVAL_IS_HEAP_ALLOCATED((tv))) { \
            DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(DUK_TVAL_GET_HEAPHDR((tv))) > 0); \
        } \
    } while (0)
#else
#define DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(h)  /* no refcount check */
#define DUK_ASSERT_REFCOUNT_NONZERO_TVAL(tv)    /* no refcount check */
#endif

#define DUK_ASSERT_TOP(ctx,n)  DUK_ASSERT((duk_idx_t) duk_get_top((ctx)) == (duk_idx_t) (n))

#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_PACKED_TVAL)
#define DUK_ASSERT_DOUBLE_IS_NORMALIZED(dval)  do { \
        duk_double_union duk__assert_tmp_du; \
        duk__assert_tmp_du.d = (dval); \
        DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&duk__assert_tmp_du)); \
    } while (0)
#else
#define DUK_ASSERT_DOUBLE_IS_NORMALIZED(dval)  /* nop */
#endif

#define DUK_ASSERT_VS_SPACE(thr) \
    DUK_ASSERT(thr->valstack_top < thr->valstack_end)

/*
 *  Helper to initialize a memory area (e.g. struct) with garbage when
 *  assertions enabled.
 */

#if defined(DUK_USE_ASSERTIONS)
#define DUK_ASSERT_SET_GARBAGE(ptr,size) do { \
        duk_memset_unsafe((void *) (ptr), 0x5a, size); \
    } while (0)
#else
#define DUK_ASSERT_SET_GARBAGE(ptr,size) do {} while (0)
#endif

/*
 *  Helper for valstack space
 *
 *  Caller of DUK_ASSERT_VALSTACK_SPACE() estimates the number of free stack entries
 *  required for its own use, and any child calls which are not (a) Duktape API calls
 *  or (b) Duktape calls which involve extending the valstack (e.g. getter call).
 */

#define DUK_VALSTACK_ASSERT_EXTRA  5  /* this is added to checks to allow for Duktape
                                       * API calls in addition to function's own use
                                       */
#if defined(DUK_USE_ASSERTIONS)
#define DUK_ASSERT_VALSTACK_SPACE(thr,n)   do { \
        DUK_ASSERT((thr) != NULL); \
        DUK_ASSERT((thr)->valstack_end - (thr)->valstack_top >= (n) + DUK_VALSTACK_ASSERT_EXTRA); \
    } while (0)
#else
#define DUK_ASSERT_VALSTACK_SPACE(thr,n)   /* no valstack space check */
#endif

/*
 *  Prototypes
 */

#if defined(DUK_USE_VERBOSE_ERRORS)
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_handle_error(duk_hthread *thr, const char *filename, duk_uint_t line_and_code, const char *msg));
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_handle_error_fmt(duk_hthread *thr, const char *filename, duk_uint_t line_and_code, const char *fmt, ...));
#else  /* DUK_USE_VERBOSE_ERRORS */
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_handle_error(duk_hthread *thr, duk_errcode_t code));
#endif  /* DUK_USE_VERBOSE_ERRORS */

#if defined(DUK_USE_VERBOSE_ERRORS)
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code, const char *msg, const char *filename, duk_int_t line));
#else
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code));
#endif

DUK_NORETURN(DUK_INTERNAL_DECL void duk_error_throw_from_negative_rc(duk_hthread *thr, duk_ret_t rc));

#define DUK_AUGMENT_FLAG_NOBLAME_FILELINE  (1U << 0)  /* if set, don't blame C file/line for .fileName and .lineNumber */
#define DUK_AUGMENT_FLAG_SKIP_ONE          (1U << 1)  /* if set, skip topmost activation in traceback construction */

#if defined(DUK_USE_AUGMENT_ERROR_CREATE)
DUK_INTERNAL_DECL void duk_err_augment_error_create(duk_hthread *thr, duk_hthread *thr_callstack, const char *filename, duk_int_t line, duk_small_uint_t flags);
#endif
#if defined(DUK_USE_AUGMENT_ERROR_THROW)
DUK_INTERNAL_DECL void duk_err_augment_error_throw(duk_hthread *thr);
#endif

#if defined(DUK_USE_VERBOSE_ERRORS)
#if defined(DUK_USE_PARANOID_ERRORS)
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_require_type_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t idx, const char *expect_name));
#else
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_require_type_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t idx, const char *expect_name));
#endif
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_error_internal(duk_hthread *thr, const char *filename, duk_int_t linenumber));
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_error_alloc_failed(duk_hthread *thr, const char *filename, duk_int_t linenumber));
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_error(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message));
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_range_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t idx));
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_range_push_beyond(duk_hthread *thr, const char *filename, duk_int_t linenumber));
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_range(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message));
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_type_invalid_args(duk_hthread *thr, const char *filename, duk_int_t linenumber));
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_type_invalid_state(duk_hthread *thr, const char *filename, duk_int_t linenumber));
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_type_invalid_trap_result(duk_hthread *thr, const char *filename, duk_int_t linenumber));
#else  /* DUK_VERBOSE_ERRORS */
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_error(duk_hthread *thr));
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_range(duk_hthread *thr));
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_eval(duk_hthread *thr));
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_reference(duk_hthread *thr));
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_syntax(duk_hthread *thr));
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_type(duk_hthread *thr));
DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_uri(duk_hthread *thr));
#endif /* DUK_VERBOSE_ERRORS */

DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_longjmp(duk_hthread *thr));

DUK_NORETURN(DUK_INTERNAL_DECL void duk_default_fatal_handler(void *udata, const char *msg));

DUK_INTERNAL_DECL void duk_err_setup_ljstate1(duk_hthread *thr, duk_small_uint_t lj_type, duk_tval *tv_val);
#if defined(DUK_USE_DEBUGGER_SUPPORT)
DUK_INTERNAL_DECL void duk_err_check_debugger_integration(duk_hthread *thr);
#endif

DUK_INTERNAL_DECL duk_hobject *duk_error_prototype_from_code(duk_hthread *thr, duk_errcode_t err_code);

#endif  /* DUK_ERROR_H_INCLUDED */
/* #include duk_unicode.h */
//#line 1 "duk_unicode.h"
/*
 *  Unicode helpers
 */

#if !defined(DUK_UNICODE_H_INCLUDED)
#define DUK_UNICODE_H_INCLUDED

/*
 *  UTF-8 / XUTF-8 / CESU-8 constants
 */

#define DUK_UNICODE_MAX_XUTF8_LENGTH      7   /* up to 36 bit codepoints */
#define DUK_UNICODE_MAX_XUTF8_BMP_LENGTH  3   /* all codepoints up to U+FFFF */
#define DUK_UNICODE_MAX_CESU8_LENGTH      6   /* all codepoints up to U+10FFFF */
#define DUK_UNICODE_MAX_CESU8_BMP_LENGTH  3   /* all codepoints up to U+FFFF */

/*
 *  Useful Unicode codepoints
 *
 *  Integer constants must be signed to avoid unexpected coercions
 *  in comparisons.
 */

#define DUK_UNICODE_CP_ZWNJ                   0x200cL  /* zero-width non-joiner */
#define DUK_UNICODE_CP_ZWJ                    0x200dL  /* zero-width joiner */
#define DUK_UNICODE_CP_REPLACEMENT_CHARACTER  0xfffdL  /* http://en.wikipedia.org/wiki/Replacement_character#Replacement_character */

/*
 *  ASCII character constants
 *
 *  C character literals like 'x' have a platform specific value and do
 *  not match ASCII (UTF-8) values on e.g. EBCDIC platforms.  So, use
 *  these (admittedly awkward) constants instead.  These constants must
 *  also have signed values to avoid unexpected coercions in comparisons.
 *
 *  http://en.wikipedia.org/wiki/ASCII
 */

#define DUK_ASC_NUL              0x00
#define DUK_ASC_SOH              0x01
#define DUK_ASC_STX              0x02
#define DUK_ASC_ETX              0x03
#define DUK_ASC_EOT              0x04
#define DUK_ASC_ENQ              0x05
#define DUK_ASC_ACK              0x06
#define DUK_ASC_BEL              0x07
#define DUK_ASC_BS               0x08
#define DUK_ASC_HT               0x09
#define DUK_ASC_LF               0x0a
#define DUK_ASC_VT               0x0b
#define DUK_ASC_FF               0x0c
#define DUK_ASC_CR               0x0d
#define DUK_ASC_SO               0x0e
#define DUK_ASC_SI               0x0f
#define DUK_ASC_DLE              0x10
#define DUK_ASC_DC1              0x11
#define DUK_ASC_DC2              0x12
#define DUK_ASC_DC3              0x13
#define DUK_ASC_DC4              0x14
#define DUK_ASC_NAK              0x15
#define DUK_ASC_SYN              0x16
#define DUK_ASC_ETB              0x17
#define DUK_ASC_CAN              0x18
#define DUK_ASC_EM               0x19
#define DUK_ASC_SUB              0x1a
#define DUK_ASC_ESC              0x1b
#define DUK_ASC_FS               0x1c
#define DUK_ASC_GS               0x1d
#define DUK_ASC_RS               0x1e
#define DUK_ASC_US               0x1f
#define DUK_ASC_SPACE            0x20
#define DUK_ASC_EXCLAMATION      0x21
#define DUK_ASC_DOUBLEQUOTE      0x22
#define DUK_ASC_HASH             0x23
#define DUK_ASC_DOLLAR           0x24
#define DUK_ASC_PERCENT          0x25
#define DUK_ASC_AMP              0x26
#define DUK_ASC_SINGLEQUOTE      0x27
#define DUK_ASC_LPAREN           0x28
#define DUK_ASC_RPAREN           0x29
#define DUK_ASC_STAR             0x2a
#define DUK_ASC_PLUS             0x2b
#define DUK_ASC_COMMA            0x2c
#define DUK_ASC_MINUS            0x2d
#define DUK_ASC_PERIOD           0x2e
#define DUK_ASC_SLASH            0x2f
#define DUK_ASC_0                0x30
#define DUK_ASC_1                0x31
#define DUK_ASC_2                0x32
#define DUK_ASC_3                0x33
#define DUK_ASC_4                0x34
#define DUK_ASC_5                0x35
#define DUK_ASC_6                0x36
#define DUK_ASC_7                0x37
#define DUK_ASC_8                0x38
#define DUK_ASC_9                0x39
#define DUK_ASC_COLON            0x3a
#define DUK_ASC_SEMICOLON        0x3b
#define DUK_ASC_LANGLE           0x3c
#define DUK_ASC_EQUALS           0x3d
#define DUK_ASC_RANGLE           0x3e
#define DUK_ASC_QUESTION         0x3f
#define DUK_ASC_ATSIGN           0x40
#define DUK_ASC_UC_A             0x41
#define DUK_ASC_UC_B             0x42
#define DUK_ASC_UC_C             0x43
#define DUK_ASC_UC_D             0x44
#define DUK_ASC_UC_E             0x45
#define DUK_ASC_UC_F             0x46
#define DUK_ASC_UC_G             0x47
#define DUK_ASC_UC_H             0x48
#define DUK_ASC_UC_I             0x49
#define DUK_ASC_UC_J             0x4a
#define DUK_ASC_UC_K             0x4b
#define DUK_ASC_UC_L             0x4c
#define DUK_ASC_UC_M             0x4d
#define DUK_ASC_UC_N             0x4e
#define DUK_ASC_UC_O             0x4f
#define DUK_ASC_UC_P             0x50
#define DUK_ASC_UC_Q             0x51
#define DUK_ASC_UC_R             0x52
#define DUK_ASC_UC_S             0x53
#define DUK_ASC_UC_T             0x54
#define DUK_ASC_UC_U             0x55
#define DUK_ASC_UC_V             0x56
#define DUK_ASC_UC_W             0x57
#define DUK_ASC_UC_X             0x58
#define DUK_ASC_UC_Y             0x59
#define DUK_ASC_UC_Z             0x5a
#define DUK_ASC_LBRACKET         0x5b
#define DUK_ASC_BACKSLASH        0x5c
#define DUK_ASC_RBRACKET         0x5d
#define DUK_ASC_CARET            0x5e
#define DUK_ASC_UNDERSCORE       0x5f
#define DUK_ASC_GRAVE            0x60
#define DUK_ASC_LC_A             0x61
#define DUK_ASC_LC_B             0x62
#define DUK_ASC_LC_C             0x63
#define DUK_ASC_LC_D             0x64
#define DUK_ASC_LC_E             0x65
#define DUK_ASC_LC_F             0x66
#define DUK_ASC_LC_G             0x67
#define DUK_ASC_LC_H             0x68
#define DUK_ASC_LC_I             0x69
#define DUK_ASC_LC_J             0x6a
#define DUK_ASC_LC_K             0x6b
#define DUK_ASC_LC_L             0x6c
#define DUK_ASC_LC_M             0x6d
#define DUK_ASC_LC_N             0x6e
#define DUK_ASC_LC_O             0x6f
#define DUK_ASC_LC_P             0x70
#define DUK_ASC_LC_Q             0x71
#define DUK_ASC_LC_R             0x72
#define DUK_ASC_LC_S             0x73
#define DUK_ASC_LC_T             0x74
#define DUK_ASC_LC_U             0x75
#define DUK_ASC_LC_V             0x76
#define DUK_ASC_LC_W             0x77
#define DUK_ASC_LC_X             0x78
#define DUK_ASC_LC_Y             0x79
#define DUK_ASC_LC_Z             0x7a
#define DUK_ASC_LCURLY           0x7b
#define DUK_ASC_PIPE             0x7c
#define DUK_ASC_RCURLY           0x7d
#define DUK_ASC_TILDE            0x7e
#define DUK_ASC_DEL              0x7f

/*
 *  Miscellaneous
 */

/* Uppercase A is 0x41, lowercase a is 0x61; OR 0x20 to convert uppercase
 * to lowercase.
 */
#define DUK_LOWERCASE_CHAR_ASCII(x)  ((x) | 0x20)

/*
 *  Unicode tables
 */

#if defined(DUK_USE_SOURCE_NONBMP)
/*
 *  Automatically generated by extract_chars.py, do not edit!
 */

extern const duk_uint8_t duk_unicode_ids_noa[1116];
#else
/*
 *  Automatically generated by extract_chars.py, do not edit!
 */

extern const duk_uint8_t duk_unicode_ids_noabmp[625];
#endif

#if defined(DUK_USE_SOURCE_NONBMP)
/*
 *  Automatically generated by extract_chars.py, do not edit!
 */

extern const duk_uint8_t duk_unicode_ids_m_let_noa[42];
#else
/*
 *  Automatically generated by extract_chars.py, do not edit!
 */

extern const duk_uint8_t duk_unicode_ids_m_let_noabmp[24];
#endif

#if defined(DUK_USE_SOURCE_NONBMP)
/*
 *  Automatically generated by extract_chars.py, do not edit!
 */

extern const duk_uint8_t duk_unicode_idp_m_ids_noa[576];
#else
/*
 *  Automatically generated by extract_chars.py, do not edit!
 */

extern const duk_uint8_t duk_unicode_idp_m_ids_noabmp[358];
#endif

/*
 *  Automatically generated by extract_caseconv.py, do not edit!
 */

extern const duk_uint8_t duk_unicode_caseconv_uc[1411];
extern const duk_uint8_t duk_unicode_caseconv_lc[706];

#if defined(DUK_USE_REGEXP_CANON_WORKAROUND)
/*
 *  Automatically generated by extract_caseconv.py, do not edit!
 */

extern const duk_uint16_t duk_unicode_re_canon_lookup[65536];
#endif

#if defined(DUK_USE_REGEXP_CANON_BITMAP)
/*
 *  Automatically generated by extract_caseconv.py, do not edit!
 */

#define DUK_CANON_BITMAP_BLKSIZE                                      32
#define DUK_CANON_BITMAP_BLKSHIFT                                     5
#define DUK_CANON_BITMAP_BLKMASK                                      31
extern const duk_uint8_t duk_unicode_re_canon_bitmap[256];
#endif

/*
 *  Extern
 */

/* duk_unicode_support.c */
#if !defined(DUK_SINGLE_FILE)
DUK_INTERNAL_DECL const duk_uint8_t duk_unicode_xutf8_markers[7];
DUK_INTERNAL_DECL const duk_uint16_t duk_unicode_re_ranges_digit[2];
DUK_INTERNAL_DECL const duk_uint16_t duk_unicode_re_ranges_white[22];
DUK_INTERNAL_DECL const duk_uint16_t duk_unicode_re_ranges_wordchar[8];
DUK_INTERNAL_DECL const duk_uint16_t duk_unicode_re_ranges_not_digit[4];
DUK_INTERNAL_DECL const duk_uint16_t duk_unicode_re_ranges_not_white[24];
DUK_INTERNAL_DECL const duk_uint16_t duk_unicode_re_ranges_not_wordchar[10];
DUK_INTERNAL_DECL const duk_int8_t duk_is_idchar_tab[128];
#endif  /* !DUK_SINGLE_FILE */

/*
 *  Prototypes
 */

DUK_INTERNAL_DECL duk_small_int_t duk_unicode_get_xutf8_length(duk_ucodepoint_t cp);
#if defined(DUK_USE_ASSERTIONS)
DUK_INTERNAL_DECL duk_small_int_t duk_unicode_get_cesu8_length(duk_ucodepoint_t cp);
#endif
DUK_INTERNAL_DECL duk_small_int_t duk_unicode_encode_xutf8(duk_ucodepoint_t cp, duk_uint8_t *out);
DUK_INTERNAL_DECL duk_small_int_t duk_unicode_encode_cesu8(duk_ucodepoint_t cp, duk_uint8_t *out);
DUK_INTERNAL_DECL duk_small_int_t duk_unicode_decode_xutf8(duk_hthread *thr, const duk_uint8_t **ptr, const duk_uint8_t *ptr_start, const duk_uint8_t *ptr_end, duk_ucodepoint_t *out_cp);
DUK_INTERNAL_DECL duk_ucodepoint_t duk_unicode_decode_xutf8_checked(duk_hthread *thr, const duk_uint8_t **ptr, const duk_uint8_t *ptr_start, const duk_uint8_t *ptr_end);
DUK_INTERNAL_DECL duk_size_t duk_unicode_unvalidated_utf8_length(const duk_uint8_t *data, duk_size_t blen);
DUK_INTERNAL_DECL duk_bool_t duk_unicode_is_utf8_compatible(const duk_uint8_t *buf, duk_size_t len);
DUK_INTERNAL_DECL duk_small_int_t duk_unicode_is_whitespace(duk_codepoint_t cp);
DUK_INTERNAL_DECL duk_small_int_t duk_unicode_is_line_terminator(duk_codepoint_t cp);
DUK_INTERNAL_DECL duk_small_int_t duk_unicode_is_identifier_start(duk_codepoint_t cp);
DUK_INTERNAL_DECL duk_small_int_t duk_unicode_is_identifier_part(duk_codepoint_t cp);
DUK_INTERNAL_DECL duk_small_int_t duk_unicode_is_letter(duk_codepoint_t cp);
DUK_INTERNAL_DECL void duk_unicode_case_convert_string(duk_hthread *thr, duk_bool_t uppercase);
#if defined(DUK_USE_REGEXP_SUPPORT)
DUK_INTERNAL_DECL duk_codepoint_t duk_unicode_re_canonicalize_char(duk_hthread *thr, duk_codepoint_t cp);
DUK_INTERNAL_DECL duk_small_int_t duk_unicode_re_is_wordchar(duk_codepoint_t cp);
#endif

#endif  /* DUK_UNICODE_H_INCLUDED */
/* #include duk_json.h */
//#line 1 "duk_json.h"
/*
 *  Defines for JSON, especially duk_bi_json.c.
 */

#if !defined(DUK_JSON_H_INCLUDED)
#define DUK_JSON_H_INCLUDED

/* Encoding/decoding flags */
#define DUK_JSON_FLAG_ASCII_ONLY              (1U << 0)  /* escape any non-ASCII characters */
#define DUK_JSON_FLAG_AVOID_KEY_QUOTES        (1U << 1)  /* avoid key quotes when key is an ASCII Identifier */
#define DUK_JSON_FLAG_EXT_CUSTOM              (1U << 2)  /* extended types: custom encoding */
#define DUK_JSON_FLAG_EXT_COMPATIBLE          (1U << 3)  /* extended types: compatible encoding */

/* How much stack to require on entry to object/array encode */
#define DUK_JSON_ENC_REQSTACK                 32

/* How much stack to require on entry to object/array decode */
#define DUK_JSON_DEC_REQSTACK                 32

/* How large a loop detection stack to use */
#define DUK_JSON_ENC_LOOPARRAY                64

/* Encoding state.  Heap object references are all borrowed. */
typedef struct {
    duk_hthread *thr;
    duk_bufwriter_ctx bw;        /* output bufwriter */
    duk_hobject *h_replacer;     /* replacer function */
    duk_hstring *h_gap;          /* gap (if empty string, NULL) */
    duk_idx_t idx_proplist;      /* explicit PropertyList */
    duk_idx_t idx_loop;          /* valstack index of loop detection object */
    duk_small_uint_t flags;
    duk_small_uint_t flag_ascii_only;
    duk_small_uint_t flag_avoid_key_quotes;
#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
    duk_small_uint_t flag_ext_custom;
    duk_small_uint_t flag_ext_compatible;
    duk_small_uint_t flag_ext_custom_or_compatible;
#endif
    duk_uint_t recursion_depth;
    duk_uint_t recursion_limit;
    duk_uint_t mask_for_undefined;      /* type bit mask: types which certainly produce 'undefined' */
#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
    duk_small_uint_t stridx_custom_undefined;
    duk_small_uint_t stridx_custom_nan;
    duk_small_uint_t stridx_custom_neginf;
    duk_small_uint_t stridx_custom_posinf;
    duk_small_uint_t stridx_custom_function;
#endif
    duk_hobject *visiting[DUK_JSON_ENC_LOOPARRAY];  /* indexed by recursion_depth */
} duk_json_enc_ctx;

typedef struct {
    duk_hthread *thr;
    const duk_uint8_t *p;
    const duk_uint8_t *p_start;
    const duk_uint8_t *p_end;
    duk_idx_t idx_reviver;
    duk_small_uint_t flags;
#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
    duk_small_uint_t flag_ext_custom;
    duk_small_uint_t flag_ext_compatible;
    duk_small_uint_t flag_ext_custom_or_compatible;
#endif
    duk_int_t recursion_depth;
    duk_int_t recursion_limit;
} duk_json_dec_ctx;

#endif  /* DUK_JSON_H_INCLUDED */
/* #include duk_js.h */
//#line 1 "duk_js.h"
/*
 *  ECMAScript execution, support primitives.
 */

#if !defined(DUK_JS_H_INCLUDED)
#define DUK_JS_H_INCLUDED

/* Flags for call handling.  Lowest flags must match bytecode DUK_BC_CALL_FLAG_xxx 1:1. */
#define DUK_CALL_FLAG_TAILCALL                 (1U << 0)  /* setup for a tail call */
#define DUK_CALL_FLAG_CONSTRUCT                (1U << 1)  /* constructor call (i.e. called as 'new Foo()') */
#define DUK_CALL_FLAG_CALLED_AS_EVAL           (1U << 2)  /* call was made using the identifier 'eval' */
#define DUK_CALL_FLAG_ALLOW_ECMATOECMA         (1U << 3)  /* ecma-to-ecma call with executor reuse is possible */
#define DUK_CALL_FLAG_DIRECT_EVAL              (1U << 4)  /* call is a direct eval call */
#define DUK_CALL_FLAG_CONSTRUCT_PROXY          (1U << 5)  /* handled via 'construct' proxy trap, check return value invariant(s) */
#define DUK_CALL_FLAG_DEFAULT_INSTANCE_UPDATED (1U << 6)  /* prototype of 'default instance' updated, temporary flag in call handling */

/* Flags for duk_js_equals_helper(). */
#define DUK_EQUALS_FLAG_SAMEVALUE            (1U << 0)  /* use SameValue instead of non-strict equality */
#define DUK_EQUALS_FLAG_STRICT               (1U << 1)  /* use strict equality instead of non-strict equality */

/* Flags for duk_js_compare_helper(). */
#define DUK_COMPARE_FLAG_NEGATE              (1U << 0)  /* negate result */
#define DUK_COMPARE_FLAG_EVAL_LEFT_FIRST     (1U << 1)  /* eval left argument first */

/* conversions, coercions, comparison, etc */
DUK_INTERNAL_DECL duk_bool_t duk_js_toboolean(duk_tval *tv);
DUK_INTERNAL_DECL duk_double_t duk_js_tonumber(duk_hthread *thr, duk_tval *tv);
DUK_INTERNAL_DECL duk_double_t duk_js_tointeger_number(duk_double_t x);
DUK_INTERNAL_DECL duk_double_t duk_js_tointeger(duk_hthread *thr, duk_tval *tv);
DUK_INTERNAL_DECL duk_uint32_t duk_js_touint32(duk_hthread *thr, duk_tval *tv);
DUK_INTERNAL_DECL duk_int32_t duk_js_toint32(duk_hthread *thr, duk_tval *tv);
DUK_INTERNAL_DECL duk_uint16_t duk_js_touint16(duk_hthread *thr, duk_tval *tv);
DUK_INTERNAL_DECL duk_uarridx_t duk_js_to_arrayindex_string(const duk_uint8_t *str, duk_uint32_t blen);
#if !defined(DUK_USE_HSTRING_ARRIDX)
DUK_INTERNAL_DECL duk_uarridx_t duk_js_to_arrayindex_hstring_fast_known(duk_hstring *h);
DUK_INTERNAL_DECL duk_uarridx_t duk_js_to_arrayindex_hstring_fast(duk_hstring *h);
#endif
DUK_INTERNAL_DECL duk_bool_t duk_js_equals_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_uint_t flags);
DUK_INTERNAL_DECL duk_small_int_t duk_js_data_compare(const duk_uint8_t *buf1, const duk_uint8_t *buf2, duk_size_t len1, duk_size_t len2);
DUK_INTERNAL_DECL duk_small_int_t duk_js_string_compare(duk_hstring *h1, duk_hstring *h2);
#if 0  /* unused */
DUK_INTERNAL_DECL duk_small_int_t duk_js_buffer_compare(duk_heap *heap, duk_hbuffer *h1, duk_hbuffer *h2);
#endif
DUK_INTERNAL_DECL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_uint_t flags);
DUK_INTERNAL_DECL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y);
#if defined(DUK_USE_SYMBOL_BUILTIN)
DUK_INTERNAL_DECL duk_bool_t duk_js_instanceof_ordinary(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y);
#endif
DUK_INTERNAL_DECL duk_bool_t duk_js_in(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y);
DUK_INTERNAL_DECL duk_small_uint_t duk_js_typeof_stridx(duk_tval *tv_x);
DUK_INTERNAL_DECL duk_bool_t duk_js_isarray_hobject(duk_hobject *h);
DUK_INTERNAL_DECL duk_bool_t duk_js_isarray(duk_tval *tv);

/* arithmetic */
DUK_INTERNAL_DECL double duk_js_arith_pow(double x, double y);
DUK_INTERNAL_DECL double duk_js_arith_mod(double x, double y);

#define duk_js_equals(thr,tv_x,tv_y) \
    duk_js_equals_helper((thr), (tv_x), (tv_y), 0)
#define duk_js_strict_equals(tv_x,tv_y) \
    duk_js_equals_helper(NULL, (tv_x), (tv_y), DUK_EQUALS_FLAG_STRICT)
#define duk_js_samevalue(tv_x,tv_y) \
    duk_js_equals_helper(NULL, (tv_x), (tv_y), DUK_EQUALS_FLAG_SAMEVALUE)

/* E5 Sections 11.8.1, 11.8.5; x < y */
#define duk_js_lessthan(thr,tv_x,tv_y) \
    duk_js_compare_helper((thr), (tv_x), (tv_Y), DUK_COMPARE_FLAG_EVAL_LEFT_FIRST)

/* E5 Sections 11.8.2, 11.8.5; x > y  -->  y < x */
#define duk_js_greaterthan(thr,tv_x,tv_y) \
    duk_js_compare_helper((thr), (tv_y), (tv_x), 0)

/* E5 Sections 11.8.3, 11.8.5; x <= y  -->  not (x > y)  -->  not (y < x) */
#define duk_js_lessthanorequal(thr,tv_x,tv_y) \
    duk_js_compare_helper((thr), (tv_y), (tv_x), DUK_COMPARE_FLAG_NEGATE)

/* E5 Sections 11.8.4, 11.8.5; x >= y  -->  not (x < y) */
#define duk_js_greaterthanorequal(thr,tv_x,tv_y) \
    duk_js_compare_helper((thr), (tv_x), (tv_y), DUK_COMPARE_FLAG_EVAL_LEFT_FIRST | DUK_COMPARE_FLAG_NEGATE)

/* identifiers and environment handling */
#if 0  /*unused*/
DUK_INTERNAL duk_bool_t duk_js_hasvar_envrec(duk_hthread *thr, duk_hobject *env, duk_hstring *name);
#endif
DUK_INTERNAL_DECL duk_bool_t duk_js_getvar_envrec(duk_hthread *thr, duk_hobject *env, duk_hstring *name, duk_bool_t throw_flag);
DUK_INTERNAL_DECL duk_bool_t duk_js_getvar_activation(duk_hthread *thr, duk_activation *act, duk_hstring *name, duk_bool_t throw_flag);
DUK_INTERNAL_DECL void duk_js_putvar_envrec(duk_hthread *thr, duk_hobject *env, duk_hstring *name, duk_tval *val, duk_bool_t strict);
DUK_INTERNAL_DECL void duk_js_putvar_activation(duk_hthread *thr, duk_activation *act, duk_hstring *name, duk_tval *val, duk_bool_t strict);
#if 0  /*unused*/
DUK_INTERNAL_DECL duk_bool_t duk_js_delvar_envrec(duk_hthread *thr, duk_hobject *env, duk_hstring *name);
#endif
DUK_INTERNAL_DECL duk_bool_t duk_js_delvar_activation(duk_hthread *thr, duk_activation *act, duk_hstring *name);
DUK_INTERNAL_DECL duk_bool_t duk_js_declvar_activation(duk_hthread *thr, duk_activation *act, duk_hstring *name, duk_tval *val, duk_small_uint_t prop_flags, duk_bool_t is_func_decl);
DUK_INTERNAL_DECL void duk_js_init_activation_environment_records_delayed(duk_hthread *thr, duk_activation *act);
DUK_INTERNAL_DECL void duk_js_close_environment_record(duk_hthread *thr, duk_hobject *env);
DUK_INTERNAL_DECL duk_hobject *duk_create_activation_environment_record(duk_hthread *thr, duk_hobject *func, duk_size_t bottom_byteoff);
DUK_INTERNAL_DECL void duk_js_push_closure(duk_hthread *thr,
                                           duk_hcompfunc *fun_temp,
                                           duk_hobject *outer_var_env,
                                           duk_hobject *outer_lex_env,
                                           duk_bool_t add_auto_proto);

/* call handling */
DUK_INTERNAL_DECL void duk_native_stack_check(duk_hthread *thr);
DUK_INTERNAL_DECL duk_int_t duk_handle_call_unprotected(duk_hthread *thr, duk_idx_t idx_func, duk_small_uint_t call_flags);
DUK_INTERNAL_DECL duk_int_t duk_handle_call_unprotected_nargs(duk_hthread *thr, duk_idx_t nargs, duk_small_uint_t call_flags);
DUK_INTERNAL_DECL duk_int_t duk_handle_safe_call(duk_hthread *thr, duk_safe_call_function func, void *udata, duk_idx_t num_stack_args, duk_idx_t num_stack_res);
DUK_INTERNAL_DECL void duk_call_construct_postprocess(duk_hthread *thr, duk_small_uint_t proxy_invariant);
#if defined(DUK_USE_VERBOSE_ERRORS)
DUK_INTERNAL_DECL void duk_call_setup_propcall_error(duk_hthread *thr, duk_tval *tv_base, duk_tval *tv_key);
#endif

/* bytecode execution */
DUK_INTERNAL_DECL void duk_js_execute_bytecode(duk_hthread *exec_thr);

#endif  /* DUK_JS_H_INCLUDED */
/* #include duk_numconv.h */
//#line 1 "duk_numconv.h"
/*
 *  Number-to-string conversion.  The semantics of these is very tightly
 *  bound with the ECMAScript semantics required for call sites.
 */

#if !defined(DUK_NUMCONV_H_INCLUDED)
#define DUK_NUMCONV_H_INCLUDED

/* Output a specified number of digits instead of using the shortest
 * form.  Used for toPrecision() and toFixed().
 */
#define DUK_N2S_FLAG_FIXED_FORMAT         (1U << 0)

/* Force exponential format.  Used for toExponential(). */
#define DUK_N2S_FLAG_FORCE_EXP            (1U << 1)

/* If number would need zero padding (for whole number part), use
 * exponential format instead.  E.g. if input number is 12300, 3
 * digits are generated ("123"), output "1.23e+4" instead of "12300".
 * Used for toPrecision().
 */
#define DUK_N2S_FLAG_NO_ZERO_PAD          (1U << 2)

/* Digit count indicates number of fractions (i.e. an absolute
 * digit index instead of a relative one).  Used together with
 * DUK_N2S_FLAG_FIXED_FORMAT for toFixed().
 */
#define DUK_N2S_FLAG_FRACTION_DIGITS      (1U << 3)

/*
 *  String-to-number conversion
 */

/* Maximum exponent value when parsing numbers.  This is not strictly
 * compliant as there should be no upper limit, but as we parse the
 * exponent without a bigint, impose some limit.  The limit should be
 * small enough that multiplying it (or limit-1 to be precise) won't
 * overflow signed 32-bit integer range.  Exponent is only parsed with
 * radix 10, but with maximum radix (36) a safe limit is:
 * (10000000*36).toString(16) -> '15752a00'
 */
#define DUK_S2N_MAX_EXPONENT              10000000L

/* Trim white space (= allow leading and trailing whitespace) */
#define DUK_S2N_FLAG_TRIM_WHITE           (1U << 0)

/* Allow exponent */
#define DUK_S2N_FLAG_ALLOW_EXP            (1U << 1)

/* Allow trailing garbage (e.g. treat "123foo" as "123) */
#define DUK_S2N_FLAG_ALLOW_GARBAGE        (1U << 2)

/* Allow leading plus sign */
#define DUK_S2N_FLAG_ALLOW_PLUS           (1U << 3)

/* Allow leading minus sign */
#define DUK_S2N_FLAG_ALLOW_MINUS          (1U << 4)

/* Allow 'Infinity' */
#define DUK_S2N_FLAG_ALLOW_INF            (1U << 5)

/* Allow fraction part */
#define DUK_S2N_FLAG_ALLOW_FRAC           (1U << 6)

/* Allow naked fraction (e.g. ".123") */
#define DUK_S2N_FLAG_ALLOW_NAKED_FRAC     (1U << 7)

/* Allow empty fraction (e.g. "123.") */
#define DUK_S2N_FLAG_ALLOW_EMPTY_FRAC     (1U << 8)

/* Allow empty string to be interpreted as 0 */
#define DUK_S2N_FLAG_ALLOW_EMPTY_AS_ZERO  (1U << 9)

/* Allow leading zeroes (e.g. "0123" -> "123") */
#define DUK_S2N_FLAG_ALLOW_LEADING_ZERO   (1U << 10)

/* Allow automatic detection of hex base ("0x" or "0X" prefix),
 * overrides radix argument and forces integer mode.
 */
#define DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT   (1U << 11)

/* Allow automatic detection of legacy octal base ("0n"),
 * overrides radix argument and forces integer mode.
 */
#define DUK_S2N_FLAG_ALLOW_AUTO_LEGACY_OCT_INT   (1U << 12)

/* Allow automatic detection of ES2015 octal base ("0o123"),
 * overrides radix argument and forces integer mode.
 */
#define DUK_S2N_FLAG_ALLOW_AUTO_OCT_INT   (1U << 13)

/* Allow automatic detection of ES2015 binary base ("0b10001"),
 * overrides radix argument and forces integer mode.
 */
#define DUK_S2N_FLAG_ALLOW_AUTO_BIN_INT   (1U << 14)

/*
 *  Prototypes
 */

DUK_INTERNAL_DECL void duk_numconv_stringify(duk_hthread *thr, duk_small_int_t radix, duk_small_int_t digits, duk_small_uint_t flags);
DUK_INTERNAL_DECL void duk_numconv_parse(duk_hthread *thr, duk_small_int_t radix, duk_small_uint_t flags);

#endif  /* DUK_NUMCONV_H_INCLUDED */
/* #include duk_bi_protos.h */
//#line 1 "duk_bi_protos.h"
/*
 *  Prototypes for built-in functions not automatically covered by the
 *  header declarations emitted by genbuiltins.py.
 */

#if !defined(DUK_BUILTIN_PROTOS_H_INCLUDED)
#define DUK_BUILTIN_PROTOS_H_INCLUDED

/* Buffer size needed for ISO 8601 formatting.
 * Accurate value is 32 + 1 for NUL termination:
 *   >>> len('+123456-01-23T12:34:56.123+12:34')
 *   32
 * Include additional space to be safe.
 */
#define  DUK_BI_DATE_ISO8601_BUFSIZE  40

/* Helpers exposed for internal use */
DUK_INTERNAL_DECL 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_INTERNAL_DECL duk_double_t duk_bi_date_get_timeval_from_dparts(duk_double_t *dparts, duk_small_uint_t flags);
DUK_INTERNAL_DECL duk_bool_t duk_bi_date_is_leap_year(duk_int_t year);
DUK_INTERNAL_DECL duk_bool_t duk_bi_date_timeval_in_valid_range(duk_double_t x);
DUK_INTERNAL_DECL duk_bool_t duk_bi_date_year_in_valid_range(duk_double_t year);
DUK_INTERNAL_DECL duk_bool_t duk_bi_date_timeval_in_leeway_range(duk_double_t x);
/* Built-in providers */
#if defined(DUK_USE_DATE_NOW_GETTIMEOFDAY)
DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_now_gettimeofday(void);
#endif
#if defined(DUK_USE_DATE_NOW_TIME)
DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_now_time(void);
#endif
#if defined(DUK_USE_DATE_NOW_WINDOWS)
DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_now_windows(void);
#endif
#if defined(DUK_USE_DATE_NOW_WINDOWS_SUBMS)
DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_now_windows_subms(void);
#endif
#if defined(DUK_USE_DATE_TZO_GMTIME_R) || defined(DUK_USE_DATE_TZO_GMTIME_S) || defined(DUK_USE_DATE_TZO_GMTIME)
DUK_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_gmtime(duk_double_t d);
#endif
#if defined(DUK_USE_DATE_TZO_WINDOWS)
DUK_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_windows(duk_double_t d);
#endif
#if defined(DUK_USE_DATE_TZO_WINDOWS_NO_DST)
DUK_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_windows_no_dst(duk_double_t d);
#endif
#if defined(DUK_USE_DATE_PRS_STRPTIME)
DUK_INTERNAL_DECL duk_bool_t duk_bi_date_parse_string_strptime(duk_hthread *thr, const char *str);
#endif
#if defined(DUK_USE_DATE_PRS_GETDATE)
DUK_INTERNAL_DECL duk_bool_t duk_bi_date_parse_string_getdate(duk_hthread *thr, const char *str);
#endif
#if defined(DUK_USE_DATE_FMT_STRFTIME)
DUK_INTERNAL_DECL duk_bool_t duk_bi_date_format_parts_strftime(duk_hthread *thr, duk_int_t *parts, duk_int_t tzoffset, duk_small_uint_t flags);
#endif

#if defined(DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME)
DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_monotonic_time_clock_gettime(void);
#endif
#if defined(DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC)
DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_monotonic_time_windows_qpc(void);
#endif

DUK_INTERNAL_DECL
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_INTERNAL_DECL
void duk_bi_json_stringify_helper(duk_hthread *thr,
                                  duk_idx_t idx_value,
                                  duk_idx_t idx_replacer,
                                  duk_idx_t idx_space,
                                  duk_small_uint_t flags);

DUK_INTERNAL_DECL duk_ret_t duk_textdecoder_decode_utf8_nodejs(duk_hthread *thr);

#if defined(DUK_USE_ES6_PROXY)
DUK_INTERNAL_DECL void duk_proxy_ownkeys_postprocess(duk_hthread *thr, duk_hobject *h_proxy_target, duk_uint_t flags);
#endif

#endif  /* DUK_BUILTIN_PROTOS_H_INCLUDED */
/* #include duk_selftest.h */
//#line 1 "duk_selftest.h"
/*
 *  Selftest code
 */

#if !defined(DUK_SELFTEST_H_INCLUDED)
#define DUK_SELFTEST_H_INCLUDED

#if defined(DUK_USE_SELF_TESTS)
DUK_INTERNAL_DECL duk_uint_t duk_selftest_run_tests(duk_alloc_function alloc_func,
                                                    duk_realloc_function realloc_func,
                                                    duk_free_function free_func,
                                                    void *udata);
#endif

#endif  /* DUK_SELFTEST_H_INCLUDED */
//#line 76 "duk_internal.h"

#endif  /* DUK_INTERNAL_H_INCLUDED */
//#line 10 "duk_replacements.c"

#if defined(DUK_USE_COMPUTED_NAN)
DUK_INTERNAL double duk_computed_nan;
#endif

#if defined(DUK_USE_COMPUTED_INFINITY)
DUK_INTERNAL double duk_computed_infinity;
#endif

#if defined(DUK_USE_REPL_FPCLASSIFY)
DUK_INTERNAL int duk_repl_fpclassify(double x) {
    duk_double_union u;
    duk_uint_fast16_t expt;
    duk_small_int_t mzero;

    u.d = x;
    expt = (duk_uint_fast16_t) (u.us[DUK_DBL_IDX_US0] & 0x7ff0UL);
    if (expt > 0x0000UL && expt < 0x7ff0UL) {
        /* expt values [0x001,0x7fe] = normal */
        return DUK_FP_NORMAL;
    }

    mzero = (u.ui[DUK_DBL_IDX_UI1] == 0 && (u.ui[DUK_DBL_IDX_UI0] & 0x000fffffUL) == 0);
    if (expt == 0x0000UL) {
        /* expt 0x000 is zero/subnormal */
        if (mzero) {
            return DUK_FP_ZERO;
        } else {
            return DUK_FP_SUBNORMAL;
        }
    } else {
        /* expt 0xfff is infinite/nan */
        if (mzero) {
            return DUK_FP_INFINITE;
        } else {
            return DUK_FP_NAN;
        }
    }
}
#endif

#if defined(DUK_USE_REPL_SIGNBIT)
DUK_INTERNAL int duk_repl_signbit(double x) {
    duk_double_union u;
    u.d = x;
    return (int) (u.uc[DUK_DBL_IDX_UC0] & 0x80UL);
}
#endif

#if defined(DUK_USE_REPL_ISFINITE)
DUK_INTERNAL int duk_repl_isfinite(double x) {
    int c = DUK_FPCLASSIFY(x);
    if (c == DUK_FP_NAN || c == DUK_FP_INFINITE) {
        return 0;
    } else {
        return 1;
    }
}
#endif

#if defined(DUK_USE_REPL_ISNAN)
DUK_INTERNAL int duk_repl_isnan(double x) {
    int c = DUK_FPCLASSIFY(x);
    return (c == DUK_FP_NAN);
}
#endif

#if defined(DUK_USE_REPL_ISINF)
DUK_INTERNAL int duk_repl_isinf(double x) {
    int c = DUK_FPCLASSIFY(x);
    return (c == DUK_FP_INFINITE);
}
#endif

#if defined(DUK_USE_DEBUG)

#if !defined(DUK_USE_DEBUG_WRITE)
#error debugging enabled (DUK_USE_DEBUG) but DUK_USE_DEBUG_WRITE not defined
#endif

#define DUK__DEBUG_BUFSIZE  DUK_USE_DEBUG_BUFSIZE

#if defined(DUK_USE_VARIADIC_MACROS)

DUK_INTERNAL void duk_debug_log(duk_int_t level, const char *file, duk_int_t line, const char *func, const char *fmt, ...) {
    va_list ap;
    long arg_level;
    const char *arg_file;
    long arg_line;
    const char *arg_func;
    const char *arg_msg;
    char buf[DUK__DEBUG_BUFSIZE];

    va_start(ap, fmt);

    duk_memzero((void *) buf, (size_t) DUK__DEBUG_BUFSIZE);
    duk_debug_vsnprintf(buf, DUK__DEBUG_BUFSIZE - 1, fmt, ap);

    arg_level = (long) level;
    arg_file = (const char *) file;
    arg_line = (long) line;
    arg_func = (const char *) func;
    arg_msg = (const char *) buf;
    DUK_USE_DEBUG_WRITE(arg_level, arg_file, arg_line, arg_func, arg_msg);

    va_end(ap);
}

#else  /* DUK_USE_VARIADIC_MACROS */

DUK_INTERNAL char duk_debug_file_stash[DUK_DEBUG_STASH_SIZE];
DUK_INTERNAL duk_int_t duk_debug_line_stash;
DUK_INTERNAL char duk_debug_func_stash[DUK_DEBUG_STASH_SIZE];
DUK_INTERNAL duk_int_t duk_debug_level_stash;

DUK_INTERNAL void duk_debug_log(const char *fmt, ...) {
    va_list ap;
    long arg_level;
    const char *arg_file;
    long arg_line;
    const char *arg_func;
    const char *arg_msg;
    char buf[DUK__DEBUG_BUFSIZE];

    va_start(ap, fmt);

    duk_memzero((void *) buf, (size_t) DUK__DEBUG_BUFSIZE);
    duk_debug_vsnprintf(buf, DUK__DEBUG_BUFSIZE - 1, fmt, ap);

    arg_level = (long) duk_debug_level_stash;
    arg_file = (const char *) duk_debug_file_stash;
    arg_line = (long) duk_debug_line_stash;
    arg_func = (const char *) duk_debug_func_stash;
    arg_msg = (const char *) buf;
    DUK_USE_DEBUG_WRITE(arg_level, arg_file, arg_line, arg_func, arg_msg);

    va_end(ap);
}

#endif  /* DUK_USE_VARIADIC_MACROS */

#else  /* DUK_USE_DEBUG */

/*
 *  Debugging disabled
 */

#endif  /* DUK_USE_DEBUG */

/* automatic undefs */
#undef DUK__DEBUG_BUFSIZE
//#line 1 "duk_builtins.c"
/*
 *  Automatically generated by genbuiltins.py, do not edit!
 */

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

#if defined(DUK_USE_ASSERTIONS)
#define DUK__REFCINIT(refc) 0 /*h_assert_refcount*/, (refc) /*actual*/
#else
#define DUK__REFCINIT(refc) (refc) /*actual*/
#endif

#if defined(DUK_USE_ROM_STRINGS)
#error ROM support not enabled, rerun configure.py with --rom-support
#else  /* DUK_USE_ROM_STRINGS */
DUK_INTERNAL const duk_uint8_t duk_strings_data[972] = {
79,40,209,144,168,105,6,78,54,139,89,185,44,48,46,90,120,8,154,140,35,103,
35,113,193,73,5,52,112,180,104,166,135,52,188,4,98,12,27,146,156,80,211,31,
129,115,150,64,52,220,109,24,18,68,156,24,38,67,114,36,55,9,119,151,132,
140,93,18,113,128,153,201,212,201,205,2,248,8,196,24,224,104,82,146,40,224,
193,48,114,168,37,147,196,54,123,28,4,98,12,43,148,67,103,177,192,70,32,
196,121,68,54,123,28,18,192,199,144,124,4,98,12,43,136,108,244,117,184,8,
196,24,95,40,134,207,71,91,128,140,65,133,113,13,158,158,151,1,24,131,11,
229,16,217,233,233,112,17,136,48,206,21,110,4,244,244,184,8,196,24,103,10,
183,2,122,218,156,4,98,12,24,203,112,64,179,113,193,79,8,218,155,131,32,
184,70,212,220,13,10,82,68,252,123,144,217,146,38,228,207,18,0,100,37,64,
178,212,11,161,17,104,162,96,10,200,193,57,165,65,169,16,5,100,81,27,70,18,
32,10,200,68,185,13,116,221,197,184,64,89,57,41,197,13,49,234,5,208,156,
113,87,55,118,147,20,187,56,161,166,92,221,212,73,210,236,226,134,153,115,
119,76,201,203,179,138,26,99,73,212,136,136,164,25,174,137,56,32,72,137,
101,23,52,45,13,34,86,9,79,136,104,201,114,149,96,52,138,134,140,151,75,
226,233,186,120,121,22,39,54,83,141,5,55,68,236,36,164,3,16,225,115,150,64,
52,205,163,2,72,154,83,138,26,99,75,12,11,150,103,5,36,20,211,70,140,133,
67,72,49,241,160,227,81,196,52,168,106,39,132,252,183,136,105,80,212,79,2,
249,110,128,126,88,95,133,109,237,237,237,151,235,127,46,249,119,203,190,
186,206,33,181,2,208,61,190,12,19,34,65,19,81,132,108,228,97,1,107,33,12,
32,45,100,137,64,247,175,9,19,155,41,198,130,155,134,69,146,100,227,226,
231,146,51,192,204,73,140,224,145,221,102,241,68,196,169,248,30,75,12,11,
151,242,233,187,143,138,24,137,162,164,255,253,63,3,201,97,129,114,254,92,
112,75,136,108,166,6,136,159,255,167,224,121,44,48,46,95,203,166,238,74,
113,67,77,201,128,223,255,223,224,121,44,48,46,95,203,145,46,9,205,16,39,
201,62,36,0,192,21,147,255,238,145,39,199,197,211,116,240,242,113,197,78,
214,211,226,233,187,107,105,19,119,37,56,161,166,52,221,212,201,205,36,240,
242,16,96,152,12,26,20,164,137,150,70,154,103,28,137,50,202,96,18,132,241,
41,104,105,56,218,48,36,138,183,57,56,128,68,24,38,2,52,12,34,10,133,147,
141,3,8,119,185,13,153,34,125,206,76,17,49,38,93,206,52,151,154,119,56,28,
76,130,112,200,141,206,21,209,96,23,35,238,114,160,139,0,243,238,114,78,
164,68,68,110,113,226,210,90,26,66,110,113,128,121,247,57,80,68,141,170,
183,56,84,52,11,70,73,19,110,114,160,93,8,113,57,143,66,200,84,53,244,154,
73,24,240,81,32,38,68,18,49,228,207,23,88,100,109,70,114,92,193,4,137,173,
168,36,220,73,19,247,247,182,168,209,144,187,223,58,156,104,79,190,183,127,
123,105,160,110,247,206,167,26,19,239,173,223,222,218,67,75,189,243,169,
198,132,251,235,183,247,182,154,134,151,123,231,83,141,9,247,215,111,239,
109,22,141,22,247,206,167,26,19,239,172,223,218,45,26,47,157,78,52,39,223,
74,24,144,10,32,129,34,20,64,152,142,129,57,179,67,104,68,12,129,161,140,
72,156,100,40,40,185,152,100,89,38,65,13,196,34,228,67,149,13,2,215,129,
149,209,65,104,209,77,14,104,144,81,33,170,67,101,48,52,68,113,70,210,88,
209,36,233,22,154,86,68,196,114,76,232,145,102,120,186,195,156,112,105,225,
228,113,71,80,68,162,115,101,50,85,200,25,108,116,44,132,178,38,114,137,96,
148,136,70,209,134,37,222,232,204,228,188,200,209,200,200,99,221,25,150,84,
121,34,70,209,107,36,227,66,20,160,92,136,164,49,235,35,8,217,201,40,108,
201,18,128,68,26,201,51,188,2,80,12,67,190,40,168,38,68,190,46,153,5,50,12,
207,160,86,129,26,83,4,208,34,225,4,88,192,
};
#endif  /* DUK_USE_ROM_STRINGS */

#if defined(DUK_USE_ROM_OBJECTS)
#error ROM support not enabled, rerun configure.py with --rom-support
#else  /* DUK_USE_ROM_OBJECTS */
/* native functions: 185 */
DUK_INTERNAL const duk_c_function duk_bi_native_functions[185] = {
    NULL,
    duk_bi_array_constructor,
    duk_bi_array_constructor_is_array,
    duk_bi_array_prototype_concat,
    duk_bi_array_prototype_indexof_shared,
    duk_bi_array_prototype_iter_shared,
    duk_bi_array_prototype_join_shared,
    duk_bi_array_prototype_pop,
    duk_bi_array_prototype_push,
    duk_bi_array_prototype_reduce_shared,
    duk_bi_array_prototype_reverse,
    duk_bi_array_prototype_shift,
    duk_bi_array_prototype_slice,
    duk_bi_array_prototype_sort,
    duk_bi_array_prototype_splice,
    duk_bi_array_prototype_to_string,
    duk_bi_array_prototype_unshift,
    duk_bi_arraybuffer_constructor,
    duk_bi_arraybuffer_isview,
    duk_bi_boolean_constructor,
    duk_bi_boolean_prototype_tostring_shared,
    duk_bi_buffer_compare_shared,
    duk_bi_buffer_readfield,
    duk_bi_buffer_slice_shared,
    duk_bi_buffer_writefield,
    duk_bi_cbor_decode,
    duk_bi_cbor_encode,
    duk_bi_dataview_constructor,
    duk_bi_date_constructor,
    duk_bi_date_constructor_now,
    duk_bi_date_constructor_parse,
    duk_bi_date_constructor_utc,
    duk_bi_date_prototype_get_shared,
    duk_bi_date_prototype_get_timezone_offset,
    duk_bi_date_prototype_set_shared,
    duk_bi_date_prototype_set_time,
    duk_bi_date_prototype_to_json,
    duk_bi_date_prototype_toprimitive,
    duk_bi_date_prototype_tostring_shared,
    duk_bi_date_prototype_value_of,
    duk_bi_duktape_object_act,
    duk_bi_duktape_object_compact,
    duk_bi_duktape_object_dec,
    duk_bi_duktape_object_enc,
    duk_bi_duktape_object_fin,
    duk_bi_duktape_object_gc,
    duk_bi_duktape_object_info,
    duk_bi_error_constructor_shared,
    duk_bi_error_prototype_filename_getter,
    duk_bi_error_prototype_filename_setter,
    duk_bi_error_prototype_linenumber_getter,
    duk_bi_error_prototype_linenumber_setter,
    duk_bi_error_prototype_stack_getter,
    duk_bi_error_prototype_stack_setter,
    duk_bi_error_prototype_to_string,
    duk_bi_function_constructor,
    duk_bi_function_prototype,
    duk_bi_function_prototype_apply,
    duk_bi_function_prototype_bind,
    duk_bi_function_prototype_call,
    duk_bi_function_prototype_hasinstance,
    duk_bi_function_prototype_to_string,
    duk_bi_global_object_decode_uri,
    duk_bi_global_object_decode_uri_component,
    duk_bi_global_object_encode_uri,
    duk_bi_global_object_encode_uri_component,
    duk_bi_global_object_escape,
    duk_bi_global_object_eval,
    duk_bi_global_object_is_finite,
    duk_bi_global_object_is_nan,
    duk_bi_global_object_parse_float,
    duk_bi_global_object_parse_int,
    duk_bi_global_object_unescape,
    duk_bi_json_object_parse,
    duk_bi_json_object_stringify,
    duk_bi_math_object_clz32,
    duk_bi_math_object_hypot,
    duk_bi_math_object_imul,
    duk_bi_math_object_max,
    duk_bi_math_object_min,
    duk_bi_math_object_onearg_shared,
    duk_bi_math_object_random,
    duk_bi_math_object_sign,
    duk_bi_math_object_twoarg_shared,
    duk_bi_native_function_length,
    duk_bi_native_function_name,
    duk_bi_nodejs_buffer_byte_length,
    duk_bi_nodejs_buffer_concat,
    duk_bi_nodejs_buffer_constructor,
    duk_bi_nodejs_buffer_copy,
    duk_bi_nodejs_buffer_fill,
    duk_bi_nodejs_buffer_is_buffer,
    duk_bi_nodejs_buffer_is_encoding,
    duk_bi_nodejs_buffer_tojson,
    duk_bi_nodejs_buffer_tostring,
    duk_bi_nodejs_buffer_write,
    duk_bi_number_check_shared,
    duk_bi_number_constructor,
    duk_bi_number_prototype_to_exponential,
    duk_bi_number_prototype_to_fixed,
    duk_bi_number_prototype_to_locale_string,
    duk_bi_number_prototype_to_precision,
    duk_bi_number_prototype_to_string,
    duk_bi_number_prototype_value_of,
    duk_bi_object_constructor,
    duk_bi_object_constructor_assign,
    duk_bi_object_constructor_create,
    duk_bi_object_constructor_define_properties,
    duk_bi_object_constructor_define_property,
    duk_bi_object_constructor_get_own_property_descriptor,
    duk_bi_object_constructor_is,
    duk_bi_object_constructor_is_extensible,
    duk_bi_object_constructor_is_sealed_frozen_shared,
    duk_bi_object_constructor_keys_shared,
    duk_bi_object_constructor_prevent_extensions,
    duk_bi_object_constructor_seal_freeze_shared,
    duk_bi_object_getprototype_shared,
    duk_bi_object_prototype_defineaccessor,
    duk_bi_object_prototype_has_own_property,
    duk_bi_object_prototype_is_prototype_of,
    duk_bi_object_prototype_lookupaccessor,
    duk_bi_object_prototype_property_is_enumerable,
    duk_bi_object_prototype_to_locale_string,
    duk_bi_object_prototype_to_string,
    duk_bi_object_prototype_value_of,
    duk_bi_object_setprototype_shared,
    duk_bi_performance_now,
    duk_bi_pointer_constructor,
    duk_bi_pointer_prototype_tostring_shared,
    duk_bi_proxy_constructor,
    duk_bi_reflect_apply,
    duk_bi_reflect_construct,
    duk_bi_reflect_object_delete_property,
    duk_bi_reflect_object_get,
    duk_bi_reflect_object_has,
    duk_bi_reflect_object_set,
    duk_bi_regexp_constructor,
    duk_bi_regexp_prototype_exec,
    duk_bi_regexp_prototype_flags,
    duk_bi_regexp_prototype_shared_getter,
    duk_bi_regexp_prototype_test,
    duk_bi_regexp_prototype_tostring,
    duk_bi_string_constructor,
    duk_bi_string_constructor_from_char_code,
    duk_bi_string_constructor_from_code_point,
    duk_bi_string_prototype_caseconv_shared,
    duk_bi_string_prototype_char_at,
    duk_bi_string_prototype_char_code_at,
    duk_bi_string_prototype_concat,
    duk_bi_string_prototype_includes,
    duk_bi_string_prototype_indexof_shared,
    duk_bi_string_prototype_locale_compare,
    duk_bi_string_prototype_match,
    duk_bi_string_prototype_repeat,
    duk_bi_string_prototype_replace,
    duk_bi_string_prototype_search,
    duk_bi_string_prototype_slice,
    duk_bi_string_prototype_split,
    duk_bi_string_prototype_startswith_endswith,
    duk_bi_string_prototype_substr,
    duk_bi_string_prototype_substring,
    duk_bi_string_prototype_to_string,
    duk_bi_string_prototype_trim,
    duk_bi_symbol_constructor_shared,
    duk_bi_symbol_key_for,
    duk_bi_symbol_toprimitive,
    duk_bi_symbol_tostring_shared,
    duk_bi_textdecoder_constructor,
    duk_bi_textdecoder_prototype_decode,
    duk_bi_textdecoder_prototype_shared_getter,
    duk_bi_textencoder_constructor,
    duk_bi_textencoder_prototype_encode,
    duk_bi_textencoder_prototype_encoding_getter,
    duk_bi_thread_constructor,
    duk_bi_thread_current,
    duk_bi_thread_resume,
    duk_bi_thread_yield,
    duk_bi_type_error_thrower,
    duk_bi_typedarray_buffer_getter,
    duk_bi_typedarray_bytelength_getter,
    duk_bi_typedarray_byteoffset_getter,
    duk_bi_typedarray_constructor,
    duk_bi_typedarray_set,
    duk_bi_uint8array_allocplain,
    duk_bi_uint8array_plainof,
};
#if defined(DUK_USE_DOUBLE_LE)
DUK_INTERNAL const duk_uint8_t duk_builtins_data[4281] = {
144,148,105,226,32,68,52,228,254,12,104,202,37,132,52,167,194,138,105,245,
124,57,28,211,57,18,64,52,239,126,44,138,111,175,241,164,19,87,145,30,33,
167,22,145,159,8,211,139,9,225,42,5,240,145,139,163,163,8,211,139,10,228,
64,211,19,132,140,93,29,56,70,156,88,119,34,66,146,36,104,137,194,70,46,
142,172,35,78,44,47,146,195,102,11,240,145,139,163,175,8,211,139,9,228,240,
242,112,145,139,163,179,8,211,139,8,237,34,130,118,49,116,118,225,26,48,0,
1,98,29,201,158,46,183,39,135,147,132,140,93,16,132,76,66,33,8,66,16,132,
33,8,66,26,180,105,97,167,68,150,34,33,154,112,0,1,91,247,35,79,111,237,
198,174,232,47,31,23,95,17,13,31,249,96,211,49,50,53,214,77,141,24,0,0,181,
10,228,240,242,15,128,140,65,128,134,188,0,0,90,167,97,181,224,0,2,213,62,
53,224,0,2,213,66,237,120,0,0,181,81,204,107,192,0,5,170,150,67,94,0,0,45,
84,245,90,240,0,1,106,169,162,215,128,0,11,85,93,150,188,0,0,90,171,111,53,
109,22,162,26,48,0,1,84,23,201,146,243,225,26,39,12,145,136,104,192,0,5,61,
11,228,201,121,240,100,19,134,72,196,33,195,14,40,203,112,64,190,76,232,
145,153,136,0,0,0,0,0,0,31,15,249,152,0,0,0,0,0,0,30,15,249,120,144,13,96,
155,194,56,80,206,36,67,141,20,228,70,57,81,206,100,131,156,39,132,168,23,
194,70,46,137,208,21,200,129,166,39,9,24,186,39,72,119,34,66,146,36,104,
137,194,70,46,137,212,23,201,97,179,5,248,72,197,209,58,194,121,60,60,156,
36,98,232,157,129,29,164,80,78,198,46,137,218,146,121,25,71,146,9,209,5,
209,61,48,126,14,138,152,30,67,186,23,143,139,175,131,202,135,228,72,85,
144,83,60,179,30,94,209,233,102,30,98,105,230,103,30,114,121,231,104,30,
122,137,231,233,30,130,153,232,106,30,138,169,232,235,30,144,67,193,25,19,
136,108,207,30,41,224,140,137,194,173,192,153,228,5,242,100,188,248,70,137,
195,36,79,78,47,147,37,231,193,144,78,25,34,122,145,111,36,74,232,176,13,
17,61,234,226,93,207,148,160,84,75,141,7,27,161,32,33,18,225,80,212,76,154,
2,2,70,65,56,100,237,34,140,209,2,67,32,156,50,118,145,64,186,230,61,205,
35,103,155,32,36,141,19,134,78,210,40,206,16,36,70,137,195,39,105,20,11,
174,99,220,210,54,121,210,1,137,33,1,228,207,16,17,70,146,66,3,201,164,32,
0,65,112,152,56,196,159,31,23,77,211,195,201,199,23,160,72,214,246,81,6,12,
73,241,214,111,31,23,60,145,158,56,50,72,81,67,230,232,242,80,19,49,39,199,
89,188,124,92,242,70,120,227,64,194,75,154,72,12,9,73,6,111,21,120,12,40,
144,19,39,25,0,225,144,168,105,56,248,185,228,140,241,200,96,64,100,42,26,
78,62,46,121,35,52,18,92,116,1,36,64,47,158,64,49,98,66,100,156,242,65,23,
196,149,35,103,194,94,100,108,144,230,203,156,64,66,37,201,16,11,32,249,
132,4,34,92,44,93,146,55,152,72,24,137,112,151,153,27,36,5,100,229,144,8,
162,98,92,210,5,76,73,241,214,111,31,23,60,145,158,57,44,48,46,92,185,164,
160,72,151,41,0,50,107,179,244,59,36,93,127,92,6,19,172,3,11,216,0,56,224,
151,29,102,241,241,115,201,25,227,164,64,106,37,199,197,211,116,240,242,
113,197,233,144,40,248,185,228,140,241,196,75,132,109,24,72,128,43,39,84,
129,13,173,161,144,168,105,56,98,78,100,142,214,215,69,1,13,173,161,144,
168,105,57,34,78,100,142,214,215,69,16,67,107,105,110,114,168,254,24,147,
153,35,181,181,212,32,67,107,105,110,114,168,254,72,147,153,35,181,181,212,
36,65,130,3,144,8,26,252,200,13,30,85,16,16,64,90,242,231,192,64,161,163,
203,31,26,172,193,17,4,23,105,159,96,27,172,251,16,32,196,4,14,137,112,17,
136,48,164,28,134,80,215,202,1,132,130,8,12,39,52,64,155,31,24,56,36,1,189,
207,132,0,35,233,35,195,62,3,196,149,36,100,72,160,2,200,232,44,227,0,11,
37,160,68,142,128,36,157,25,200,32,26,79,90,4,73,43,192,122,54,71,65,103,
44,248,14,134,140,151,227,138,231,208,45,96,148,248,134,140,151,227,138,
231,240,1,255,254,10,74,146,56,128,104,4,147,152,72,6,144,28,174,143,8,1,
30,1,165,3,96,31,0,211,3,21,11,153,35,0,211,131,68,131,160,137,16,250,5,
196,131,160,137,200,160,199,156,67,248,0,255,255,65,140,10,48,177,115,56,
35,130,60,19,134,79,89,240,52,177,115,56,39,12,156,123,144,217,251,15,135,
34,167,30,20,170,154,255,232,12,47,244,0,97,28,17,224,39,238,32,40,71,4,
120,39,12,156,4,253,228,5,137,195,39,30,228,54,124,4,253,228,128,194,115,
68,9,252,15,128,232,104,201,126,56,191,35,64,90,193,41,241,13,25,47,199,23,
228,105,3,86,225,1,100,224,156,199,130,36,249,144,10,192,76,71,250,16,15,
18,61,96,17,62,200,3,72,128,136,143,247,32,22,75,64,137,248,64,22,79,90,39,
249,64,38,84,12,167,20,52,223,196,2,230,238,45,214,36,120,32,72,158,208,4,
102,238,45,194,2,201,197,186,196,143,4,9,19,218,0,92,221,202,61,228,143,4,
9,19,218,8,35,55,113,110,16,22,78,81,239,36,120,32,72,158,208,64,73,197,12,
255,0,13,18,60,128,159,212,128,169,76,17,156,185,100,76,255,163,64,65,26,
57,114,200,153,255,70,144,33,13,18,232,50,75,226,104,6,149,3,41,199,246,
130,12,128,28,142,156,120,203,175,158,8,194,207,1,6,81,20,79,88,11,237,84,
11,161,32,127,255,255,255,255,255,247,191,137,235,16,221,170,129,116,36,0,
16,0,0,0,0,0,0,12,196,0,0,0,0,0,0,15,135,242,61,123,164,137,162,164,218,67,
74,134,162,120,128,0,0,0,0,0,1,224,254,71,173,33,129,52,84,155,72,105,80,
212,79,16,0,0,0,0,0,0,60,63,195,244,143,146,22,230,192,0,0,0,0,0,0,176,60,
33,214,2,251,82,1,73,180,134,204,134,36,96,127,255,255,255,255,255,159,161,
144,235,16,221,169,0,164,218,67,102,67,18,48,63,255,255,255,255,255,207,
240,196,60,17,145,56,134,204,241,226,158,8,200,156,42,220,9,158,65,196,34,
92,42,26,137,147,120,64,74,37,196,54,100,49,35,188,36,5,68,184,208,113,187,
194,80,212,75,146,1,73,196,54,100,49,35,188,38,57,37,56,240,0,0,0,0,0,0,0,
0,32,235,248,68,48,156,2,24,94,24,0,243,119,10,139,144,123,242,3,102,238,
18,239,115,72,217,160,11,223,16,23,55,113,241,32,145,36,57,188,18,16,102,3,
5,120,35,34,89,32,15,180,152,173,127,0,218,235,88,0,228,180,227,200,0,0,0,
0,0,0,248,127,197,107,240,64,6,77,220,24,38,78,74,113,67,77,130,4,12,155,
185,52,48,156,148,226,134,155,4,10,194,96,129,132,166,238,45,194,2,201,193,
130,100,228,167,20,52,216,32,113,41,187,139,112,128,178,114,104,97,57,41,
197,13,54,8,32,48,216,32,130,195,224,130,19,97,124,134,23,6,0,57,137,62,77,
12,38,12,0,179,18,124,45,22,190,96,128,141,176,134,28,98,79,180,152,139,
218,45,124,193,1,27,97,16,32,196,159,24,230,204,246,194,40,89,137,62,210,
98,103,92,217,158,216,70,7,49,39,193,130,100,182,17,194,140,73,246,147,16,
250,9,146,216,72,6,49,39,193,131,22,194,72,73,137,62,210,98,31,65,139,97,
40,32,196,159,14,234,70,86,194,88,89,137,62,210,98,63,93,72,202,216,76,10,
49,39,198,33,180,153,37,108,38,134,152,147,237,38,38,117,13,164,201,43,97,
56,40,196,159,36,65,57,163,149,176,158,26,98,79,180,152,165,210,9,205,28,
173,133,0,243,18,124,98,22,180,72,130,115,71,43,97,68,72,196,159,105,49,51,
168,90,209,34,9,205,28,173,133,33,19,18,124,154,24,76,185,164,227,138,89,
18,119,0,7,145,39,201,161,132,188,64,124,137,62,49,11,90,36,65,57,163,149,
210,166,37,34,79,180,152,153,212,45,104,145,4,230,142,87,74,160,84,137,62,
72,130,115,71,43,171,234,134,200,147,237,38,41,116,130,115,71,43,171,235,5,
72,147,227,16,218,76,146,186,254,184,108,137,62,210,98,103,80,218,76,146,
186,254,192,68,137,62,29,212,140,174,207,178,23,34,79,180,152,143,215,82,
50,187,62,208,60,137,62,12,19,37,210,182,21,34,79,180,152,135,208,76,151,
74,224,68,137,62,49,205,153,238,175,186,23,34,79,180,152,153,215,54,103,
186,190,240,92,137,62,22,139,95,48,64,70,235,251,225,210,36,251,73,136,189,
162,215,204,16,17,186,255,2,14,98,79,152,32,35,108,48,64,242,36,249,130,2,
55,75,6,212,224,72,200,51,128,114,108,28,100,128,0,0,0,0,0,0,0,12,110,127,
48,98,115,249,201,117,243,249,195,21,159,206,38,47,63,156,86,8,75,144,94,
82,1,38,73,79,208,67,95,233,1,6,128,14,79,129,186,40,249,18,149,182,207,
144,200,155,188,248,204,105,184,207,142,199,137,175,201,0,159,72,10,5,21,
221,10,120,74,129,124,36,98,232,228,74,81,62,160,20,10,107,186,21,114,32,
105,137,194,70,46,142,68,165,19,235,1,64,170,187,161,119,34,66,146,36,104,
137,194,70,46,142,68,165,19,236,1,64,174,187,161,95,37,134,204,23,225,35,
23,71,34,82,137,246,128,160,89,93,208,167,147,195,201,194,70,46,142,68,165,
19,238,1,64,182,187,161,71,105,20,19,177,139,163,145,41,68,16,7,6,15,82,70,
72,115,96,0,0,0,0,0,15,106,32,91,60,165,195,201,194,8,134,149,216,162,0,
192,41,225,8,2,48,177,36,1,149,13,196,15,0,200,209,97,199,128,99,32,176,
195,192,113,57,143,0,167,133,32,230,80,28,202,139,175,238,2,48,189,192,20,
1,119,80,87,193,186,129,89,56,72,197,209,200,193,185,35,23,71,109,13,219,
36,98,232,237,156,13,26,208,211,14,102,19,87,137,91,95,128,0,10,96,24,92,0,
0,83,2,53,56,0,0,165,3,28,204,160,160,226,100,226,200,211,76,241,240,0,1,
102,8,22,75,64,137,73,20,230,105,133,7,19,39,22,70,154,103,143,128,0,11,48,
20,28,76,156,113,75,34,78,62,0,0,45,3,103,31,0,0,22,65,44,57,137,62,33,179,
216,162,152,192,131,18,124,162,27,61,138,41,108,32,196,159,16,217,232,235,
81,76,104,73,137,62,81,13,158,142,181,20,184,16,98,79,136,108,244,244,168,
166,56,36,196,159,40,134,207,79,74,138,93,10,49,39,194,173,192,158,158,149,
20,188,20,98,79,133,91,129,61,109,74,41,124,30,68,159,16,217,236,83,108,96,
68,137,62,81,13,158,197,54,182,17,34,79,136,108,244,117,169,182,52,38,68,
159,40,134,207,71,90,155,92,8,145,39,196,54,122,122,84,219,28,19,34,79,148,
67,103,167,165,77,174,133,72,147,225,86,224,79,79,74,155,94,10,145,39,194,
173,192,158,182,165,54,190,206,25,212,35,208,226,100,150,211,201,29,162,44,
140,35,103,0,0,0,0,0,0,3,192,252,206,25,228,35,208,226,100,150,211,201,29,
162,44,140,35,103,0,0,0,0,0,0,3,192,252,206,25,244,35,208,226,100,150,211,
201,29,162,44,140,35,103,0,0,0,0,0,0,3,192,252,206,26,4,35,208,226,100,150,
211,201,29,162,44,140,35,103,0,0,0,0,0,0,0,1,0,206,26,20,35,208,226,100,
150,211,201,29,162,44,140,35,103,0,0,0,0,0,0,0,1,0,206,26,36,35,208,226,
100,150,211,201,29,162,44,140,35,103,0,0,0,0,0,0,0,65,0,206,26,52,35,208,
226,100,150,211,201,29,162,44,140,35,103,0,0,0,0,0,0,0,65,0,206,26,68,35,
208,226,100,150,211,201,29,162,44,140,35,103,0,0,0,0,0,0,0,65,0,206,26,84,
35,208,226,100,150,211,201,29,162,44,140,35,103,0,0,0,0,0,0,0,129,0,195,
154,99,16,38,36,0,251,68,117,179,216,162,128,68,72,1,241,13,158,197,20,150,
25,18,0,125,162,58,217,232,235,117,100,162,136,25,18,0,125,162,58,217,232,
235,116,36,162,145,2,226,64,15,136,108,244,117,186,178,81,73,129,113,32,7,
196,54,122,58,221,9,40,165,64,200,144,3,237,17,214,207,79,75,171,37,20,80,
200,144,3,237,17,214,207,79,75,161,37,20,138,23,18,0,124,67,103,167,165,
213,146,138,77,11,137,0,62,33,179,211,210,232,73,69,42,133,196,128,31,10,
183,2,125,89,40,163,5,196,128,31,10,183,2,125,9,40,164,96,200,144,3,224,
221,64,172,157,89,40,163,134,68,128,31,6,234,5,100,232,73,69,35,133,68,128,
31,104,142,182,125,89,40,180,0,168,144,3,237,17,214,207,161,37,22,144,19,
18,0,124,67,103,213,146,139,80,9,137,0,62,33,179,232,73,69,172,5,90,40,153,
59,68,117,179,216,166,192,77,162,137,147,136,108,246,41,180,176,219,69,19,
39,104,142,182,122,58,221,89,41,178,6,218,40,153,59,68,117,179,209,214,232,
73,77,162,6,90,40,153,56,134,207,71,91,171,37,54,152,25,104,162,100,226,27,
61,29,110,132,148,218,160,109,162,137,147,180,71,91,61,61,46,172,148,217,
67,109,20,76,157,162,58,217,233,233,116,36,166,209,67,45,20,76,156,67,103,
167,165,213,146,155,77,12,180,81,50,113,13,158,158,151,66,74,109,84,50,209,
68,201,194,173,192,159,86,74,108,193,150,138,38,78,21,110,4,250,18,83,104,
193,182,138,38,78,13,212,10,201,213,146,155,56,109,162,137,147,131,117,2,
178,116,36,166,209,194,237,20,76,157,162,58,217,245,100,167,16,2,237,20,76,
157,162,58,217,244,36,167,18,2,173,20,76,156,67,103,213,146,156,80,10,180,
81,50,113,13,159,66,74,113,97,175,221,48,216,110,64,4,42,22,189,179,0,196,
133,0,185,80,32,28,78,99,193,18,80,36,4,19,159,141,172,0,178,90,4,74,73,0,
22,209,68,201,187,129,4,2,8,3,132,64,60,36,6,149,113,72,176,171,240,84,0,
157,91,116,116,32,11,42,218,221,216,181,129,32,3,234,219,165,3,188,231,235,
249,8,187,152,252,47,86,227,105,18,7,244,17,91,42,56,175,185,248,110,173,
198,209,208,36,0,238,82,97,87,188,189,179,240,93,122,32,12,22,162,42,125,
144,132,160,7,236,161,25,232,237,105,64,205,59,127,102,158,160,230,63,11,
217,66,51,210,129,154,118,254,205,61,65,236,127,171,197,34,168,48,6,90,194,
1,0,39,75,88,72,8,9,33,186,194,80,64,76,13,214,19,2,130,96,110,150,189,0,
65,6,51,214,20,128,65,17,11,214,19,130,137,121,211,210,211,144,6,39,75,88,
80,0,201,119,235,10,8,41,86,231,71,88,80,129,79,135,186,122,133,224,34,25,
69,234,80,3,91,141,172,40,96,139,113,180,181,133,36,21,110,54,142,134,176,
165,1,176,23,213,47,0,216,134,234,215,128,111,117,181,232,128,209,3,70,230,
107,64,5,139,168,209,235,10,32,36,144,102,235,136,3,146,27,172,40,160,146,
132,103,172,40,192,115,3,117,133,28,22,113,163,69,172,41,103,1,66,188,17,
145,52,168,4,202,113,67,76,130,227,76,194,13,240,108,0,0,83,224,0,2,193,0,
104,146,84,97,48,0,1,94,192,56,169,24,145,179,192,0,5,112,8,56,16,32,128,
56,18,52,125,230,86,147,190,140,28,50,21,13,39,31,23,60,145,158,57,12,141,
47,129,6,155,194,188,24,49,39,199,89,188,124,92,242,70,120,224,201,33,69,
15,155,163,201,68,14,49,39,199,197,211,116,240,242,113,197,232,18,180,254,
36,3,17,46,18,243,35,100,128,172,156,178,70,163,154,76,34,248,146,164,108,
248,75,204,141,146,28,217,115,137,27,95,27,241,173,236,162,160,224,200,2,
206,9,113,13,148,192,209,18,22,164,146,37,193,57,162,4,249,39,196,128,24,2,
178,66,213,136,68,201,16,77,209,131,31,192,242,88,96,92,191,151,34,100,136,
38,232,255,252,92,221,199,197,12,68,209,82,66,212,11,155,185,41,197,13,55,
38,3,66,213,47,135,254,72,12,162,99,133,116,112,0,1,72,66,14,16,16,50,37,
202,160,150,154,66,14,20,8,57,192,28,24,80,113,50,113,100,105,166,120,248,
0,0,179,1,65,196,201,199,20,178,36,227,224,0,2,208,54,113,240,0,1,100,11,
181,192,0,5,178,1,18,160,65,24,131,20,145,25,188,48,132,122,28,76,146,218,
121,35,180,69,145,132,108,224,0,0,0,0,0,0,120,31,153,188,56,132,122,28,76,
146,218,121,35,180,69,145,132,108,224,0,0,0,0,0,0,120,31,168,160,45,110,23,
30,176,33,184,0,0,183,32,29,235,2,27,199,23,0,0,23,4,51,120,129,8,244,56,
153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,240,63,51,120,145,8,244,
56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,0,64,51,120,161,8,
244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,0,64,51,120,177,
8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,16,64,51,120,
193,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,16,64,51,
120,209,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,16,64,
51,120,225,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,32,
64,32,227,194,0,97,57,162,4,246,104,5,34,92,35,68,225,161,166,220,16,16,
137,112,52,41,73,29,185,1,65,196,201,197,145,166,153,246,72,3,137,204,120,
34,74,8,199,1,67,17,162,112,201,84,128,97,144,78,25,42,16,131,169,1,205,66,
8,35,68,225,161,166,239,128,0,10,192,64,196,104,156,50,96,0,2,172,73,240,
117,96,57,170,97,4,104,156,52,52,221,240,0,1,82,1,74,9,129,125,240,0,1,82,
32,148,25,174,137,58,23,51,190,0,0,42,69,64,195,32,156,50,96,0,2,160,81,
238,2,3,107,173,218,3,192,
};
#elif defined(DUK_USE_DOUBLE_BE)
DUK_INTERNAL const duk_uint8_t duk_builtins_data[4281] = {
144,148,105,226,32,68,52,228,254,12,104,202,37,132,52,167,194,138,105,245,
124,57,28,211,57,18,64,52,239,126,44,138,111,175,241,164,19,87,145,30,33,
167,22,145,159,8,211,139,9,225,42,5,240,145,139,163,163,8,211,139,10,228,
64,211,19,132,140,93,29,56,70,156,88,119,34,66,146,36,104,137,194,70,46,
142,172,35,78,44,47,146,195,102,11,240,145,139,163,175,8,211,139,9,228,240,
242,112,145,139,163,179,8,211,139,8,237,34,130,118,49,116,118,225,26,48,0,
1,98,29,201,158,46,183,39,135,147,132,140,93,16,132,76,66,33,8,66,16,132,
33,8,66,26,180,105,97,167,68,150,34,33,154,112,0,1,91,247,35,79,111,237,
198,174,232,47,31,23,95,17,13,31,249,96,211,49,50,53,214,77,141,24,0,0,181,
10,228,240,242,15,128,140,65,128,134,188,0,0,90,167,97,181,224,0,2,213,62,
53,224,0,2,213,66,237,120,0,0,181,81,204,107,192,0,5,170,150,67,94,0,0,45,
84,245,90,240,0,1,106,169,162,215,128,0,11,85,93,150,188,0,0,90,171,111,53,
109,22,162,26,48,0,1,84,23,201,146,243,225,26,39,12,145,136,104,192,0,5,61,
11,228,201,121,240,100,19,134,72,196,33,195,14,40,203,112,64,190,76,232,
145,153,136,15,255,0,0,0,0,0,0,25,152,15,254,0,0,0,0,0,0,25,120,144,13,96,
155,194,56,80,206,36,67,141,20,228,70,57,81,206,100,131,156,39,132,168,23,
194,70,46,137,208,21,200,129,166,39,9,24,186,39,72,119,34,66,146,36,104,
137,194,70,46,137,212,23,201,97,179,5,248,72,197,209,58,194,121,60,60,156,
36,98,232,157,129,29,164,80,78,198,46,137,218,146,121,25,71,146,9,209,5,
209,61,48,126,14,138,152,30,67,186,23,143,139,175,131,202,135,228,72,85,
144,83,60,179,30,94,209,233,102,30,98,105,230,103,30,114,121,231,104,30,
122,137,231,233,30,130,153,232,106,30,138,169,232,235,30,144,67,193,25,19,
136,108,207,30,41,224,140,137,194,173,192,153,228,5,242,100,188,248,70,137,
195,36,79,78,47,147,37,231,193,144,78,25,34,122,145,111,36,74,232,176,13,
17,61,234,226,93,207,148,160,84,75,141,7,27,161,32,33,18,225,80,212,76,154,
2,2,70,65,56,100,237,34,140,209,2,67,32,156,50,118,145,64,186,230,61,205,
35,103,155,32,36,141,19,134,78,210,40,206,16,36,70,137,195,39,105,20,11,
174,99,220,210,54,121,210,1,137,33,1,228,207,16,17,70,146,66,3,201,164,32,
0,65,112,152,56,196,159,31,23,77,211,195,201,199,23,160,72,214,246,81,6,12,
73,241,214,111,31,23,60,145,158,56,50,72,81,67,230,232,242,80,19,49,39,199,
89,188,124,92,242,70,120,227,64,194,75,154,72,12,9,73,6,111,21,120,12,40,
144,19,39,25,0,225,144,168,105,56,248,185,228,140,241,200,96,64,100,42,26,
78,62,46,121,35,52,18,92,116,1,36,64,47,158,64,49,98,66,100,156,242,65,23,
196,149,35,103,194,94,100,108,144,230,203,156,64,66,37,201,16,11,32,249,
132,4,34,92,44,93,146,55,152,72,24,137,112,151,153,27,36,5,100,229,144,8,
162,98,92,210,5,76,73,241,214,111,31,23,60,145,158,57,44,48,46,92,185,164,
160,72,151,41,0,50,107,179,244,59,36,93,127,92,6,19,172,3,11,216,0,56,224,
151,29,102,241,241,115,201,25,227,164,64,106,37,199,197,211,116,240,242,
113,197,233,144,40,248,185,228,140,241,196,75,132,109,24,72,128,43,39,84,
129,13,173,161,144,168,105,56,98,78,100,142,214,215,69,1,13,173,161,144,
168,105,57,34,78,100,142,214,215,69,16,67,107,105,110,114,168,254,24,147,
153,35,181,181,212,32,67,107,105,110,114,168,254,72,147,153,35,181,181,212,
36,65,130,3,144,8,26,252,200,13,30,85,16,16,64,90,242,231,192,64,161,163,
203,31,26,172,193,17,4,23,105,159,96,27,172,251,16,32,196,4,14,137,112,17,
136,48,164,28,134,80,215,202,1,132,130,8,12,39,52,64,155,31,24,56,36,1,189,
207,132,0,35,233,35,195,62,3,196,149,36,100,72,160,2,200,232,44,227,0,11,
37,160,68,142,128,36,157,25,200,32,26,79,90,4,73,43,192,122,54,71,65,103,
44,248,14,134,140,151,227,138,231,208,45,96,148,248,134,140,151,227,138,
231,240,1,255,254,10,74,146,56,128,104,4,147,152,72,6,144,28,174,143,8,1,
30,1,165,3,96,31,0,211,3,21,11,153,35,0,211,131,68,131,160,137,16,250,5,
196,131,160,137,200,160,199,156,67,248,0,255,255,65,140,10,48,177,115,56,
35,130,60,19,134,79,89,240,52,177,115,56,39,12,156,123,144,217,251,15,135,
34,167,30,20,170,154,255,232,12,47,244,0,97,28,17,224,39,238,32,40,71,4,
120,39,12,156,4,253,228,5,137,195,39,30,228,54,124,4,253,228,128,194,115,
68,9,252,15,128,232,104,201,126,56,191,35,64,90,193,41,241,13,25,47,199,23,
228,105,3,86,225,1,100,224,156,199,130,36,249,144,10,192,76,71,250,16,15,
18,61,96,17,62,200,3,72,128,136,143,247,32,22,75,64,137,248,64,22,79,90,39,
249,64,38,84,12,167,20,52,223,196,2,230,238,45,214,36,120,32,72,158,208,4,
102,238,45,194,2,201,197,186,196,143,4,9,19,218,0,92,221,202,61,228,143,4,
9,19,218,8,35,55,113,110,16,22,78,81,239,36,120,32,72,158,208,64,73,197,12,
255,0,13,18,60,128,159,212,128,169,76,17,156,185,100,76,255,163,64,65,26,
57,114,200,153,255,70,144,33,13,18,232,50,75,226,104,6,149,3,41,199,246,
130,12,128,28,142,156,120,203,175,158,8,194,207,1,6,81,20,79,88,11,237,84,
11,161,32,63,247,255,255,255,255,255,255,137,235,16,221,170,129,116,36,0,0,
0,0,0,0,0,0,28,196,7,255,128,0,0,0,0,0,2,61,123,164,137,162,164,218,67,74,
134,162,120,128,255,224,0,0,0,0,0,0,71,173,33,129,52,84,155,72,105,80,212,
79,16,63,252,0,0,0,0,0,0,3,244,143,146,22,230,192,60,176,0,0,0,0,0,0,33,
214,2,251,82,1,73,180,134,204,134,36,96,33,159,255,255,255,255,255,255,144,
235,16,221,169,0,164,218,67,102,67,18,48,48,207,255,255,255,255,255,255,
196,60,17,145,56,134,204,241,226,158,8,200,156,42,220,9,158,65,196,34,92,
42,26,137,147,120,64,74,37,196,54,100,49,35,188,36,5,68,184,208,113,187,
194,80,212,75,146,1,73,196,54,100,49,35,188,38,57,37,56,240,0,0,0,0,0,0,0,
0,32,235,248,68,48,156,2,24,94,24,0,243,119,10,139,144,123,242,3,102,238,
18,239,115,72,217,160,11,223,16,23,55,113,241,32,145,36,57,188,18,16,102,3,
5,120,35,34,89,32,15,180,152,173,127,0,218,235,88,0,228,180,227,200,127,
248,0,0,0,0,0,0,197,107,240,64,6,77,220,24,38,78,74,113,67,77,130,4,12,155,
185,52,48,156,148,226,134,155,4,10,194,96,129,132,166,238,45,194,2,201,193,
130,100,228,167,20,52,216,32,113,41,187,139,112,128,178,114,104,97,57,41,
197,13,54,8,32,48,216,32,130,195,224,130,19,97,124,134,23,6,0,57,137,62,77,
12,38,12,0,179,18,124,45,22,190,96,128,141,176,134,28,98,79,180,152,139,
218,45,124,193,1,27,97,16,32,196,159,24,230,204,246,194,40,89,137,62,210,
98,103,92,217,158,216,70,7,49,39,193,130,100,182,17,194,140,73,246,147,16,
250,9,146,216,72,6,49,39,193,131,22,194,72,73,137,62,210,98,31,65,139,97,
40,32,196,159,14,234,70,86,194,88,89,137,62,210,98,63,93,72,202,216,76,10,
49,39,198,33,180,153,37,108,38,134,152,147,237,38,38,117,13,164,201,43,97,
56,40,196,159,36,65,57,163,149,176,158,26,98,79,180,152,165,210,9,205,28,
173,133,0,243,18,124,98,22,180,72,130,115,71,43,97,68,72,196,159,105,49,51,
168,90,209,34,9,205,28,173,133,33,19,18,124,154,24,76,185,164,227,138,89,
18,119,0,7,145,39,201,161,132,188,64,124,137,62,49,11,90,36,65,57,163,149,
210,166,37,34,79,180,152,153,212,45,104,145,4,230,142,87,74,160,84,137,62,
72,130,115,71,43,171,234,134,200,147,237,38,41,116,130,115,71,43,171,235,5,
72,147,227,16,218,76,146,186,254,184,108,137,62,210,98,103,80,218,76,146,
186,254,192,68,137,62,29,212,140,174,207,178,23,34,79,180,152,143,215,82,
50,187,62,208,60,137,62,12,19,37,210,182,21,34,79,180,152,135,208,76,151,
74,224,68,137,62,49,205,153,238,175,186,23,34,79,180,152,153,215,54,103,
186,190,240,92,137,62,22,139,95,48,64,70,235,251,225,210,36,251,73,136,189,
162,215,204,16,17,186,255,2,14,98,79,152,32,35,108,48,64,242,36,249,130,2,
55,75,6,212,224,72,200,51,128,114,108,28,100,128,0,0,0,0,0,0,0,12,110,127,
48,98,115,249,201,117,243,249,195,21,159,206,38,47,63,156,86,8,75,144,94,
82,1,38,73,79,208,67,95,233,1,6,128,14,79,129,186,40,249,18,149,182,207,
144,200,155,188,248,204,105,184,207,142,199,137,175,201,0,159,72,10,5,21,
221,10,120,74,129,124,36,98,232,228,74,81,62,160,20,10,107,186,21,114,32,
105,137,194,70,46,142,68,165,19,235,1,64,170,187,161,119,34,66,146,36,104,
137,194,70,46,142,68,165,19,236,1,64,174,187,161,95,37,134,204,23,225,35,
23,71,34,82,137,246,128,160,89,93,208,167,147,195,201,194,70,46,142,68,165,
19,238,1,64,182,187,161,71,105,20,19,177,139,163,145,41,68,16,7,6,15,82,70,
72,115,96,32,106,15,0,0,0,0,0,91,60,165,195,201,194,8,134,149,216,162,0,
192,41,225,8,2,48,177,36,1,149,13,196,15,0,200,209,97,199,128,99,32,176,
195,192,113,57,143,0,167,133,32,230,80,28,202,139,175,238,2,48,189,192,20,
1,119,80,87,193,186,129,89,56,72,197,209,200,193,185,35,23,71,109,13,219,
36,98,232,237,156,13,26,208,211,14,102,19,87,137,91,95,128,0,10,96,24,92,0,
0,83,2,53,56,0,0,165,3,28,204,160,160,226,100,226,200,211,76,241,240,0,1,
102,8,22,75,64,137,73,20,230,105,133,7,19,39,22,70,154,103,143,128,0,11,48,
20,28,76,156,113,75,34,78,62,0,0,45,3,103,31,0,0,22,65,44,57,137,62,33,179,
216,162,152,192,131,18,124,162,27,61,138,41,108,32,196,159,16,217,232,235,
81,76,104,73,137,62,81,13,158,142,181,20,184,16,98,79,136,108,244,244,168,
166,56,36,196,159,40,134,207,79,74,138,93,10,49,39,194,173,192,158,158,149,
20,188,20,98,79,133,91,129,61,109,74,41,124,30,68,159,16,217,236,83,108,96,
68,137,62,81,13,158,197,54,182,17,34,79,136,108,244,117,169,182,52,38,68,
159,40,134,207,71,90,155,92,8,145,39,196,54,122,122,84,219,28,19,34,79,148,
67,103,167,165,77,174,133,72,147,225,86,224,79,79,74,155,94,10,145,39,194,
173,192,158,182,165,54,190,206,25,212,35,208,226,100,150,211,201,29,162,44,
140,35,103,0,255,192,0,0,0,0,0,0,206,25,228,35,208,226,100,150,211,201,29,
162,44,140,35,103,0,255,192,0,0,0,0,0,0,206,25,244,35,208,226,100,150,211,
201,29,162,44,140,35,103,0,255,192,0,0,0,0,0,0,206,26,4,35,208,226,100,150,
211,201,29,162,44,140,35,103,1,0,0,0,0,0,0,0,0,206,26,20,35,208,226,100,
150,211,201,29,162,44,140,35,103,1,0,0,0,0,0,0,0,0,206,26,36,35,208,226,
100,150,211,201,29,162,44,140,35,103,1,0,64,0,0,0,0,0,0,206,26,52,35,208,
226,100,150,211,201,29,162,44,140,35,103,1,0,64,0,0,0,0,0,0,206,26,68,35,
208,226,100,150,211,201,29,162,44,140,35,103,1,0,64,0,0,0,0,0,0,206,26,84,
35,208,226,100,150,211,201,29,162,44,140,35,103,1,0,128,0,0,0,0,0,0,195,
154,99,16,38,36,0,251,68,117,179,216,162,128,68,72,1,241,13,158,197,20,150,
25,18,0,125,162,58,217,232,235,117,100,162,136,25,18,0,125,162,58,217,232,
235,116,36,162,145,2,226,64,15,136,108,244,117,186,178,81,73,129,113,32,7,
196,54,122,58,221,9,40,165,64,200,144,3,237,17,214,207,79,75,171,37,20,80,
200,144,3,237,17,214,207,79,75,161,37,20,138,23,18,0,124,67,103,167,165,
213,146,138,77,11,137,0,62,33,179,211,210,232,73,69,42,133,196,128,31,10,
183,2,125,89,40,163,5,196,128,31,10,183,2,125,9,40,164,96,200,144,3,224,
221,64,172,157,89,40,163,134,68,128,31,6,234,5,100,232,73,69,35,133,68,128,
31,104,142,182,125,89,40,180,0,168,144,3,237,17,214,207,161,37,22,144,19,
18,0,124,67,103,213,146,139,80,9,137,0,62,33,179,232,73,69,172,5,90,40,153,
59,68,117,179,216,166,192,77,162,137,147,136,108,246,41,180,176,219,69,19,
39,104,142,182,122,58,221,89,41,178,6,218,40,153,59,68,117,179,209,214,232,
73,77,162,6,90,40,153,56,134,207,71,91,171,37,54,152,25,104,162,100,226,27,
61,29,110,132,148,218,160,109,162,137,147,180,71,91,61,61,46,172,148,217,
67,109,20,76,157,162,58,217,233,233,116,36,166,209,67,45,20,76,156,67,103,
167,165,213,146,155,77,12,180,81,50,113,13,158,158,151,66,74,109,84,50,209,
68,201,194,173,192,159,86,74,108,193,150,138,38,78,21,110,4,250,18,83,104,
193,182,138,38,78,13,212,10,201,213,146,155,56,109,162,137,147,131,117,2,
178,116,36,166,209,194,237,20,76,157,162,58,217,245,100,167,16,2,237,20,76,
157,162,58,217,244,36,167,18,2,173,20,76,156,67,103,213,146,156,80,10,180,
81,50,113,13,159,66,74,113,97,175,221,48,216,110,64,4,42,22,189,179,0,196,
133,0,185,80,32,28,78,99,193,18,80,36,4,19,159,141,172,0,178,90,4,74,73,0,
22,209,68,201,187,129,4,2,8,3,132,64,60,36,4,0,91,240,168,177,69,118,144,
157,91,116,116,32,32,1,53,216,221,218,170,139,3,234,219,165,0,255,152,185,
11,251,232,231,188,47,86,227,105,18,1,255,184,170,59,41,92,23,240,110,173,
198,209,208,36,3,253,188,183,177,82,110,80,224,93,122,32,32,4,144,253,170,
34,22,140,7,236,161,25,232,237,105,64,63,230,160,158,102,127,59,205,11,217,
66,51,210,128,127,237,65,60,204,254,119,155,171,197,34,168,48,6,90,194,1,0,
39,75,88,72,8,9,33,186,194,80,64,76,13,214,19,2,130,96,110,150,189,0,65,6,
51,214,20,128,65,17,11,214,19,130,137,121,211,210,211,144,6,39,75,88,80,0,
201,119,235,10,8,41,86,231,71,88,80,129,79,135,186,122,133,224,34,25,69,
234,80,3,91,141,172,40,96,139,113,180,181,133,36,21,110,54,142,134,176,165,
1,176,23,213,47,0,216,134,234,215,128,111,117,181,232,128,209,3,70,230,107,
64,5,139,168,209,235,10,32,36,144,102,235,136,3,146,27,172,40,160,146,132,
103,172,40,192,115,3,117,133,28,22,113,163,69,172,41,103,1,66,188,17,145,
52,168,4,202,113,67,76,130,227,76,194,13,240,108,0,0,83,224,0,2,193,0,104,
146,84,97,48,0,1,94,192,56,169,24,145,179,192,0,5,112,8,56,16,32,128,56,18,
52,125,230,86,147,190,140,28,50,21,13,39,31,23,60,145,158,57,12,141,47,129,
6,155,194,188,24,49,39,199,89,188,124,92,242,70,120,224,201,33,69,15,155,
163,201,68,14,49,39,199,197,211,116,240,242,113,197,232,18,180,254,36,3,17,
46,18,243,35,100,128,172,156,178,70,163,154,76,34,248,146,164,108,248,75,
204,141,146,28,217,115,137,27,95,27,241,173,236,162,160,224,200,2,206,9,
113,13,148,192,209,18,22,164,146,37,193,57,162,4,249,39,196,128,24,2,178,
66,213,136,68,201,16,77,209,131,31,192,242,88,96,92,191,151,34,100,136,38,
232,255,252,92,221,199,197,12,68,209,82,66,212,11,155,185,41,197,13,55,38,
3,66,213,47,135,254,72,12,162,99,133,116,112,0,1,72,66,14,16,16,50,37,202,
160,150,154,66,14,20,8,57,192,28,24,80,113,50,113,100,105,166,120,248,0,0,
179,1,65,196,201,199,20,178,36,227,224,0,2,208,54,113,240,0,1,100,11,181,
192,0,5,178,1,18,160,65,24,131,20,145,25,188,48,132,122,28,76,146,218,121,
35,180,69,145,132,108,224,31,248,0,0,0,0,0,0,25,188,56,132,122,28,76,146,
218,121,35,180,69,145,132,108,224,31,248,0,0,0,0,0,0,40,160,45,110,23,30,
176,33,184,0,0,183,32,29,235,2,27,199,23,0,0,23,4,51,120,129,8,244,56,153,
37,180,242,71,104,139,35,8,217,192,63,240,0,0,0,0,0,0,51,120,145,8,244,56,
153,37,180,242,71,104,139,35,8,217,192,64,0,0,0,0,0,0,0,51,120,161,8,244,
56,153,37,180,242,71,104,139,35,8,217,192,64,0,0,0,0,0,0,0,51,120,177,8,
244,56,153,37,180,242,71,104,139,35,8,217,192,64,16,0,0,0,0,0,0,51,120,193,
8,244,56,153,37,180,242,71,104,139,35,8,217,192,64,16,0,0,0,0,0,0,51,120,
209,8,244,56,153,37,180,242,71,104,139,35,8,217,192,64,16,0,0,0,0,0,0,51,
120,225,8,244,56,153,37,180,242,71,104,139,35,8,217,192,64,32,0,0,0,0,0,0,
32,227,194,0,97,57,162,4,246,104,5,34,92,35,68,225,161,166,220,16,16,137,
112,52,41,73,29,185,1,65,196,201,197,145,166,153,246,72,3,137,204,120,34,
74,8,199,1,67,17,162,112,201,84,128,97,144,78,25,42,16,131,169,1,205,66,8,
35,68,225,161,166,239,128,0,10,192,64,196,104,156,50,96,0,2,172,73,240,117,
96,57,170,97,4,104,156,52,52,221,240,0,1,82,1,74,9,129,125,240,0,1,82,32,
148,25,174,137,58,23,51,190,0,0,42,69,64,195,32,156,50,96,0,2,160,81,238,2,
3,107,173,218,3,192,
};
#elif defined(DUK_USE_DOUBLE_ME)
DUK_INTERNAL const duk_uint8_t duk_builtins_data[4281] = {
144,148,105,226,32,68,52,228,254,12,104,202,37,132,52,167,194,138,105,245,
124,57,28,211,57,18,64,52,239,126,44,138,111,175,241,164,19,87,145,30,33,
167,22,145,159,8,211,139,9,225,42,5,240,145,139,163,163,8,211,139,10,228,
64,211,19,132,140,93,29,56,70,156,88,119,34,66,146,36,104,137,194,70,46,
142,172,35,78,44,47,146,195,102,11,240,145,139,163,175,8,211,139,9,228,240,
242,112,145,139,163,179,8,211,139,8,237,34,130,118,49,116,118,225,26,48,0,
1,98,29,201,158,46,183,39,135,147,132,140,93,16,132,76,66,33,8,66,16,132,
33,8,66,26,180,105,97,167,68,150,34,33,154,112,0,1,91,247,35,79,111,237,
198,174,232,47,31,23,95,17,13,31,249,96,211,49,50,53,214,77,141,24,0,0,181,
10,228,240,242,15,128,140,65,128,134,188,0,0,90,167,97,181,224,0,2,213,62,
53,224,0,2,213,66,237,120,0,0,181,81,204,107,192,0,5,170,150,67,94,0,0,45,
84,245,90,240,0,1,106,169,162,215,128,0,11,85,93,150,188,0,0,90,171,111,53,
109,22,162,26,48,0,1,84,23,201,146,243,225,26,39,12,145,136,104,192,0,5,61,
11,228,201,121,240,100,19,134,72,196,33,195,14,40,203,112,64,190,76,232,
145,153,136,0,0,31,15,224,0,0,0,25,152,0,0,30,15,224,0,0,0,25,120,144,13,
96,155,194,56,80,206,36,67,141,20,228,70,57,81,206,100,131,156,39,132,168,
23,194,70,46,137,208,21,200,129,166,39,9,24,186,39,72,119,34,66,146,36,104,
137,194,70,46,137,212,23,201,97,179,5,248,72,197,209,58,194,121,60,60,156,
36,98,232,157,129,29,164,80,78,198,46,137,218,146,121,25,71,146,9,209,5,
209,61,48,126,14,138,152,30,67,186,23,143,139,175,131,202,135,228,72,85,
144,83,60,179,30,94,209,233,102,30,98,105,230,103,30,114,121,231,104,30,
122,137,231,233,30,130,153,232,106,30,138,169,232,235,30,144,67,193,25,19,
136,108,207,30,41,224,140,137,194,173,192,153,228,5,242,100,188,248,70,137,
195,36,79,78,47,147,37,231,193,144,78,25,34,122,145,111,36,74,232,176,13,
17,61,234,226,93,207,148,160,84,75,141,7,27,161,32,33,18,225,80,212,76,154,
2,2,70,65,56,100,237,34,140,209,2,67,32,156,50,118,145,64,186,230,61,205,
35,103,155,32,36,141,19,134,78,210,40,206,16,36,70,137,195,39,105,20,11,
174,99,220,210,54,121,210,1,137,33,1,228,207,16,17,70,146,66,3,201,164,32,
0,65,112,152,56,196,159,31,23,77,211,195,201,199,23,160,72,214,246,81,6,12,
73,241,214,111,31,23,60,145,158,56,50,72,81,67,230,232,242,80,19,49,39,199,
89,188,124,92,242,70,120,227,64,194,75,154,72,12,9,73,6,111,21,120,12,40,
144,19,39,25,0,225,144,168,105,56,248,185,228,140,241,200,96,64,100,42,26,
78,62,46,121,35,52,18,92,116,1,36,64,47,158,64,49,98,66,100,156,242,65,23,
196,149,35,103,194,94,100,108,144,230,203,156,64,66,37,201,16,11,32,249,
132,4,34,92,44,93,146,55,152,72,24,137,112,151,153,27,36,5,100,229,144,8,
162,98,92,210,5,76,73,241,214,111,31,23,60,145,158,57,44,48,46,92,185,164,
160,72,151,41,0,50,107,179,244,59,36,93,127,92,6,19,172,3,11,216,0,56,224,
151,29,102,241,241,115,201,25,227,164,64,106,37,199,197,211,116,240,242,
113,197,233,144,40,248,185,228,140,241,196,75,132,109,24,72,128,43,39,84,
129,13,173,161,144,168,105,56,98,78,100,142,214,215,69,1,13,173,161,144,
168,105,57,34,78,100,142,214,215,69,16,67,107,105,110,114,168,254,24,147,
153,35,181,181,212,32,67,107,105,110,114,168,254,72,147,153,35,181,181,212,
36,65,130,3,144,8,26,252,200,13,30,85,16,16,64,90,242,231,192,64,161,163,
203,31,26,172,193,17,4,23,105,159,96,27,172,251,16,32,196,4,14,137,112,17,
136,48,164,28,134,80,215,202,1,132,130,8,12,39,52,64,155,31,24,56,36,1,189,
207,132,0,35,233,35,195,62,3,196,149,36,100,72,160,2,200,232,44,227,0,11,
37,160,68,142,128,36,157,25,200,32,26,79,90,4,73,43,192,122,54,71,65,103,
44,248,14,134,140,151,227,138,231,208,45,96,148,248,134,140,151,227,138,
231,240,1,255,254,10,74,146,56,128,104,4,147,152,72,6,144,28,174,143,8,1,
30,1,165,3,96,31,0,211,3,21,11,153,35,0,211,131,68,131,160,137,16,250,5,
196,131,160,137,200,160,199,156,67,248,0,255,255,65,140,10,48,177,115,56,
35,130,60,19,134,79,89,240,52,177,115,56,39,12,156,123,144,217,251,15,135,
34,167,30,20,170,154,255,232,12,47,244,0,97,28,17,224,39,238,32,40,71,4,
120,39,12,156,4,253,228,5,137,195,39,30,228,54,124,4,253,228,128,194,115,
68,9,252,15,128,232,104,201,126,56,191,35,64,90,193,41,241,13,25,47,199,23,
228,105,3,86,225,1,100,224,156,199,130,36,249,144,10,192,76,71,250,16,15,
18,61,96,17,62,200,3,72,128,136,143,247,32,22,75,64,137,248,64,22,79,90,39,
249,64,38,84,12,167,20,52,223,196,2,230,238,45,214,36,120,32,72,158,208,4,
102,238,45,194,2,201,197,186,196,143,4,9,19,218,0,92,221,202,61,228,143,4,
9,19,218,8,35,55,113,110,16,22,78,81,239,36,120,32,72,158,208,64,73,197,12,
255,0,13,18,60,128,159,212,128,169,76,17,156,185,100,76,255,163,64,65,26,
57,114,200,153,255,70,144,33,13,18,232,50,75,226,104,6,149,3,41,199,246,
130,12,128,28,142,156,120,203,175,158,8,194,207,1,6,81,20,79,88,11,237,84,
11,161,32,127,255,247,191,255,255,255,255,137,235,16,221,170,129,116,36,0,
0,0,0,0,16,0,0,12,196,0,0,15,135,240,0,0,0,2,61,123,164,137,162,164,218,67,
74,134,162,120,128,0,1,224,254,0,0,0,0,71,173,33,129,52,84,155,72,105,80,
212,79,16,0,0,60,63,192,0,0,0,3,244,143,146,22,230,192,0,0,176,60,0,0,0,0,
33,214,2,251,82,1,73,180,134,204,134,36,96,127,255,159,161,255,255,255,255,
144,235,16,221,169,0,164,218,67,102,67,18,48,63,255,207,240,255,255,255,
255,196,60,17,145,56,134,204,241,226,158,8,200,156,42,220,9,158,65,196,34,
92,42,26,137,147,120,64,74,37,196,54,100,49,35,188,36,5,68,184,208,113,187,
194,80,212,75,146,1,73,196,54,100,49,35,188,38,57,37,56,240,0,0,0,0,0,0,0,
0,32,235,248,68,48,156,2,24,94,24,0,243,119,10,139,144,123,242,3,102,238,
18,239,115,72,217,160,11,223,16,23,55,113,241,32,145,36,57,188,18,16,102,3,
5,120,35,34,89,32,15,180,152,173,127,0,218,235,88,0,228,180,227,200,0,0,
248,127,0,0,0,0,197,107,240,64,6,77,220,24,38,78,74,113,67,77,130,4,12,155,
185,52,48,156,148,226,134,155,4,10,194,96,129,132,166,238,45,194,2,201,193,
130,100,228,167,20,52,216,32,113,41,187,139,112,128,178,114,104,97,57,41,
197,13,54,8,32,48,216,32,130,195,224,130,19,97,124,134,23,6,0,57,137,62,77,
12,38,12,0,179,18,124,45,22,190,96,128,141,176,134,28,98,79,180,152,139,
218,45,124,193,1,27,97,16,32,196,159,24,230,204,246,194,40,89,137,62,210,
98,103,92,217,158,216,70,7,49,39,193,130,100,182,17,194,140,73,246,147,16,
250,9,146,216,72,6,49,39,193,131,22,194,72,73,137,62,210,98,31,65,139,97,
40,32,196,159,14,234,70,86,194,88,89,137,62,210,98,63,93,72,202,216,76,10,
49,39,198,33,180,153,37,108,38,134,152,147,237,38,38,117,13,164,201,43,97,
56,40,196,159,36,65,57,163,149,176,158,26,98,79,180,152,165,210,9,205,28,
173,133,0,243,18,124,98,22,180,72,130,115,71,43,97,68,72,196,159,105,49,51,
168,90,209,34,9,205,28,173,133,33,19,18,124,154,24,76,185,164,227,138,89,
18,119,0,7,145,39,201,161,132,188,64,124,137,62,49,11,90,36,65,57,163,149,
210,166,37,34,79,180,152,153,212,45,104,145,4,230,142,87,74,160,84,137,62,
72,130,115,71,43,171,234,134,200,147,237,38,41,116,130,115,71,43,171,235,5,
72,147,227,16,218,76,146,186,254,184,108,137,62,210,98,103,80,218,76,146,
186,254,192,68,137,62,29,212,140,174,207,178,23,34,79,180,152,143,215,82,
50,187,62,208,60,137,62,12,19,37,210,182,21,34,79,180,152,135,208,76,151,
74,224,68,137,62,49,205,153,238,175,186,23,34,79,180,152,153,215,54,103,
186,190,240,92,137,62,22,139,95,48,64,70,235,251,225,210,36,251,73,136,189,
162,215,204,16,17,186,255,2,14,98,79,152,32,35,108,48,64,242,36,249,130,2,
55,75,6,212,224,72,200,51,128,114,108,28,100,128,0,0,0,0,0,0,0,12,110,127,
48,98,115,249,201,117,243,249,195,21,159,206,38,47,63,156,86,8,75,144,94,
82,1,38,73,79,208,67,95,233,1,6,128,14,79,129,186,40,249,18,149,182,207,
144,200,155,188,248,204,105,184,207,142,199,137,175,201,0,159,72,10,5,21,
221,10,120,74,129,124,36,98,232,228,74,81,62,160,20,10,107,186,21,114,32,
105,137,194,70,46,142,68,165,19,235,1,64,170,187,161,119,34,66,146,36,104,
137,194,70,46,142,68,165,19,236,1,64,174,187,161,95,37,134,204,23,225,35,
23,71,34,82,137,246,128,160,89,93,208,167,147,195,201,194,70,46,142,68,165,
19,238,1,64,182,187,161,71,105,20,19,177,139,163,145,41,68,16,7,6,15,82,70,
72,115,96,0,15,106,32,0,0,0,0,91,60,165,195,201,194,8,134,149,216,162,0,
192,41,225,8,2,48,177,36,1,149,13,196,15,0,200,209,97,199,128,99,32,176,
195,192,113,57,143,0,167,133,32,230,80,28,202,139,175,238,2,48,189,192,20,
1,119,80,87,193,186,129,89,56,72,197,209,200,193,185,35,23,71,109,13,219,
36,98,232,237,156,13,26,208,211,14,102,19,87,137,91,95,128,0,10,96,24,92,0,
0,83,2,53,56,0,0,165,3,28,204,160,160,226,100,226,200,211,76,241,240,0,1,
102,8,22,75,64,137,73,20,230,105,133,7,19,39,22,70,154,103,143,128,0,11,48,
20,28,76,156,113,75,34,78,62,0,0,45,3,103,31,0,0,22,65,44,57,137,62,33,179,
216,162,152,192,131,18,124,162,27,61,138,41,108,32,196,159,16,217,232,235,
81,76,104,73,137,62,81,13,158,142,181,20,184,16,98,79,136,108,244,244,168,
166,56,36,196,159,40,134,207,79,74,138,93,10,49,39,194,173,192,158,158,149,
20,188,20,98,79,133,91,129,61,109,74,41,124,30,68,159,16,217,236,83,108,96,
68,137,62,81,13,158,197,54,182,17,34,79,136,108,244,117,169,182,52,38,68,
159,40,134,207,71,90,155,92,8,145,39,196,54,122,122,84,219,28,19,34,79,148,
67,103,167,165,77,174,133,72,147,225,86,224,79,79,74,155,94,10,145,39,194,
173,192,158,182,165,54,190,206,25,212,35,208,226,100,150,211,201,29,162,44,
140,35,103,0,0,3,192,252,0,0,0,0,206,25,228,35,208,226,100,150,211,201,29,
162,44,140,35,103,0,0,3,192,252,0,0,0,0,206,25,244,35,208,226,100,150,211,
201,29,162,44,140,35,103,0,0,3,192,252,0,0,0,0,206,26,4,35,208,226,100,150,
211,201,29,162,44,140,35,103,0,0,0,1,0,0,0,0,0,206,26,20,35,208,226,100,
150,211,201,29,162,44,140,35,103,0,0,0,1,0,0,0,0,0,206,26,36,35,208,226,
100,150,211,201,29,162,44,140,35,103,0,0,0,65,0,0,0,0,0,206,26,52,35,208,
226,100,150,211,201,29,162,44,140,35,103,0,0,0,65,0,0,0,0,0,206,26,68,35,
208,226,100,150,211,201,29,162,44,140,35,103,0,0,0,65,0,0,0,0,0,206,26,84,
35,208,226,100,150,211,201,29,162,44,140,35,103,0,0,0,129,0,0,0,0,0,195,
154,99,16,38,36,0,251,68,117,179,216,162,128,68,72,1,241,13,158,197,20,150,
25,18,0,125,162,58,217,232,235,117,100,162,136,25,18,0,125,162,58,217,232,
235,116,36,162,145,2,226,64,15,136,108,244,117,186,178,81,73,129,113,32,7,
196,54,122,58,221,9,40,165,64,200,144,3,237,17,214,207,79,75,171,37,20,80,
200,144,3,237,17,214,207,79,75,161,37,20,138,23,18,0,124,67,103,167,165,
213,146,138,77,11,137,0,62,33,179,211,210,232,73,69,42,133,196,128,31,10,
183,2,125,89,40,163,5,196,128,31,10,183,2,125,9,40,164,96,200,144,3,224,
221,64,172,157,89,40,163,134,68,128,31,6,234,5,100,232,73,69,35,133,68,128,
31,104,142,182,125,89,40,180,0,168,144,3,237,17,214,207,161,37,22,144,19,
18,0,124,67,103,213,146,139,80,9,137,0,62,33,179,232,73,69,172,5,90,40,153,
59,68,117,179,216,166,192,77,162,137,147,136,108,246,41,180,176,219,69,19,
39,104,142,182,122,58,221,89,41,178,6,218,40,153,59,68,117,179,209,214,232,
73,77,162,6,90,40,153,56,134,207,71,91,171,37,54,152,25,104,162,100,226,27,
61,29,110,132,148,218,160,109,162,137,147,180,71,91,61,61,46,172,148,217,
67,109,20,76,157,162,58,217,233,233,116,36,166,209,67,45,20,76,156,67,103,
167,165,213,146,155,77,12,180,81,50,113,13,158,158,151,66,74,109,84,50,209,
68,201,194,173,192,159,86,74,108,193,150,138,38,78,21,110,4,250,18,83,104,
193,182,138,38,78,13,212,10,201,213,146,155,56,109,162,137,147,131,117,2,
178,116,36,166,209,194,237,20,76,157,162,58,217,245,100,167,16,2,237,20,76,
157,162,58,217,244,36,167,18,2,173,20,76,156,67,103,213,146,156,80,10,180,
81,50,113,13,159,66,74,113,97,175,221,48,216,110,64,4,42,22,189,179,0,196,
133,0,185,80,32,28,78,99,193,18,80,36,4,19,159,141,172,0,178,90,4,74,73,0,
22,209,68,201,187,129,4,2,8,3,132,64,60,36,0,171,240,84,6,149,113,72,176,
157,91,116,116,32,88,181,129,32,11,42,218,221,131,234,219,165,1,8,187,152,
255,188,231,235,248,47,86,227,105,18,2,56,175,185,255,244,17,91,40,110,173,
198,209,208,36,7,188,189,179,240,238,82,97,80,93,122,32,125,144,132,160,12,
22,162,42,7,236,161,25,232,237,105,64,158,160,230,63,205,59,127,102,11,217,
66,51,210,129,61,65,236,127,154,118,254,205,171,197,34,168,48,6,90,194,1,0,
39,75,88,72,8,9,33,186,194,80,64,76,13,214,19,2,130,96,110,150,189,0,65,6,
51,214,20,128,65,17,11,214,19,130,137,121,211,210,211,144,6,39,75,88,80,0,
201,119,235,10,8,41,86,231,71,88,80,129,79,135,186,122,133,224,34,25,69,
234,80,3,91,141,172,40,96,139,113,180,181,133,36,21,110,54,142,134,176,165,
1,176,23,213,47,0,216,134,234,215,128,111,117,181,232,128,209,3,70,230,107,
64,5,139,168,209,235,10,32,36,144,102,235,136,3,146,27,172,40,160,146,132,
103,172,40,192,115,3,117,133,28,22,113,163,69,172,41,103,1,66,188,17,145,
52,168,4,202,113,67,76,130,227,76,194,13,240,108,0,0,83,224,0,2,193,0,104,
146,84,97,48,0,1,94,192,56,169,24,145,179,192,0,5,112,8,56,16,32,128,56,18,
52,125,230,86,147,190,140,28,50,21,13,39,31,23,60,145,158,57,12,141,47,129,
6,155,194,188,24,49,39,199,89,188,124,92,242,70,120,224,201,33,69,15,155,
163,201,68,14,49,39,199,197,211,116,240,242,113,197,232,18,180,254,36,3,17,
46,18,243,35,100,128,172,156,178,70,163,154,76,34,248,146,164,108,248,75,
204,141,146,28,217,115,137,27,95,27,241,173,236,162,160,224,200,2,206,9,
113,13,148,192,209,18,22,164,146,37,193,57,162,4,249,39,196,128,24,2,178,
66,213,136,68,201,16,77,209,131,31,192,242,88,96,92,191,151,34,100,136,38,
232,255,252,92,221,199,197,12,68,209,82,66,212,11,155,185,41,197,13,55,38,
3,66,213,47,135,254,72,12,162,99,133,116,112,0,1,72,66,14,16,16,50,37,202,
160,150,154,66,14,20,8,57,192,28,24,80,113,50,113,100,105,166,120,248,0,0,
179,1,65,196,201,199,20,178,36,227,224,0,2,208,54,113,240,0,1,100,11,181,
192,0,5,178,1,18,160,65,24,131,20,145,25,188,48,132,122,28,76,146,218,121,
35,180,69,145,132,108,224,0,0,120,31,128,0,0,0,25,188,56,132,122,28,76,146,
218,121,35,180,69,145,132,108,224,0,0,120,31,128,0,0,0,40,160,45,110,23,30,
176,33,184,0,0,183,32,29,235,2,27,199,23,0,0,23,4,51,120,129,8,244,56,153,
37,180,242,71,104,139,35,8,217,192,0,0,240,63,0,0,0,0,51,120,145,8,244,56,
153,37,180,242,71,104,139,35,8,217,192,0,0,0,64,0,0,0,0,51,120,161,8,244,
56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,64,0,0,0,0,51,120,177,8,
244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,16,64,0,0,0,0,51,120,193,
8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,16,64,0,0,0,0,51,120,
209,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,16,64,0,0,0,0,51,
120,225,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,32,64,0,0,0,0,
32,227,194,0,97,57,162,4,246,104,5,34,92,35,68,225,161,166,220,16,16,137,
112,52,41,73,29,185,1,65,196,201,197,145,166,153,246,72,3,137,204,120,34,
74,8,199,1,67,17,162,112,201,84,128,97,144,78,25,42,16,131,169,1,205,66,8,
35,68,225,161,166,239,128,0,10,192,64,196,104,156,50,96,0,2,172,73,240,117,
96,57,170,97,4,104,156,52,52,221,240,0,1,82,1,74,9,129,125,240,0,1,82,32,
148,25,174,137,58,23,51,190,0,0,42,69,64,195,32,156,50,96,0,2,160,81,238,2,
3,107,173,218,3,192,
};
#else
#error invalid endianness defines
#endif
#endif  /* DUK_USE_ROM_OBJECTS */

/* automatic undefs */
#undef DUK__REFCINIT
//#line 1 "duk_error_macros.c"
/*
 *  Error and fatal handling.
 */

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

#define DUK__ERRFMT_BUFSIZE  256  /* size for formatting buffers */

#if defined(DUK_USE_VERBOSE_ERRORS)

DUK_INTERNAL DUK_COLD void duk_err_handle_error_fmt(duk_hthread *thr, const char *filename, duk_uint_t line_and_code, const char *fmt, ...) {
    va_list ap;
    char msg[DUK__ERRFMT_BUFSIZE];
    va_start(ap, fmt);
    (void) DUK_VSNPRINTF(msg, sizeof(msg), fmt, ap);
    msg[sizeof(msg) - 1] = (char) 0;
    duk_err_create_and_throw(thr, (duk_errcode_t) (line_and_code >> 24), msg, filename, (duk_int_t) (line_and_code & 0x00ffffffL));
    va_end(ap);  /* dead code, but ensures portability (see Linux man page notes) */
}

DUK_INTERNAL DUK_COLD void duk_err_handle_error(duk_hthread *thr, const char *filename, duk_uint_t line_and_code, const char *msg) {
    duk_err_create_and_throw(thr, (duk_errcode_t) (line_and_code >> 24), msg, filename, (duk_int_t) (line_and_code & 0x00ffffffL));
}

#else  /* DUK_USE_VERBOSE_ERRORS */

DUK_INTERNAL DUK_COLD void duk_err_handle_error(duk_hthread *thr, duk_errcode_t code) {
    duk_err_create_and_throw(thr, code);
}

#endif  /* DUK_USE_VERBOSE_ERRORS */

/*
 *  Error throwing helpers
 */

#if defined(DUK_USE_VERBOSE_ERRORS)
#if defined(DUK_USE_PARANOID_ERRORS)
DUK_INTERNAL DUK_COLD void duk_err_require_type_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t idx, const char *expect_name) {
    DUK_ERROR_RAW_FMT3(thr, filename, linenumber, DUK_ERR_TYPE_ERROR, "%s required, found %s (stack index %ld)",
                       expect_name, duk_get_type_name(thr, idx), (long) idx);
}
#else
DUK_INTERNAL DUK_COLD void duk_err_require_type_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t idx, const char *expect_name) {
    DUK_ERROR_RAW_FMT3(thr, filename, linenumber, DUK_ERR_TYPE_ERROR, "%s required, found %s (stack index %ld)",
                       expect_name, duk_push_string_readable(thr, idx), (long) idx);
}
#endif
DUK_INTERNAL DUK_COLD void duk_err_error_internal(duk_hthread *thr, const char *filename, duk_int_t linenumber) {
    DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_ERROR, DUK_STR_INTERNAL_ERROR);
}
DUK_INTERNAL DUK_COLD void duk_err_error_alloc_failed(duk_hthread *thr, const char *filename, duk_int_t linenumber) {
    DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_ERROR, DUK_STR_ALLOC_FAILED);
}
DUK_INTERNAL DUK_COLD void duk_err_error(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message) {
    DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_ERROR, message);
}
DUK_INTERNAL DUK_COLD void duk_err_range(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message) {
    DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_RANGE_ERROR, message);
}
DUK_INTERNAL DUK_COLD void duk_err_range_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t idx) {
    DUK_ERROR_RAW_FMT1(thr, filename, linenumber, DUK_ERR_RANGE_ERROR, "invalid stack index %ld", (long) (idx));
}
DUK_INTERNAL DUK_COLD void duk_err_range_push_beyond(duk_hthread *thr, const char *filename, duk_int_t linenumber) {
    DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_RANGE_ERROR, DUK_STR_PUSH_BEYOND_ALLOC_STACK);
}
DUK_INTERNAL DUK_COLD void duk_err_type_invalid_args(duk_hthread *thr, const char *filename, duk_int_t linenumber) {
    DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_TYPE_ERROR, DUK_STR_INVALID_ARGS);
}
DUK_INTERNAL DUK_COLD void duk_err_type_invalid_state(duk_hthread *thr, const char *filename, duk_int_t linenumber) {
    DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_TYPE_ERROR, DUK_STR_INVALID_STATE);
}
DUK_INTERNAL DUK_COLD void duk_err_type_invalid_trap_result(duk_hthread *thr, const char *filename, duk_int_t linenumber) {
    DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_TYPE_ERROR, DUK_STR_INVALID_TRAP_RESULT);
}
#else
/* The file/line arguments are NULL and 0, they're ignored by DUK_ERROR_RAW()
 * when non-verbose errors are used.
 */

DUK_NORETURN(DUK_LOCAL_DECL void duk__err_shared(duk_hthread *thr, duk_errcode_t code));
DUK_LOCAL void duk__err_shared(duk_hthread *thr, duk_errcode_t code) {
    DUK_ERROR_RAW(thr, NULL, 0, code, NULL);
}
DUK_INTERNAL DUK_COLD void duk_err_error(duk_hthread *thr) {
    duk__err_shared(thr, DUK_ERR_ERROR);
}
DUK_INTERNAL DUK_COLD void duk_err_range(duk_hthread *thr) {
    duk__err_shared(thr, DUK_ERR_RANGE_ERROR);
}
DUK_INTERNAL DUK_COLD void duk_err_eval(duk_hthread *thr) {
    duk__err_shared(thr, DUK_ERR_EVAL_ERROR);
}
DUK_INTERNAL DUK_COLD void duk_err_reference(duk_hthread *thr) {
    duk__err_shared(thr, DUK_ERR_REFERENCE_ERROR);
}
DUK_INTERNAL DUK_COLD void duk_err_syntax(duk_hthread *thr) {
    duk__err_shared(thr, DUK_ERR_SYNTAX_ERROR);
}
DUK_INTERNAL DUK_COLD void duk_err_type(duk_hthread *thr) {
    duk__err_shared(thr, DUK_ERR_TYPE_ERROR);
}
DUK_INTERNAL DUK_COLD void duk_err_uri(duk_hthread *thr) {
    duk__err_shared(thr, DUK_ERR_URI_ERROR);
}
#endif

/*
 *  Default fatal error handler
 */

DUK_INTERNAL DUK_COLD void duk_default_fatal_handler(void *udata, const char *msg) {
    DUK_UNREF(udata);
    DUK_UNREF(msg);

    msg = msg ? msg : "NULL";

#if defined(DUK_USE_FATAL_HANDLER)
    /* duk_config.h provided a custom default fatal handler. */
    DUK_D(DUK_DPRINT("custom default fatal error handler called: %s", msg));
    DUK_USE_FATAL_HANDLER(udata, msg);
#elif defined(DUK_USE_CPP_EXCEPTIONS)
    /* With C++ use a duk_fatal_exception which user code can catch in
     * a natural way.
     */
    DUK_D(DUK_DPRINT("built-in default C++ fatal error handler called: %s", msg));
    throw duk_fatal_exception(msg);
#else
    /* Default behavior is to abort() on error.  There's no printout
     * which makes this awkward, so it's always recommended to use an
     * explicit fatal error handler.
     *
     * ====================================================================
     * NOTE: If you are seeing this, you are most likely dealing with an
     * uncaught error.  You should provide a fatal error handler in Duktape
     * heap creation, and should consider using a protected call as your
     * first call into an empty Duktape context to properly handle errors.
     * See:
     *   - http://duktape.org/guide.html#error-handling
     *   - http://wiki.duktape.org/HowtoFatalErrors.html
     *   - http://duktape.org/api.html#taglist-protected
     * ====================================================================
     */
    DUK_D(DUK_DPRINT("built-in default fatal error handler called: %s", msg));
    DUK_ABORT();
#endif

    DUK_D(DUK_DPRINT("fatal error handler returned, enter forever loop"));
    for (;;) {
        /* Loop forever to ensure we don't return. */
    }
}

/* automatic undefs */
#undef DUK__ERRFMT_BUFSIZE
//#line 1 "duk_unicode_support.c"
/*
 *  Various Unicode help functions for character classification predicates,
 *  case conversion, decoding, etc.
 */

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

/*
 *  Fast path tables
 */

#if defined(DUK_USE_IDCHAR_FASTPATH)
DUK_INTERNAL const duk_int8_t duk_is_idchar_tab[128] = {
    /* 0: not IdentifierStart or IdentifierPart
     * 1: IdentifierStart and IdentifierPart
     * -1: IdentifierPart only
     */
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   /* 0x00...0x0f */
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   /* 0x10...0x1f */
    0,  0,  0,  0,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   /* 0x20...0x2f */
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0,  0,  0,  0,  0,  0,   /* 0x30...0x3f */
    0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,   /* 0x40...0x4f */
    1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  1,   /* 0x50...0x5f */
    0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,   /* 0x60...0x6f */
    1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0    /* 0x70...0x7f */
};
#endif

/*
 *  XUTF-8 and CESU-8 encoding/decoding
 */

DUK_INTERNAL duk_small_int_t duk_unicode_get_xutf8_length(duk_ucodepoint_t cp) {
    duk_uint_fast32_t x = (duk_uint_fast32_t) cp;
    if (x < 0x80UL) {
        /* 7 bits */
        return 1;
    } else if (x < 0x800UL) {
        /* 11 bits */
        return 2;
    } else if (x < 0x10000UL) {
        /* 16 bits */
        return 3;
    } else if (x < 0x200000UL) {
        /* 21 bits */
        return 4;
    } else if (x < 0x4000000UL) {
        /* 26 bits */
        return 5;
    } else if (x < (duk_ucodepoint_t) 0x80000000UL) {
        /* 31 bits */
        return 6;
    } else {
        /* 36 bits */
        return 7;
    }
}

#if defined(DUK_USE_ASSERTIONS)
DUK_INTERNAL duk_small_int_t duk_unicode_get_cesu8_length(duk_ucodepoint_t cp) {
    duk_uint_fast32_t x = (duk_uint_fast32_t) cp;
    if (x < 0x80UL) {
        /* 7 bits */
        return 1;
    } else if (x < 0x800UL) {
        /* 11 bits */
        return 2;
    } else if (x < 0x10000UL) {
        /* 16 bits */
        return 3;
    } else {
        /* Encoded as surrogate pair, each encoding to 3 bytes for
         * 6 bytes total.  Codepoints above U+10FFFF encode as 6 bytes
         * too, see duk_unicode_encode_cesu8().
          */
        return 3 + 3;
    }
}
#endif  /* DUK_USE_ASSERTIONS */

DUK_INTERNAL const duk_uint8_t duk_unicode_xutf8_markers[7] = {
    0x00, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe
};

/* Encode to extended UTF-8; 'out' must have space for at least
 * DUK_UNICODE_MAX_XUTF8_LENGTH bytes.  Allows encoding of any
 * 32-bit (unsigned) codepoint.
 */
DUK_INTERNAL duk_small_int_t duk_unicode_encode_xutf8(duk_ucodepoint_t cp, duk_uint8_t *out) {
    duk_uint_fast32_t x = (duk_uint_fast32_t) cp;
    duk_small_int_t len;
    duk_uint8_t marker;
    duk_small_int_t i;

    len = duk_unicode_get_xutf8_length(cp);
    DUK_ASSERT(len > 0);

    marker = duk_unicode_xutf8_markers[len - 1];  /* 64-bit OK because always >= 0 */

    i = len;
    DUK_ASSERT(i > 0);
    do {
        i--;
        if (i > 0) {
            out[i] = (duk_uint8_t) (0x80 + (x & 0x3f));
            x >>= 6;
        } else {
            /* Note: masking of 'x' is not necessary because of
             * range check and shifting -> no bits overlapping
             * the marker should be set.
             */
            out[0] = (duk_uint8_t) (marker + x);
        }
    } while (i > 0);

    return len;
}

/* Encode to CESU-8; 'out' must have space for at least
 * DUK_UNICODE_MAX_CESU8_LENGTH bytes; codepoints above U+10FFFF
 * will encode to garbage but won't overwrite the output buffer.
 */
DUK_INTERNAL duk_small_int_t duk_unicode_encode_cesu8(duk_ucodepoint_t cp, duk_uint8_t *out) {
    duk_uint_fast32_t x = (duk_uint_fast32_t) cp;
    duk_small_int_t len;

    if (x < 0x80UL) {
        out[0] = (duk_uint8_t) x;
        len = 1;
    } else if (x < 0x800UL) {
        out[0] = (duk_uint8_t) (0xc0 + ((x >> 6) & 0x1f));
        out[1] = (duk_uint8_t) (0x80 + (x & 0x3f));
        len = 2;
    } else if (x < 0x10000UL) {
        /* surrogate pairs get encoded here */
        out[0] = (duk_uint8_t) (0xe0 + ((x >> 12) & 0x0f));
        out[1] = (duk_uint8_t) (0x80 + ((x >> 6) & 0x3f));
        out[2] = (duk_uint8_t) (0x80 + (x & 0x3f));
        len = 3;
    } else {
        /*
         *  Unicode codepoints above U+FFFF are encoded as surrogate
         *  pairs here.  This ensures that all CESU-8 codepoints are
         *  16-bit values as expected in ECMAScript.  The surrogate
         *  pairs always get a 3-byte encoding (each) in CESU-8.
         *  See: http://en.wikipedia.org/wiki/Surrogate_pair
         *
         *  20-bit codepoint, 10 bits (A and B) per surrogate pair:
         *
         *    x = 0b00000000 0000AAAA AAAAAABB BBBBBBBB
         *  sp1 = 0b110110AA AAAAAAAA  (0xd800 + ((x >> 10) & 0x3ff))
         *  sp2 = 0b110111BB BBBBBBBB  (0xdc00 + (x & 0x3ff))
         *
         *  Encoded into CESU-8:
         *
         *  sp1 -> 0b11101101  (0xe0 + ((sp1 >> 12) & 0x0f))
         *      -> 0b1010AAAA  (0x80 + ((sp1 >> 6) & 0x3f))
         *      -> 0b10AAAAAA  (0x80 + (sp1 & 0x3f))
         *  sp2 -> 0b11101101  (0xe0 + ((sp2 >> 12) & 0x0f))
         *      -> 0b1011BBBB  (0x80 + ((sp2 >> 6) & 0x3f))
         *      -> 0b10BBBBBB  (0x80 + (sp2 & 0x3f))
         *
         *  Note that 0x10000 must be subtracted first.  The code below
         *  avoids the sp1, sp2 temporaries which saves around 20 bytes
         *  of code.
         */

        x -= 0x10000UL;

        out[0] = (duk_uint8_t) (0xed);
        out[1] = (duk_uint8_t) (0xa0 + ((x >> 16) & 0x0f));
        out[2] = (duk_uint8_t) (0x80 + ((x >> 10) & 0x3f));
        out[3] = (duk_uint8_t) (0xed);
        out[4] = (duk_uint8_t) (0xb0 + ((x >> 6) & 0x0f));
        out[5] = (duk_uint8_t) (0x80 + (x & 0x3f));
        len = 6;
    }

    return len;
}

/* Decode helper.  Return zero on error. */
DUK_INTERNAL duk_small_int_t duk_unicode_decode_xutf8(duk_hthread *thr, const duk_uint8_t **ptr, const duk_uint8_t *ptr_start, const duk_uint8_t *ptr_end, duk_ucodepoint_t *out_cp) {
    const duk_uint8_t *p;
    duk_uint32_t res;
    duk_uint_fast8_t ch;
    duk_small_int_t n;

    DUK_UNREF(thr);

    p = *ptr;
    if (p < ptr_start || p >= ptr_end) {
        goto fail;
    }

    /*
     *  UTF-8 decoder which accepts longer than standard byte sequences.
     *  This allows full 32-bit code points to be used.
     */

    ch = (duk_uint_fast8_t) (*p++);
    if (ch < 0x80) {
        /* 0xxx xxxx   [7 bits] */
        res = (duk_uint32_t) (ch & 0x7f);
        n = 0;
    } else if (ch < 0xc0) {
        /* 10xx xxxx -> invalid */
        goto fail;
    } else if (ch < 0xe0) {
        /* 110x xxxx   10xx xxxx   [11 bits] */
        res = (duk_uint32_t) (ch & 0x1f);
        n = 1;
    } else if (ch < 0xf0) {
        /* 1110 xxxx   10xx xxxx   10xx xxxx   [16 bits] */
        res = (duk_uint32_t) (ch & 0x0f);
        n = 2;
    } else if (ch < 0xf8) {
        /* 1111 0xxx   10xx xxxx   10xx xxxx   10xx xxxx   [21 bits] */
        res = (duk_uint32_t) (ch & 0x07);
        n = 3;
    } else if (ch < 0xfc) {
        /* 1111 10xx   10xx xxxx   10xx xxxx   10xx xxxx   10xx xxxx   [26 bits] */
        res = (duk_uint32_t) (ch & 0x03);
        n = 4;
    } else if (ch < 0xfe) {
        /* 1111 110x   10xx xxxx   10xx xxxx   10xx xxxx   10xx xxxx   10xx xxxx   [31 bits] */
        res = (duk_uint32_t) (ch & 0x01);
        n = 5;
    } else if (ch < 0xff) {
        /* 1111 1110   10xx xxxx   10xx xxxx   10xx xxxx   10xx xxxx   10xx xxxx   10xx xxxx   [36 bits] */
        res = (duk_uint32_t) (0);
        n = 6;
    } else {
        /* 8-byte format could be:
         * 1111 1111   10xx xxxx   10xx xxxx   10xx xxxx   10xx xxxx   10xx xxxx   10xx xxxx   10xx xxxx   [41 bits]
         *
         * However, this format would not have a zero bit following the
         * leading one bits and would not allow 0xFF to be used as an
         * "invalid xutf-8" marker for internal keys.  Further, 8-byte
         * encodings (up to 41 bit code points) are not currently needed.
         */
        goto fail;
    }

    DUK_ASSERT(p >= ptr_start);  /* verified at beginning */
    if (p + n > ptr_end) {
        /* check pointer at end */
        goto fail;
    }

    while (n > 0) {
        DUK_ASSERT(p >= ptr_start && p < ptr_end);
        ch = (duk_uint_fast8_t) (*p++);
#if 0
        if (ch & 0xc0 != 0x80) {
            /* not a continuation byte */
            p--;
            *ptr = p;
            *out_cp = DUK_UNICODE_CP_REPLACEMENT_CHARACTER;
            return 1;
        }
#endif
        res = (res << 6) + (duk_uint32_t) (ch & 0x3f);
        n--;
    }

    *ptr = p;
    *out_cp = res;
    return 1;

 fail:
    return 0;
}

/* used by e.g. duk_regexp_executor.c, string built-ins */
DUK_INTERNAL duk_ucodepoint_t duk_unicode_decode_xutf8_checked(duk_hthread *thr, const duk_uint8_t **ptr, const duk_uint8_t *ptr_start, const duk_uint8_t *ptr_end) {
    duk_ucodepoint_t cp;

    if (duk_unicode_decode_xutf8(thr, ptr, ptr_start, ptr_end, &cp)) {
        return cp;
    }
    DUK_ERROR_INTERNAL(thr);
    DUK_WO_NORETURN(return 0;);
}

/* Compute (extended) utf-8 length without codepoint encoding validation,
 * used for string interning.
 *
 * NOTE: This algorithm is performance critical, more so than string hashing
 * in some cases.  It is needed when interning a string and needs to scan
 * every byte of the string with no skipping.  Having an ASCII fast path
 * is useful if possible in the algorithm.  The current algorithms were
 * chosen from several variants, based on x64 gcc -O2 testing.  See:
 * https://github.com/svaarala/duktape/pull/422
 *
 * NOTE: must match tools/dukutil.py:duk_unicode_unvalidated_utf8_length().
 */

#if defined(DUK_USE_PREFER_SIZE)
/* Small variant; roughly 150 bytes smaller than the fast variant. */
DUK_INTERNAL duk_size_t duk_unicode_unvalidated_utf8_length(const duk_uint8_t *data, duk_size_t blen) {
    const duk_uint8_t *p;
    const duk_uint8_t *p_end;
    duk_size_t ncont;
    duk_size_t clen;

    p = data;
    p_end = data + blen;
    ncont = 0;
    while (p != p_end) {
        duk_uint8_t x;
        x = *p++;
        if (DUK_UNLIKELY(x >= 0x80 && x <= 0xbf)) {
            ncont++;
        }
    }

    DUK_ASSERT(ncont <= blen);
    clen = blen - ncont;
    DUK_ASSERT(clen <= blen);
    return clen;
}
#else  /* DUK_USE_PREFER_SIZE */
/* This seems like a good overall approach.  Fast path for ASCII in 4 byte
 * blocks.
 */
DUK_INTERNAL duk_size_t duk_unicode_unvalidated_utf8_length(const duk_uint8_t *data, duk_size_t blen) {
    const duk_uint8_t *p;
    const duk_uint8_t *p_end;
    const duk_uint32_t *p32_end;
    const duk_uint32_t *p32;
    duk_size_t ncont;
    duk_size_t clen;

    ncont = 0;  /* number of continuation (non-initial) bytes in [0x80,0xbf] */
    p = data;
    p_end = data + blen;
    if (blen < 16) {
        goto skip_fastpath;
    }

    /* Align 'p' to 4; the input data may have arbitrary alignment.
     * End of string check not needed because blen >= 16.
     */
    while (((duk_size_t) (const void *) p) & 0x03U) {
        duk_uint8_t x;
        x = *p++;
        if (DUK_UNLIKELY(x >= 0x80 && x <= 0xbf)) {
            ncont++;
        }
    }

    /* Full, aligned 4-byte reads. */
    p32_end = (const duk_uint32_t *) (const void *) (p + ((duk_size_t) (p_end - p) & (duk_size_t) (~0x03)));
    p32 = (const duk_uint32_t *) (const void *) p;
    while (p32 != (const duk_uint32_t *) p32_end) {
        duk_uint32_t x;
        x = *p32++;
        if (DUK_LIKELY((x & 0x80808080UL) == 0)) {
            ;  /* ASCII fast path */
        } else {
            /* Flip highest bit of each byte which changes
             * the bit pattern 10xxxxxx into 00xxxxxx which
             * allows an easy bit mask test.
             */
            x ^= 0x80808080UL;
            if (DUK_UNLIKELY(!(x & 0xc0000000UL))) {
                ncont++;
            }
            if (DUK_UNLIKELY(!(x & 0x00c00000UL))) {
                ncont++;
            }
            if (DUK_UNLIKELY(!(x & 0x0000c000UL))) {
                ncont++;
            }
            if (DUK_UNLIKELY(!(x & 0x000000c0UL))) {
                ncont++;
            }
        }
    }
    p = (const duk_uint8_t *) p32;
    /* Fall through to handle the rest. */

 skip_fastpath:
    while (p != p_end) {
        duk_uint8_t x;
        x = *p++;
        if (DUK_UNLIKELY(x >= 0x80 && x <= 0xbf)) {
            ncont++;
        }
    }

    DUK_ASSERT(ncont <= blen);
    clen = blen - ncont;
    DUK_ASSERT(clen <= blen);
    return clen;
}
#endif  /* DUK_USE_PREFER_SIZE */

/* Check whether a string is UTF-8 compatible or not. */
DUK_INTERNAL duk_bool_t duk_unicode_is_utf8_compatible(const duk_uint8_t *buf, duk_size_t len) {
    duk_size_t i = 0;
#if !defined(DUK_USE_PREFER_SIZE)
    duk_size_t len_safe;
#endif

    /* Many practical strings are ASCII only, so use a fast path check
     * to check chunks of bytes at once with minimal branch cost.
     */
#if !defined(DUK_USE_PREFER_SIZE)
    len_safe = len & ~0x03UL;
    for (; i < len_safe; i += 4) {
        duk_uint8_t t = buf[i] | buf[i + 1] | buf[i + 2] | buf[i + 3];
        if (DUK_UNLIKELY((t & 0x80U) != 0U)) {
            /* At least one byte was outside 0x00-0x7f, break
             * out to slow path (and remain there).
             *
             * XXX: We could also deal with the problem character
             * and resume fast path later.
             */
            break;
        }
    }
#endif

    for (; i < len;) {
        duk_uint8_t t;
        duk_size_t left;
        duk_size_t ncont;
        duk_uint32_t cp;
        duk_uint32_t mincp;

        t = buf[i++];
        if (DUK_LIKELY((t & 0x80U) == 0U)) {
            /* Fast path, ASCII. */
            continue;
        }

        /* Non-ASCII start byte, slow path.
         *
         * 10xx xxxx          -> continuation byte
         * 110x xxxx + 1*CONT -> [0x80, 0x7ff]
         * 1110 xxxx + 2*CONT -> [0x800, 0xffff], must reject [0xd800,0xdfff]
         * 1111 0xxx + 3*CONT -> [0x10000, 0x10ffff]
         */
        left = len - i;
        if (t <= 0xdfU) {  /* 1101 1111 = 0xdf */
            if (t <= 0xbfU) {  /* 1011 1111 = 0xbf */
                return 0;
            }
            ncont = 1;
            mincp = 0x80UL;
            cp = t & 0x1fU;
        } else if (t <= 0xefU) {  /* 1110 1111 = 0xef */
            ncont = 2;
            mincp = 0x800UL;
            cp = t & 0x0fU;
        } else if (t <= 0xf7U) {  /* 1111 0111 = 0xf7 */
            ncont = 3;
            mincp = 0x10000UL;
            cp = t & 0x07U;
        } else {
            return 0;
        }
        if (left < ncont) {
            return 0;
        }
        while (ncont > 0U) {
            t = buf[i++];
            if ((t & 0xc0U) != 0x80U) {  /* 10xx xxxx */
                return 0;
            }
            cp = (cp << 6) + (t & 0x3fU);
            ncont--;
        }
        if (cp < mincp || cp > 0x10ffffUL || (cp >= 0xd800UL && cp <= 0xdfffUL)) {
            return 0;
        }
    }

    return 1;
}

/*
 *  Unicode range matcher
 *
 *  Matches a codepoint against a packed bitstream of character ranges.
 *  Used for slow path Unicode matching.
 */

/* Must match tools/extract_chars.py, generate_match_table3(). */
DUK_LOCAL duk_uint32_t duk__uni_decode_value(duk_bitdecoder_ctx *bd_ctx) {
    duk_uint32_t t;

    t = (duk_uint32_t) duk_bd_decode(bd_ctx, 4);
    if (t <= 0x0eU) {
        return t;
    }
    t = (duk_uint32_t) duk_bd_decode(bd_ctx, 8);
    if (t <= 0xfdU) {
        return t + 0x0f;
    }
    if (t == 0xfeU) {
        t = (duk_uint32_t) duk_bd_decode(bd_ctx, 12);
        return t + 0x0fU + 0xfeU;
    } else {
        t = (duk_uint32_t) duk_bd_decode(bd_ctx, 24);
        return t + 0x0fU + 0xfeU + 0x1000UL;
    }
}

DUK_LOCAL duk_small_int_t duk__uni_range_match(const duk_uint8_t *unitab, duk_size_t unilen, duk_codepoint_t cp) {
    duk_bitdecoder_ctx bd_ctx;
    duk_codepoint_t prev_re;

    duk_memzero(&bd_ctx, sizeof(bd_ctx));
    bd_ctx.data = (const duk_uint8_t *) unitab;
    bd_ctx.length = (duk_size_t) unilen;

    prev_re = 0;
    for (;;) {
        duk_codepoint_t r1, r2;
        r1 = (duk_codepoint_t) duk__uni_decode_value(&bd_ctx);
        if (r1 == 0) {
            break;
        }
        r2 = (duk_codepoint_t) duk__uni_decode_value(&bd_ctx);

        r1 = prev_re + r1;
        r2 = r1 + r2;
        prev_re = r2;

        /* [r1,r2] is the range */

        DUK_DDD(DUK_DDDPRINT("duk__uni_range_match: cp=%06lx range=[0x%06lx,0x%06lx]",
                             (unsigned long) cp, (unsigned long) r1, (unsigned long) r2));
        if (cp >= r1 && cp <= r2) {
            return 1;
        }
    }

    return 0;
}

/*
 *  "WhiteSpace" production check.
 */

DUK_INTERNAL duk_small_int_t duk_unicode_is_whitespace(duk_codepoint_t cp) {
    /*
     *  E5 Section 7.2 specifies six characters specifically as
     *  white space:
     *
     *    0009;<control>;Cc;0;S;;;;;N;CHARACTER TABULATION;;;;
     *    000B;<control>;Cc;0;S;;;;;N;LINE TABULATION;;;;
     *    000C;<control>;Cc;0;WS;;;;;N;FORM FEED (FF);;;;
     *    0020;SPACE;Zs;0;WS;;;;;N;;;;;
     *    00A0;NO-BREAK SPACE;Zs;0;CS;<noBreak> 0020;;;;N;NON-BREAKING SPACE;;;;
     *    FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;;
     *
     *  It also specifies any Unicode category 'Zs' characters as white
     *  space.  These can be extracted with the "tools/extract_chars.py" script.
     *  Current result:
     *
     *    RAW OUTPUT:
     *    ===========
     *    0020;SPACE;Zs;0;WS;;;;;N;;;;;
     *    00A0;NO-BREAK SPACE;Zs;0;CS;<noBreak> 0020;;;;N;NON-BREAKING SPACE;;;;
     *    1680;OGHAM SPACE MARK;Zs;0;WS;;;;;N;;;;;
     *    180E;MONGOLIAN VOWEL SEPARATOR;Zs;0;WS;;;;;N;;;;;
     *    2000;EN QUAD;Zs;0;WS;2002;;;;N;;;;;
     *    2001;EM QUAD;Zs;0;WS;2003;;;;N;;;;;
     *    2002;EN SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
     *    2003;EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
     *    2004;THREE-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
     *    2005;FOUR-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
     *    2006;SIX-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
     *    2007;FIGURE SPACE;Zs;0;WS;<noBreak> 0020;;;;N;;;;;
     *    2008;PUNCTUATION SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
     *    2009;THIN SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
     *    200A;HAIR SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
     *    202F;NARROW NO-BREAK SPACE;Zs;0;CS;<noBreak> 0020;;;;N;;;;;
     *    205F;MEDIUM MATHEMATICAL SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
     *    3000;IDEOGRAPHIC SPACE;Zs;0;WS;<wide> 0020;;;;N;;;;;
     *
     *    RANGES:
     *    =======
     *    0x0020
     *    0x00a0
     *    0x1680
     *    0x180e
     *    0x2000 ... 0x200a
     *    0x202f
     *    0x205f
     *    0x3000
     *
     *  A manual decoder (below) is probably most compact for this.
     */

    duk_uint_fast8_t lo;
    duk_uint_fast32_t hi;

    /* cp == -1 (EOF) never matches and causes return value 0 */

    lo = (duk_uint_fast8_t) (cp & 0xff);
    hi = (duk_uint_fast32_t) (cp >> 8);  /* does not fit into an uchar */

    if (hi == 0x0000UL) {
        if (lo == 0x09U || lo == 0x0bU || lo == 0x0cU ||
            lo == 0x20U || lo == 0xa0U) {
            return 1;
        }
    } else if (hi == 0x0020UL) {
        if (lo <= 0x0aU || lo == 0x2fU || lo == 0x5fU) {
            return 1;
        }
    } else if (cp == 0x1680L || cp == 0x180eL || cp == 0x3000L ||
               cp == 0xfeffL) {
        return 1;
    }

    return 0;
}

/*
 *  "LineTerminator" production check.
 */

DUK_INTERNAL duk_small_int_t duk_unicode_is_line_terminator(duk_codepoint_t cp) {
    /*
     *  E5 Section 7.3
     *
     *  A LineTerminatorSequence essentially merges <CR> <LF> sequences
     *  into a single line terminator.  This must be handled by the caller.
     */

    if (cp == 0x000aL || cp == 0x000dL || cp == 0x2028L ||
        cp == 0x2029L) {
        return 1;
    }

    return 0;
}

/*
 *  "IdentifierStart" production check.
 */

DUK_INTERNAL duk_small_int_t duk_unicode_is_identifier_start(duk_codepoint_t cp) {
    /*
     *  E5 Section 7.6:
     *
     *    IdentifierStart:
     *      UnicodeLetter
     *      $
     *      _
     *      \ UnicodeEscapeSequence
     *
     *  IdentifierStart production has one multi-character production:
     *
     *    \ UnicodeEscapeSequence
     *
     *  The '\' character is -not- matched by this function.  Rather, the caller
     *  should decode the escape and then call this function to check whether the
     *  decoded character is acceptable (see discussion in E5 Section 7.6).
     *
     *  The "UnicodeLetter" alternative of the production allows letters
     *  from various Unicode categories.  These can be extracted with the
     *  "tools/extract_chars.py" script.
     *
     *  Because the result has hundreds of Unicode codepoint ranges, matching
     *  for any values >= 0x80 are done using a very slow range-by-range scan
     *  and a packed range format.
     *
     *  The ASCII portion (codepoints 0x00 ... 0x7f) is fast-pathed below because
     *  it matters the most.  The ASCII related ranges of IdentifierStart are:
     *
     *    0x0041 ... 0x005a     ['A' ... 'Z']
     *    0x0061 ... 0x007a     ['a' ... 'z']
     *    0x0024                ['$']
     *    0x005f                ['_']
     */

    /* ASCII (and EOF) fast path -- quick accept and reject */
    if (cp <= 0x7fL) {
#if defined(DUK_USE_IDCHAR_FASTPATH)
        return (cp >= 0) && (duk_is_idchar_tab[cp] > 0);
#else
        if ((cp >= 'a' && cp <= 'z') ||
            (cp >= 'A' && cp <= 'Z') ||
            cp == '_' || cp == '$') {
            return 1;
        }
        return 0;
#endif
    }

    /* Non-ASCII slow path (range-by-range linear comparison), very slow */

#if defined(DUK_USE_SOURCE_NONBMP)
    if (duk__uni_range_match(duk_unicode_ids_noa,
                             (duk_size_t) sizeof(duk_unicode_ids_noa),
                             (duk_codepoint_t) cp)) {
        return 1;
    }
    return 0;
#else
    if (cp < 0x10000L) {
        if (duk__uni_range_match(duk_unicode_ids_noabmp,
                                 sizeof(duk_unicode_ids_noabmp),
                                 (duk_codepoint_t) cp)) {
            return 1;
        }
        return 0;
    } else {
        /* without explicit non-BMP support, assume non-BMP characters
         * are always accepted as identifier characters.
         */
        return 1;
    }
#endif
}

/*
 *  "IdentifierPart" production check.
 */

DUK_INTERNAL duk_small_int_t duk_unicode_is_identifier_part(duk_codepoint_t cp) {
    /*
     *  E5 Section 7.6:
     *
     *    IdentifierPart:
     *      IdentifierStart
     *      UnicodeCombiningMark
     *      UnicodeDigit
     *      UnicodeConnectorPunctuation
     *      <ZWNJ>  [U+200C]
     *      <ZWJ>   [U+200D]
     *
     *  IdentifierPart production has one multi-character production
     *  as part of its IdentifierStart alternative.  The '\' character
     *  of an escape sequence is not matched here, see discussion in
     *  duk_unicode_is_identifier_start().
     *
     *  To match non-ASCII characters (codepoints >= 0x80), a very slow
     *  linear range-by-range scan is used.  The codepoint is first compared
     *  to the IdentifierStart ranges, and if it doesn't match, then to a
     *  set consisting of code points in IdentifierPart but not in
     *  IdentifierStart.  This is done to keep the unicode range data small,
     *  at the expense of speed.
     *
     *  The ASCII fast path consists of:
     *
     *    0x0030 ... 0x0039     ['0' ... '9', UnicodeDigit]
     *    0x0041 ... 0x005a     ['A' ... 'Z', IdentifierStart]
     *    0x0061 ... 0x007a     ['a' ... 'z', IdentifierStart]
     *    0x0024                ['$', IdentifierStart]
     *    0x005f                ['_', IdentifierStart and
     *                                UnicodeConnectorPunctuation]
     *
     *  UnicodeCombiningMark has no code points <= 0x7f.
     *
     *  The matching code reuses the "identifier start" tables, and then
     *  consults a separate range set for characters in "identifier part"
     *  but not in "identifier start".  These can be extracted with the
     *  "tools/extract_chars.py" script.
     *
     *  UnicodeCombiningMark -> categories Mn, Mc
     *  UnicodeDigit -> categories Nd
     *  UnicodeConnectorPunctuation -> categories Pc
     */

    /* ASCII (and EOF) fast path -- quick accept and reject */
    if (cp <= 0x7fL) {
#if defined(DUK_USE_IDCHAR_FASTPATH)
        return (cp >= 0) && (duk_is_idchar_tab[cp] != 0);
#else
        if ((cp >= 'a' && cp <= 'z') ||
            (cp >= 'A' && cp <= 'Z') ||
            (cp >= '0' && cp <= '9') ||
            cp == '_' || cp == '$') {
            return 1;
        }
        return 0;
#endif
    }

    /* Non-ASCII slow path (range-by-range linear comparison), very slow */

#if defined(DUK_USE_SOURCE_NONBMP)
    if (duk__uni_range_match(duk_unicode_ids_noa,
                             sizeof(duk_unicode_ids_noa),
                             (duk_codepoint_t) cp) ||
        duk__uni_range_match(duk_unicode_idp_m_ids_noa,
                             sizeof(duk_unicode_idp_m_ids_noa),
                             (duk_codepoint_t) cp)) {
        return 1;
    }
    return 0;
#else
    if (cp < 0x10000L) {
        if (duk__uni_range_match(duk_unicode_ids_noabmp,
                                 sizeof(duk_unicode_ids_noabmp),
                                 (duk_codepoint_t) cp) ||
            duk__uni_range_match(duk_unicode_idp_m_ids_noabmp,
                                 sizeof(duk_unicode_idp_m_ids_noabmp),
                                 (duk_codepoint_t) cp)) {
            return 1;
        }
        return 0;
    } else {
        /* without explicit non-BMP support, assume non-BMP characters
         * are always accepted as identifier characters.
         */
        return 1;
    }
#endif
}

/*
 *  Unicode letter check.
 */

DUK_INTERNAL duk_small_int_t duk_unicode_is_letter(duk_codepoint_t cp) {
    /*
     *  Unicode letter is now taken to be the categories:
     *
     *    Lu, Ll, Lt, Lm, Lo
     *
     *  (Not sure if this is exactly correct.)
     *
     *  The ASCII fast path consists of:
     *
     *    0x0041 ... 0x005a     ['A' ... 'Z']
     *    0x0061 ... 0x007a     ['a' ... 'z']
     */

    /* ASCII (and EOF) fast path -- quick accept and reject */
    if (cp <= 0x7fL) {
        if ((cp >= 'a' && cp <= 'z') ||
            (cp >= 'A' && cp <= 'Z')) {
            return 1;
        }
        return 0;
    }

    /* Non-ASCII slow path (range-by-range linear comparison), very slow */

#if defined(DUK_USE_SOURCE_NONBMP)
    if (duk__uni_range_match(duk_unicode_ids_noa,
                             sizeof(duk_unicode_ids_noa),
                             (duk_codepoint_t) cp) &&
        !duk__uni_range_match(duk_unicode_ids_m_let_noa,
                              sizeof(duk_unicode_ids_m_let_noa),
                              (duk_codepoint_t) cp)) {
        return 1;
    }
    return 0;
#else
    if (cp < 0x10000L) {
        if (duk__uni_range_match(duk_unicode_ids_noabmp,
                                 sizeof(duk_unicode_ids_noabmp),
                                 (duk_codepoint_t) cp) &&
            !duk__uni_range_match(duk_unicode_ids_m_let_noabmp,
                                  sizeof(duk_unicode_ids_m_let_noabmp),
                                  (duk_codepoint_t) cp)) {
            return 1;
        }
        return 0;
    } else {
        /* without explicit non-BMP support, assume non-BMP characters
         * are always accepted as letters.
         */
        return 1;
    }
#endif
}

/*
 *  Complex case conversion helper which decodes a bit-packed conversion
 *  control stream generated by tools/extract_caseconv.py.  The conversion
 *  is very slow because it runs through the conversion data in a linear
 *  fashion to save space (which is why ASCII characters have a special
 *  fast path before arriving here).
 *
 *  The particular bit counts etc have been determined experimentally to
 *  be small but still sufficient, and must match the Python script
 *  (tools/extract_caseconv.py).
 *
 *  The return value is the case converted codepoint or -1 if the conversion
 *  results in multiple characters (this is useful for regexp Canonicalization
 *  operation).  If 'buf' is not NULL, the result codepoint(s) are also
 *  appended to the hbuffer.
 *
 *  Context and locale specific rules must be checked before consulting
 *  this function.
 */

DUK_LOCAL
duk_codepoint_t duk__slow_case_conversion(duk_hthread *thr,
                                          duk_bufwriter_ctx *bw,
                                          duk_codepoint_t cp,
                                          duk_bitdecoder_ctx *bd_ctx) {
    duk_small_int_t skip = 0;
    duk_small_int_t n;
    duk_small_int_t t;
    duk_small_int_t count;
    duk_codepoint_t tmp_cp;
    duk_codepoint_t start_i;
    duk_codepoint_t start_o;

    DUK_ASSERT(bd_ctx != NULL);
    DUK_UNREF(thr);

    DUK_DDD(DUK_DDDPRINT("slow case conversion for codepoint: %ld", (long) cp));

    /* range conversion with a "skip" */
    DUK_DDD(DUK_DDDPRINT("checking ranges"));
    for (;;) {
        skip++;
        n = (duk_small_int_t) duk_bd_decode(bd_ctx, 6);
        if (n == 0x3f) {
            /* end marker */
            break;
        }
        DUK_DDD(DUK_DDDPRINT("skip=%ld, n=%ld", (long) skip, (long) n));

        while (n--) {
            start_i = (duk_codepoint_t) duk_bd_decode(bd_ctx, 16);
            start_o = (duk_codepoint_t) duk_bd_decode(bd_ctx, 16);
            count = (duk_small_int_t) duk_bd_decode(bd_ctx, 7);
            DUK_DDD(DUK_DDDPRINT("range: start_i=%ld, start_o=%ld, count=%ld, skip=%ld",
                                 (long) start_i, (long) start_o, (long) count, (long) skip));

            if (cp >= start_i) {
                tmp_cp = cp - start_i;  /* always >= 0 */
                if (tmp_cp < (duk_codepoint_t) count * (duk_codepoint_t) skip &&
                    (tmp_cp % (duk_codepoint_t) skip) == 0) {
                    DUK_DDD(DUK_DDDPRINT("range matches input codepoint"));
                    cp = start_o + tmp_cp;
                    goto single;
                }
            }
        }
    }

    /* 1:1 conversion */
    n = (duk_small_int_t) duk_bd_decode(bd_ctx, 7);
    DUK_DDD(DUK_DDDPRINT("checking 1:1 conversions (count %ld)", (long) n));
    while (n--) {
        start_i = (duk_codepoint_t) duk_bd_decode(bd_ctx, 16);
        start_o = (duk_codepoint_t) duk_bd_decode(bd_ctx, 16);
        DUK_DDD(DUK_DDDPRINT("1:1 conversion %ld -> %ld", (long) start_i, (long) start_o));
        if (cp == start_i) {
            DUK_DDD(DUK_DDDPRINT("1:1 matches input codepoint"));
            cp = start_o;
            goto single;
        }
    }

    /* complex, multicharacter conversion */
    n = (duk_small_int_t) duk_bd_decode(bd_ctx, 7);
    DUK_DDD(DUK_DDDPRINT("checking 1:n conversions (count %ld)", (long) n));
    while (n--) {
        start_i = (duk_codepoint_t) duk_bd_decode(bd_ctx, 16);
        t = (duk_small_int_t) duk_bd_decode(bd_ctx, 2);
        DUK_DDD(DUK_DDDPRINT("1:n conversion %ld -> %ld chars", (long) start_i, (long) t));
        if (cp == start_i) {
            DUK_DDD(DUK_DDDPRINT("1:n matches input codepoint"));
            if (bw != NULL) {
                while (t--) {
                    tmp_cp = (duk_codepoint_t) duk_bd_decode(bd_ctx, 16);
                    DUK_BW_WRITE_RAW_XUTF8(thr, bw, (duk_ucodepoint_t) tmp_cp);
                }
            }
            return -1;
        } else {
            while (t--) {
                (void) duk_bd_decode(bd_ctx, 16);
            }
        }
    }

    /* default: no change */
    DUK_DDD(DUK_DDDPRINT("no rule matches, output is same as input"));
    /* fall through */

 single:
    if (bw != NULL) {
        DUK_BW_WRITE_RAW_XUTF8(thr, bw, (duk_ucodepoint_t) cp);
    }
    return cp;
}

/*
 *  Case conversion helper, with context/local sensitivity.
 *  For proper case conversion, one needs to know the character
 *  and the preceding and following characters, as well as
 *  locale/language.
 */

/* XXX: add 'language' argument when locale/language sensitive rule
 * support added.
 */
DUK_LOCAL
duk_codepoint_t duk__case_transform_helper(duk_hthread *thr,
                                           duk_bufwriter_ctx *bw,
                                           duk_codepoint_t cp,
                                           duk_codepoint_t prev,
                                           duk_codepoint_t next,
                                           duk_bool_t uppercase) {
    duk_bitdecoder_ctx bd_ctx;

    /* fast path for ASCII */
    if (cp < 0x80L) {
        /* XXX: there are language sensitive rules for the ASCII range.
         * If/when language/locale support is implemented, they need to
         * be implemented here for the fast path.  There are no context
         * sensitive rules for ASCII range.
         */

        if (uppercase) {
            if (cp >= 'a' && cp <= 'z') {
                cp = cp - 'a' + 'A';
            }
        } else {
            if (cp >= 'A' && cp <= 'Z') {
                cp = cp - 'A' + 'a';
            }
        }

        if (bw != NULL) {
            DUK_BW_WRITE_RAW_U8(thr, bw, (duk_uint8_t) cp);
        }
        return cp;
    }

    /* context and locale specific rules which cannot currently be represented
     * in the caseconv bitstream: hardcoded rules in C
     */
    if (uppercase) {
        /* XXX: turkish / azeri */
    } else {
        /*
         *  Final sigma context specific rule.  This is a rather tricky
         *  rule and this handling is probably not 100% correct now.
         *  The rule is not locale/language specific so it is supported.
         */

        if (cp == 0x03a3L &&    /* U+03A3 = GREEK CAPITAL LETTER SIGMA */
            duk_unicode_is_letter(prev) &&        /* prev exists and is not a letter */
            !duk_unicode_is_letter(next)) {       /* next does not exist or next is not a letter */
            /* Capital sigma occurred at "end of word", lowercase to
             * U+03C2 = GREEK SMALL LETTER FINAL SIGMA.  Otherwise
             * fall through and let the normal rules lowercase it to
             * U+03C3 = GREEK SMALL LETTER SIGMA.
             */
            cp = 0x03c2L;
            goto singlechar;
        }

        /* XXX: lithuanian not implemented */
        /* XXX: lithuanian, explicit dot rules */
        /* XXX: turkish / azeri, lowercase rules */
    }

    /* 1:1 or special conversions, but not locale/context specific: script generated rules */
    duk_memzero(&bd_ctx, sizeof(bd_ctx));
    if (uppercase) {
        bd_ctx.data = (const duk_uint8_t *) duk_unicode_caseconv_uc;
        bd_ctx.length = (duk_size_t) sizeof(duk_unicode_caseconv_uc);
    } else {
        bd_ctx.data = (const duk_uint8_t *) duk_unicode_caseconv_lc;
        bd_ctx.length = (duk_size_t) sizeof(duk_unicode_caseconv_lc);
    }
    return duk__slow_case_conversion(thr, bw, cp, &bd_ctx);

 singlechar:
    if (bw != NULL) {
        DUK_BW_WRITE_RAW_XUTF8(thr, bw, (duk_ucodepoint_t) cp);
    }
    return cp;

 /* unused now, not needed until Turkish/Azeri */
#if 0
 nochar:
    return -1;
#endif
}

/*
 *  Replace valstack top with case converted version.
 */

DUK_INTERNAL void duk_unicode_case_convert_string(duk_hthread *thr, duk_bool_t uppercase) {
    duk_hstring *h_input;
    duk_bufwriter_ctx bw_alloc;
    duk_bufwriter_ctx *bw;
    const duk_uint8_t *p, *p_start, *p_end;
    duk_codepoint_t prev, curr, next;

    h_input = duk_require_hstring(thr, -1);  /* Accept symbols. */
    DUK_ASSERT(h_input != NULL);

    bw = &bw_alloc;
    DUK_BW_INIT_PUSHBUF(thr, bw, DUK_HSTRING_GET_BYTELEN(h_input));

    /* [ ... input buffer ] */

    p_start = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_input);
    p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_input);
    p = p_start;

    prev = -1; DUK_UNREF(prev);
    curr = -1;
    next = -1;
    for (;;) {
        prev = curr;
        curr = next;
        next = -1;
        if (p < p_end) {
            next = (duk_codepoint_t) duk_unicode_decode_xutf8_checked(thr, &p, p_start, p_end);
        } else {
            /* end of input and last char has been processed */
            if (curr < 0) {
                break;
            }
        }

        /* on first round, skip */
        if (curr >= 0) {
            /* XXX: could add a fast path to process chunks of input codepoints,
             * but relative benefit would be quite small.
             */

            /* Ensure space for maximum multi-character result; estimate is overkill. */
            DUK_BW_ENSURE(thr, bw, 8 * DUK_UNICODE_MAX_XUTF8_LENGTH);

            duk__case_transform_helper(thr,
                                       bw,
                                       (duk_codepoint_t) curr,
                                       prev,
                                       next,
                                       uppercase);
        }
    }

    DUK_BW_COMPACT(thr, bw);
    (void) duk_buffer_to_string(thr, -1);  /* Safe, output is encoded. */
    /* invalidates h_buf pointer */
    duk_remove_m2(thr);
}

#if defined(DUK_USE_REGEXP_SUPPORT)

/*
 *  Canonicalize() abstract operation needed for canonicalization of individual
 *  codepoints during regexp compilation and execution, see E5 Section 15.10.2.8.
 *  Note that codepoints are canonicalized one character at a time, so no context
 *  specific rules can apply.  Locale specific rules can apply, though.
 */

DUK_INTERNAL duk_codepoint_t duk_unicode_re_canonicalize_char(duk_hthread *thr, duk_codepoint_t cp) {
#if defined(DUK_USE_REGEXP_CANON_WORKAROUND)
    /* Fast canonicalization lookup at the cost of 128kB footprint. */
    DUK_ASSERT(cp >= 0);
    DUK_UNREF(thr);
    if (DUK_LIKELY(cp < 0x10000L)) {
        return (duk_codepoint_t) duk_unicode_re_canon_lookup[cp];
    }
    return cp;
#else  /* DUK_USE_REGEXP_CANON_WORKAROUND */
    duk_codepoint_t y;

    y = duk__case_transform_helper(thr,
                                   NULL,    /* NULL is allowed, no output */
                                   cp,      /* curr char */
                                   -1,      /* prev char */
                                   -1,      /* next char */
                                   1);      /* uppercase */

    if ((y < 0) || (cp >= 0x80 && y < 0x80)) {
        /* multiple codepoint conversion or non-ASCII mapped to ASCII
         * --> leave as is.
         */
        return cp;
    }

    return y;
#endif  /* DUK_USE_REGEXP_CANON_WORKAROUND */
}

/*
 *  E5 Section 15.10.2.6 "IsWordChar" abstract operation.  Assume
 *  x < 0 for characters read outside the string.
 */

DUK_INTERNAL duk_small_int_t duk_unicode_re_is_wordchar(duk_codepoint_t x) {
    /*
     *  Note: the description in E5 Section 15.10.2.6 has a typo, it
     *  contains 'A' twice and lacks 'a'; the intent is [0-9a-zA-Z_].
     */
    if ((x >= '0' && x <= '9') ||
        (x >= 'a' && x <= 'z') ||
        (x >= 'A' && x <= 'Z') ||
        (x == '_')) {
        return 1;
    }
    return 0;
}

/*
 *  Regexp range tables
 */

/* exposed because lexer needs these too */
DUK_INTERNAL const duk_uint16_t duk_unicode_re_ranges_digit[2] = {
    (duk_uint16_t) 0x0030UL, (duk_uint16_t) 0x0039UL,
};
DUK_INTERNAL const duk_uint16_t duk_unicode_re_ranges_white[22] = {
    (duk_uint16_t) 0x0009UL, (duk_uint16_t) 0x000DUL,
    (duk_uint16_t) 0x0020UL, (duk_uint16_t) 0x0020UL,
    (duk_uint16_t) 0x00A0UL, (duk_uint16_t) 0x00A0UL,
    (duk_uint16_t) 0x1680UL, (duk_uint16_t) 0x1680UL,
    (duk_uint16_t) 0x180EUL, (duk_uint16_t) 0x180EUL,
    (duk_uint16_t) 0x2000UL, (duk_uint16_t) 0x200AUL,
    (duk_uint16_t) 0x2028UL, (duk_uint16_t) 0x2029UL,
    (duk_uint16_t) 0x202FUL, (duk_uint16_t) 0x202FUL,
    (duk_uint16_t) 0x205FUL, (duk_uint16_t) 0x205FUL,
    (duk_uint16_t) 0x3000UL, (duk_uint16_t) 0x3000UL,
    (duk_uint16_t) 0xFEFFUL, (duk_uint16_t) 0xFEFFUL,
};
DUK_INTERNAL const duk_uint16_t duk_unicode_re_ranges_wordchar[8] = {
    (duk_uint16_t) 0x0030UL, (duk_uint16_t) 0x0039UL,
    (duk_uint16_t) 0x0041UL, (duk_uint16_t) 0x005AUL,
    (duk_uint16_t) 0x005FUL, (duk_uint16_t) 0x005FUL,
    (duk_uint16_t) 0x0061UL, (duk_uint16_t) 0x007AUL,
};
DUK_INTERNAL const duk_uint16_t duk_unicode_re_ranges_not_digit[4] = {
    (duk_uint16_t) 0x0000UL, (duk_uint16_t) 0x002FUL,
    (duk_uint16_t) 0x003AUL, (duk_uint16_t) 0xFFFFUL,
};
DUK_INTERNAL const duk_uint16_t duk_unicode_re_ranges_not_white[24] = {
    (duk_uint16_t) 0x0000UL, (duk_uint16_t) 0x0008UL,
    (duk_uint16_t) 0x000EUL, (duk_uint16_t) 0x001FUL,
    (duk_uint16_t) 0x0021UL, (duk_uint16_t) 0x009FUL,
    (duk_uint16_t) 0x00A1UL, (duk_uint16_t) 0x167FUL,
    (duk_uint16_t) 0x1681UL, (duk_uint16_t) 0x180DUL,
    (duk_uint16_t) 0x180FUL, (duk_uint16_t) 0x1FFFUL,
    (duk_uint16_t) 0x200BUL, (duk_uint16_t) 0x2027UL,
    (duk_uint16_t) 0x202AUL, (duk_uint16_t) 0x202EUL,
    (duk_uint16_t) 0x2030UL, (duk_uint16_t) 0x205EUL,
    (duk_uint16_t) 0x2060UL, (duk_uint16_t) 0x2FFFUL,
    (duk_uint16_t) 0x3001UL, (duk_uint16_t) 0xFEFEUL,
    (duk_uint16_t) 0xFF00UL, (duk_uint16_t) 0xFFFFUL,
};
DUK_INTERNAL const duk_uint16_t duk_unicode_re_ranges_not_wordchar[10] = {
    (duk_uint16_t) 0x0000UL, (duk_uint16_t) 0x002FUL,
    (duk_uint16_t) 0x003AUL, (duk_uint16_t) 0x0040UL,
    (duk_uint16_t) 0x005BUL, (duk_uint16_t) 0x005EUL,
    (duk_uint16_t) 0x0060UL, (duk_uint16_t) 0x0060UL,
    (duk_uint16_t) 0x007BUL, (duk_uint16_t) 0xFFFFUL,
};

#endif  /* DUK_USE_REGEXP_SUPPORT */
//#line 1 "duk_util_memrw.c"
/*
 *  Macro support functions for reading/writing raw data.
 *
 *  These are done using memcpy to ensure they're valid even for unaligned
 *  reads/writes on platforms where alignment counts.  On x86 at least gcc
 *  is able to compile these into a bswap+mov.  "Always inline" is used to
 *  ensure these macros compile to minimal code.
 */

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

union duk__u16_union {
    duk_uint8_t b[2];
    duk_uint16_t x;
};
typedef union duk__u16_union duk__u16_union;

union duk__u32_union {
    duk_uint8_t b[4];
    duk_uint32_t x;
};
typedef union duk__u32_union duk__u32_union;

#if defined(DUK_USE_64BIT_OPS)
union duk__u64_union {
    duk_uint8_t b[8];
    duk_uint64_t x;
};
typedef union duk__u64_union duk__u64_union;
#endif

DUK_INTERNAL DUK_ALWAYS_INLINE duk_uint16_t duk_raw_read_u16_be(const duk_uint8_t *p) {
    duk__u16_union u;
    duk_memcpy((void *) u.b, (const void *) p, (size_t) 2);
    u.x = DUK_NTOH16(u.x);
    return u.x;
}

DUK_INTERNAL DUK_ALWAYS_INLINE duk_uint32_t duk_raw_read_u32_be(const duk_uint8_t *p) {
    duk__u32_union u;
    duk_memcpy((void *) u.b, (const void *) p, (size_t) 4);
    u.x = DUK_NTOH32(u.x);
    return u.x;
}

DUK_INTERNAL DUK_ALWAYS_INLINE duk_float_t duk_raw_read_float_be(const duk_uint8_t *p) {
    duk_float_union fu;
    duk_memcpy((void *) fu.uc, (const void *) p, (size_t) 4);
    duk_fltunion_big_to_host(&fu);
    return fu.f;
}

DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t duk_raw_read_double_be(const duk_uint8_t *p) {
    duk_double_union du;
    duk_memcpy((void *) du.uc, (const void *) p, (size_t) 8);
    duk_dblunion_big_to_host(&du);
    return du.d;
}

DUK_INTERNAL DUK_ALWAYS_INLINE duk_uint16_t duk_raw_readinc_u16_be(const duk_uint8_t **p) {
    duk_uint16_t res = duk_raw_read_u16_be(*p);
    *p += 2;
    return res;
}

DUK_INTERNAL DUK_ALWAYS_INLINE duk_uint32_t duk_raw_readinc_u32_be(const duk_uint8_t **p) {
    duk_uint32_t res = duk_raw_read_u32_be(*p);
    *p += 4;
    return res;
}

DUK_INTERNAL DUK_ALWAYS_INLINE duk_float_t duk_raw_readinc_float_be(const duk_uint8_t **p) {
    duk_float_t res = duk_raw_read_float_be(*p);
    *p += 4;
    return res;
}

DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t duk_raw_readinc_double_be(const duk_uint8_t **p) {
    duk_double_t res = duk_raw_read_double_be(*p);
    *p += 8;
    return res;
}

DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_write_u16_be(duk_uint8_t *p, duk_uint16_t val) {
    duk__u16_union u;
    u.x = DUK_HTON16(val);
    duk_memcpy((void *) p, (const void *) u.b, (size_t) 2);
}

DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_write_u32_be(duk_uint8_t *p, duk_uint32_t val) {
    duk__u32_union u;
    u.x = DUK_HTON32(val);
    duk_memcpy((void *) p, (const void *) u.b, (size_t) 4);
}

DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_write_float_be(duk_uint8_t *p, duk_float_t val) {
    duk_float_union fu;
    fu.f = val;
    duk_fltunion_host_to_big(&fu);
    duk_memcpy((void *) p, (const void *) fu.uc, (size_t) 4);
}

DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_write_double_be(duk_uint8_t *p, duk_double_t val) {
    duk_double_union du;
    du.d = val;
    duk_dblunion_host_to_big(&du);
    duk_memcpy((void *) p, (const void *) du.uc, (size_t) 8);
}

DUK_INTERNAL duk_small_int_t duk_raw_write_xutf8(duk_uint8_t *p, duk_ucodepoint_t val) {
    duk_small_int_t len = duk_unicode_encode_xutf8(val, p);
    return len;
}

DUK_INTERNAL duk_small_int_t duk_raw_write_cesu8(duk_uint8_t *p, duk_ucodepoint_t val) {
    duk_small_int_t len = duk_unicode_encode_cesu8(val, p);
    return len;
}

DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_writeinc_u16_be(duk_uint8_t **p, duk_uint16_t val) {
    duk_raw_write_u16_be(*p, val);
    *p += 2;
}

DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_writeinc_u32_be(duk_uint8_t **p, duk_uint32_t val) {
    duk_raw_write_u32_be(*p, val);
    *p += 4;
}

DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_writeinc_float_be(duk_uint8_t **p, duk_float_t val) {
    duk_raw_write_float_be(*p, val);
    *p += 4;
}

DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_writeinc_double_be(duk_uint8_t **p, duk_double_t val) {
    duk_raw_write_double_be(*p, val);
    *p += 8;
}

DUK_INTERNAL void duk_raw_writeinc_xutf8(duk_uint8_t **p, duk_ucodepoint_t val) {
    duk_small_int_t len = duk_unicode_encode_xutf8(val, *p);
    *p += len;
}

DUK_INTERNAL void duk_raw_writeinc_cesu8(duk_uint8_t **p, duk_ucodepoint_t val) {
    duk_small_int_t len = duk_unicode_encode_cesu8(val, *p);
    *p += len;
}
//#line 1 "duk_util_misc.c"
/*
 *  Misc util stuff.
 */

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

/*
 *  Lowercase digits for radix values 2 to 36.  Also doubles as lowercase
 *  hex nybble table.
 */

DUK_INTERNAL const duk_uint8_t duk_lc_digits[36] = {
    DUK_ASC_0, DUK_ASC_1, DUK_ASC_2, DUK_ASC_3,
    DUK_ASC_4, DUK_ASC_5, DUK_ASC_6, DUK_ASC_7,
    DUK_ASC_8, DUK_ASC_9, DUK_ASC_LC_A, DUK_ASC_LC_B,
    DUK_ASC_LC_C, DUK_ASC_LC_D, DUK_ASC_LC_E, DUK_ASC_LC_F,
    DUK_ASC_LC_G, DUK_ASC_LC_H, DUK_ASC_LC_I, DUK_ASC_LC_J,
    DUK_ASC_LC_K, DUK_ASC_LC_L, DUK_ASC_LC_M, DUK_ASC_LC_N,
    DUK_ASC_LC_O, DUK_ASC_LC_P, DUK_ASC_LC_Q, DUK_ASC_LC_R,
    DUK_ASC_LC_S, DUK_ASC_LC_T, DUK_ASC_LC_U, DUK_ASC_LC_V,
    DUK_ASC_LC_W, DUK_ASC_LC_X, DUK_ASC_LC_Y, DUK_ASC_LC_Z
};

DUK_INTERNAL const duk_uint8_t duk_uc_nybbles[16] = {
    DUK_ASC_0, DUK_ASC_1, DUK_ASC_2, DUK_ASC_3,
    DUK_ASC_4, DUK_ASC_5, DUK_ASC_6, DUK_ASC_7,
    DUK_ASC_8, DUK_ASC_9, DUK_ASC_UC_A, DUK_ASC_UC_B,
    DUK_ASC_UC_C, DUK_ASC_UC_D, DUK_ASC_UC_E, DUK_ASC_UC_F
};

/*
 *  Table for hex decoding ASCII hex digits
 */

DUK_INTERNAL const duk_int8_t duk_hex_dectab[256] = {
    /* -1 if invalid */
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  /* 0x00-0x0f */
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  /* 0x10-0x1f */
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  /* 0x20-0x2f */
     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1,  /* 0x30-0x3f */
    -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,  /* 0x40-0x4f */
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  /* 0x50-0x5f */
    -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,  /* 0x60-0x6f */
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  /* 0x70-0x7f */
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  /* 0x80-0x8f */
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  /* 0x90-0x9f */
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  /* 0xa0-0xaf */
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  /* 0xb0-0xbf */
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  /* 0xc0-0xcf */
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  /* 0xd0-0xdf */
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  /* 0xe0-0xef */
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1   /* 0xf0-0xff */
};

#if defined(DUK_USE_HEX_FASTPATH)
/* Preshifted << 4.  Must use 16-bit entry to allow negative value signaling. */
DUK_INTERNAL const duk_int16_t duk_hex_dectab_shift4[256] = {
      -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  /* 0x00-0x0f */
      -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  /* 0x10-0x1f */
      -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  /* 0x20-0x2f */
    0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90,   -1,   -1,   -1,   -1,   -1,   -1,  /* 0x30-0x3f */
      -1, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  /* 0x40-0x4f */
      -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  /* 0x50-0x5f */
      -1, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  /* 0x60-0x6f */
      -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  /* 0x70-0x7f */
      -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  /* 0x80-0x8f */
      -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  /* 0x90-0x9f */
      -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  /* 0xa0-0xaf */
      -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  /* 0xb0-0xbf */
      -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  /* 0xc0-0xcf */
      -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  /* 0xd0-0xdf */
      -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  /* 0xe0-0xef */
      -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1   /* 0xf0-0xff */
};
#endif

/*
 *  Table for hex encoding bytes
 */

#if defined(DUK_USE_HEX_FASTPATH)
/* Lookup to encode one byte directly into 2 characters:
 *
 *   def genhextab(bswap):
 *       for i in xrange(256):
 *           t = chr(i).encode('hex')
 *           if bswap:
 *               t = t[1] + t[0]
 *           print('0x' + t.encode('hex') + 'U')
 *   print('big endian'); genhextab(False)
 *   print('little endian'); genhextab(True)
*/
DUK_INTERNAL const duk_uint16_t duk_hex_enctab[256] = {
#if defined(DUK_USE_INTEGER_BE)
    0x3030U, 0x3031U, 0x3032U, 0x3033U, 0x3034U, 0x3035U, 0x3036U, 0x3037U,
    0x3038U, 0x3039U, 0x3061U, 0x3062U, 0x3063U, 0x3064U, 0x3065U, 0x3066U,
    0x3130U, 0x3131U, 0x3132U, 0x3133U, 0x3134U, 0x3135U, 0x3136U, 0x3137U,
    0x3138U, 0x3139U, 0x3161U, 0x3162U, 0x3163U, 0x3164U, 0x3165U, 0x3166U,
    0x3230U, 0x3231U, 0x3232U, 0x3233U, 0x3234U, 0x3235U, 0x3236U, 0x3237U,
    0x3238U, 0x3239U, 0x3261U, 0x3262U, 0x3263U, 0x3264U, 0x3265U, 0x3266U,
    0x3330U, 0x3331U, 0x3332U, 0x3333U, 0x3334U, 0x3335U, 0x3336U, 0x3337U,
    0x3338U, 0x3339U, 0x3361U, 0x3362U, 0x3363U, 0x3364U, 0x3365U, 0x3366U,
    0x3430U, 0x3431U, 0x3432U, 0x3433U, 0x3434U, 0x3435U, 0x3436U, 0x3437U,
    0x3438U, 0x3439U, 0x3461U, 0x3462U, 0x3463U, 0x3464U, 0x3465U, 0x3466U,
    0x3530U, 0x3531U, 0x3532U, 0x3533U, 0x3534U, 0x3535U, 0x3536U, 0x3537U,
    0x3538U, 0x3539U, 0x3561U, 0x3562U, 0x3563U, 0x3564U, 0x3565U, 0x3566U,
    0x3630U, 0x3631U, 0x3632U, 0x3633U, 0x3634U, 0x3635U, 0x3636U, 0x3637U,
    0x3638U, 0x3639U, 0x3661U, 0x3662U, 0x3663U, 0x3664U, 0x3665U, 0x3666U,
    0x3730U, 0x3731U, 0x3732U, 0x3733U, 0x3734U, 0x3735U, 0x3736U, 0x3737U,
    0x3738U, 0x3739U, 0x3761U, 0x3762U, 0x3763U, 0x3764U, 0x3765U, 0x3766U,
    0x3830U, 0x3831U, 0x3832U, 0x3833U, 0x3834U, 0x3835U, 0x3836U, 0x3837U,
    0x3838U, 0x3839U, 0x3861U, 0x3862U, 0x3863U, 0x3864U, 0x3865U, 0x3866U,
    0x3930U, 0x3931U, 0x3932U, 0x3933U, 0x3934U, 0x3935U, 0x3936U, 0x3937U,
    0x3938U, 0x3939U, 0x3961U, 0x3962U, 0x3963U, 0x3964U, 0x3965U, 0x3966U,
    0x6130U, 0x6131U, 0x6132U, 0x6133U, 0x6134U, 0x6135U, 0x6136U, 0x6137U,
    0x6138U, 0x6139U, 0x6161U, 0x6162U, 0x6163U, 0x6164U, 0x6165U, 0x6166U,
    0x6230U, 0x6231U, 0x6232U, 0x6233U, 0x6234U, 0x6235U, 0x6236U, 0x6237U,
    0x6238U, 0x6239U, 0x6261U, 0x6262U, 0x6263U, 0x6264U, 0x6265U, 0x6266U,
    0x6330U, 0x6331U, 0x6332U, 0x6333U, 0x6334U, 0x6335U, 0x6336U, 0x6337U,
    0x6338U, 0x6339U, 0x6361U, 0x6362U, 0x6363U, 0x6364U, 0x6365U, 0x6366U,
    0x6430U, 0x6431U, 0x6432U, 0x6433U, 0x6434U, 0x6435U, 0x6436U, 0x6437U,
    0x6438U, 0x6439U, 0x6461U, 0x6462U, 0x6463U, 0x6464U, 0x6465U, 0x6466U,
    0x6530U, 0x6531U, 0x6532U, 0x6533U, 0x6534U, 0x6535U, 0x6536U, 0x6537U,
    0x6538U, 0x6539U, 0x6561U, 0x6562U, 0x6563U, 0x6564U, 0x6565U, 0x6566U,
    0x6630U, 0x6631U, 0x6632U, 0x6633U, 0x6634U, 0x6635U, 0x6636U, 0x6637U,
    0x6638U, 0x6639U, 0x6661U, 0x6662U, 0x6663U, 0x6664U, 0x6665U, 0x6666U
#else  /* DUK_USE_INTEGER_BE */
    0x3030U, 0x3130U, 0x3230U, 0x3330U, 0x3430U, 0x3530U, 0x3630U, 0x3730U,
    0x3830U, 0x3930U, 0x6130U, 0x6230U, 0x6330U, 0x6430U, 0x6530U, 0x6630U,
    0x3031U, 0x3131U, 0x3231U, 0x3331U, 0x3431U, 0x3531U, 0x3631U, 0x3731U,
    0x3831U, 0x3931U, 0x6131U, 0x6231U, 0x6331U, 0x6431U, 0x6531U, 0x6631U,
    0x3032U, 0x3132U, 0x3232U, 0x3332U, 0x3432U, 0x3532U, 0x3632U, 0x3732U,
    0x3832U, 0x3932U, 0x6132U, 0x6232U, 0x6332U, 0x6432U, 0x6532U, 0x6632U,
    0x3033U, 0x3133U, 0x3233U, 0x3333U, 0x3433U, 0x3533U, 0x3633U, 0x3733U,
    0x3833U, 0x3933U, 0x6133U, 0x6233U, 0x6333U, 0x6433U, 0x6533U, 0x6633U,
    0x3034U, 0x3134U, 0x3234U, 0x3334U, 0x3434U, 0x3534U, 0x3634U, 0x3734U,
    0x3834U, 0x3934U, 0x6134U, 0x6234U, 0x6334U, 0x6434U, 0x6534U, 0x6634U,
    0x3035U, 0x3135U, 0x3235U, 0x3335U, 0x3435U, 0x3535U, 0x3635U, 0x3735U,
    0x3835U, 0x3935U, 0x6135U, 0x6235U, 0x6335U, 0x6435U, 0x6535U, 0x6635U,
    0x3036U, 0x3136U, 0x3236U, 0x3336U, 0x3436U, 0x3536U, 0x3636U, 0x3736U,
    0x3836U, 0x3936U, 0x6136U, 0x6236U, 0x6336U, 0x6436U, 0x6536U, 0x6636U,
    0x3037U, 0x3137U, 0x3237U, 0x3337U, 0x3437U, 0x3537U, 0x3637U, 0x3737U,
    0x3837U, 0x3937U, 0x6137U, 0x6237U, 0x6337U, 0x6437U, 0x6537U, 0x6637U,
    0x3038U, 0x3138U, 0x3238U, 0x3338U, 0x3438U, 0x3538U, 0x3638U, 0x3738U,
    0x3838U, 0x3938U, 0x6138U, 0x6238U, 0x6338U, 0x6438U, 0x6538U, 0x6638U,
    0x3039U, 0x3139U, 0x3239U, 0x3339U, 0x3439U, 0x3539U, 0x3639U, 0x3739U,
    0x3839U, 0x3939U, 0x6139U, 0x6239U, 0x6339U, 0x6439U, 0x6539U, 0x6639U,
    0x3061U, 0x3161U, 0x3261U, 0x3361U, 0x3461U, 0x3561U, 0x3661U, 0x3761U,
    0x3861U, 0x3961U, 0x6161U, 0x6261U, 0x6361U, 0x6461U, 0x6561U, 0x6661U,
    0x3062U, 0x3162U, 0x3262U, 0x3362U, 0x3462U, 0x3562U, 0x3662U, 0x3762U,
    0x3862U, 0x3962U, 0x6162U, 0x6262U, 0x6362U, 0x6462U, 0x6562U, 0x6662U,
    0x3063U, 0x3163U, 0x3263U, 0x3363U, 0x3463U, 0x3563U, 0x3663U, 0x3763U,
    0x3863U, 0x3963U, 0x6163U, 0x6263U, 0x6363U, 0x6463U, 0x6563U, 0x6663U,
    0x3064U, 0x3164U, 0x3264U, 0x3364U, 0x3464U, 0x3564U, 0x3664U, 0x3764U,
    0x3864U, 0x3964U, 0x6164U, 0x6264U, 0x6364U, 0x6464U, 0x6564U, 0x6664U,
    0x3065U, 0x3165U, 0x3265U, 0x3365U, 0x3465U, 0x3565U, 0x3665U, 0x3765U,
    0x3865U, 0x3965U, 0x6165U, 0x6265U, 0x6365U, 0x6465U, 0x6565U, 0x6665U,
    0x3066U, 0x3166U, 0x3266U, 0x3366U, 0x3466U, 0x3566U, 0x3666U, 0x3766U,
    0x3866U, 0x3966U, 0x6166U, 0x6266U, 0x6366U, 0x6466U, 0x6566U, 0x6666U
#endif  /* DUK_USE_INTEGER_BE */
};
#endif  /* DUK_USE_HEX_FASTPATH */

/*
 *  Arbitrary byteswap for potentially unaligned values
 *
 *  Used to byteswap pointers e.g. in debugger code.
 */

#if defined(DUK_USE_DEBUGGER_SUPPORT)  /* For now only needed by the debugger. */
DUK_INTERNAL void duk_byteswap_bytes(duk_uint8_t *p, duk_small_uint_t len) {
    duk_uint8_t tmp;
    duk_uint8_t *q = p + len - 1;

    while (p - q < 0) {
        tmp = *p;
        *p = *q;
        *q = tmp;
        p++;
        q--;
    }
}
#endif
//#line 1 "duk_hobject_class.c"
/*
 *  Hobject ECMAScript [[Class]].
 */

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

#if (DUK_STRIDX_UC_ARGUMENTS > 255)
#error constant too large
#endif
#if (DUK_STRIDX_UC_ARRAY > 255)
#error constant too large
#endif
#if (DUK_STRIDX_UC_BOOLEAN > 255)
#error constant too large
#endif
#if (DUK_STRIDX_UC_DATE > 255)
#error constant too large
#endif
#if (DUK_STRIDX_UC_ERROR > 255)
#error constant too large
#endif
#if (DUK_STRIDX_UC_FUNCTION > 255)
#error constant too large
#endif
#if (DUK_STRIDX_JSON > 255)
#error constant too large
#endif
#if (DUK_STRIDX_MATH > 255)
#error constant too large
#endif
#if (DUK_STRIDX_UC_NUMBER > 255)
#error constant too large
#endif
#if (DUK_STRIDX_UC_OBJECT > 255)
#error constant too large
#endif
#if (DUK_STRIDX_REG_EXP > 255)
#error constant too large
#endif
#if (DUK_STRIDX_UC_STRING > 255)
#error constant too large
#endif
#if (DUK_STRIDX_GLOBAL > 255)
#error constant too large
#endif
#if (DUK_STRIDX_OBJ_ENV > 255)
#error constant too large
#endif
#if (DUK_STRIDX_DEC_ENV > 255)
#error constant too large
#endif
#if (DUK_STRIDX_UC_POINTER > 255)
#error constant too large
#endif
#if (DUK_STRIDX_UC_THREAD > 255)
#error constant too large
#endif
#if (DUK_STRIDX_ARRAY_BUFFER > 255)
#error constant too large
#endif
#if (DUK_STRIDX_DATA_VIEW > 255)
#error constant too large
#endif
#if (DUK_STRIDX_INT8_ARRAY > 255)
#error constant too large
#endif
#if (DUK_STRIDX_UINT8_ARRAY > 255)
#error constant too large
#endif
#if (DUK_STRIDX_UINT8_CLAMPED_ARRAY > 255)
#error constant too large
#endif
#if (DUK_STRIDX_INT16_ARRAY > 255)
#error constant too large
#endif
#if (DUK_STRIDX_UINT16_ARRAY > 255)
#error constant too large
#endif
#if (DUK_STRIDX_INT32_ARRAY > 255)
#error constant too large
#endif
#if (DUK_STRIDX_UINT32_ARRAY > 255)
#error constant too large
#endif
#if (DUK_STRIDX_FLOAT32_ARRAY > 255)
#error constant too large
#endif
#if (DUK_STRIDX_FLOAT64_ARRAY > 255)
#error constant too large
#endif
#if (DUK_STRIDX_EMPTY_STRING > 255)
#error constant too large
#endif

/* Note: assumes that these string indexes are 8-bit, genstrings.py must ensure that */
DUK_INTERNAL duk_uint8_t duk_class_number_to_stridx[32] = {
    DUK_STRIDX_EMPTY_STRING,  /* NONE, intentionally empty */
    DUK_STRIDX_UC_OBJECT,
    DUK_STRIDX_UC_ARRAY,
    DUK_STRIDX_UC_FUNCTION,
    DUK_STRIDX_UC_ARGUMENTS,
    DUK_STRIDX_UC_BOOLEAN,
    DUK_STRIDX_UC_DATE,
    DUK_STRIDX_UC_ERROR,
    DUK_STRIDX_JSON,
    DUK_STRIDX_MATH,
    DUK_STRIDX_UC_NUMBER,
    DUK_STRIDX_REG_EXP,
    DUK_STRIDX_UC_STRING,
    DUK_STRIDX_GLOBAL,
    DUK_STRIDX_UC_SYMBOL,
    DUK_STRIDX_OBJ_ENV,
    DUK_STRIDX_DEC_ENV,
    DUK_STRIDX_UC_POINTER,
    DUK_STRIDX_UC_THREAD,
    DUK_STRIDX_ARRAY_BUFFER,
    DUK_STRIDX_DATA_VIEW,
    DUK_STRIDX_INT8_ARRAY,
    DUK_STRIDX_UINT8_ARRAY,
    DUK_STRIDX_UINT8_CLAMPED_ARRAY,
    DUK_STRIDX_INT16_ARRAY,
    DUK_STRIDX_UINT16_ARRAY,
    DUK_STRIDX_INT32_ARRAY,
    DUK_STRIDX_UINT32_ARRAY,
    DUK_STRIDX_FLOAT32_ARRAY,
    DUK_STRIDX_FLOAT64_ARRAY,
    DUK_STRIDX_EMPTY_STRING,  /* UNUSED, intentionally empty */
    DUK_STRIDX_EMPTY_STRING,  /* UNUSED, intentionally empty */
};
//#line 1 "duk_alloc_default.c"
/*
 *  Default allocation functions.
 *
 *  Assumes behavior such as malloc allowing zero size, yielding
 *  a NULL or a unique pointer which is a no-op for free.
 */

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

#if defined(DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS)
DUK_INTERNAL void *duk_default_alloc_function(void *udata, duk_size_t size) {
    void *res;
    DUK_UNREF(udata);
    res = DUK_ANSI_MALLOC(size);
    DUK_DDD(DUK_DDDPRINT("default alloc function: %lu -> %p",
                         (unsigned long) size, (void *) res));
    return res;
}

DUK_INTERNAL void *duk_default_realloc_function(void *udata, void *ptr, duk_size_t newsize) {
    void *res;
    DUK_UNREF(udata);
    res = DUK_ANSI_REALLOC(ptr, newsize);
    DUK_DDD(DUK_DDDPRINT("default realloc function: %p %lu -> %p",
                         (void *) ptr, (unsigned long) newsize, (void *) res));
    return res;
}

DUK_INTERNAL void duk_default_free_function(void *udata, void *ptr) {
    DUK_DDD(DUK_DDDPRINT("default free function: %p", (void *) ptr));
    DUK_UNREF(udata);
    DUK_ANSI_FREE(ptr);
}
#endif  /* DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS */
//#line 1 "duk_api_buffer.c"
/*
 *  Buffer
 */

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

DUK_EXTERNAL void *duk_resize_buffer(duk_hthread *thr, duk_idx_t idx, duk_size_t new_size) {
    duk_hbuffer_dynamic *h;

    DUK_ASSERT_API_ENTRY(thr);

    h = (duk_hbuffer_dynamic *) duk_require_hbuffer(thr, idx);
    DUK_ASSERT(h != NULL);

    if (!(DUK_HBUFFER_HAS_DYNAMIC(h) && !DUK_HBUFFER_HAS_EXTERNAL(h))) {
        DUK_ERROR_TYPE(thr, DUK_STR_WRONG_BUFFER_TYPE);
        DUK_WO_NORETURN(return NULL;);
    }

    /* Maximum size check is handled by callee. */
    duk_hbuffer_resize(thr, h, new_size);

    return DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, h);
}

DUK_EXTERNAL void *duk_steal_buffer(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size) {
    duk_hbuffer_dynamic *h;
    void *ptr;
    duk_size_t sz;

    DUK_ASSERT_API_ENTRY(thr);

    h = (duk_hbuffer_dynamic *) duk_require_hbuffer(thr, idx);
    DUK_ASSERT(h != NULL);

    if (!(DUK_HBUFFER_HAS_DYNAMIC(h) && !DUK_HBUFFER_HAS_EXTERNAL(h))) {
        DUK_ERROR_TYPE(thr, DUK_STR_WRONG_BUFFER_TYPE);
        DUK_WO_NORETURN(return NULL;);
    }

    /* Forget the previous allocation, setting size to 0 and alloc to
     * NULL.  Caller is responsible for freeing the previous allocation.
     * Getting the allocation and clearing it is done in the same API
     * call to avoid any chance of a realloc.
     */
    ptr = DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, h);
    sz = DUK_HBUFFER_DYNAMIC_GET_SIZE(h);
    if (out_size) {
        *out_size = sz;
    }
    DUK_HBUFFER_DYNAMIC_SET_DATA_PTR_NULL(thr->heap, h);
    DUK_HBUFFER_DYNAMIC_SET_SIZE(h, 0);

    return ptr;
}

DUK_EXTERNAL void duk_config_buffer(duk_hthread *thr, duk_idx_t idx, void *ptr, duk_size_t len) {
    duk_hbuffer_external *h;

    DUK_ASSERT_API_ENTRY(thr);

    h = (duk_hbuffer_external *) duk_require_hbuffer(thr, idx);
    DUK_ASSERT(h != NULL);

    if (!DUK_HBUFFER_HAS_EXTERNAL(h)) {
        DUK_ERROR_TYPE(thr, DUK_STR_WRONG_BUFFER_TYPE);
        DUK_WO_NORETURN(return;);
    }
    DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(h));

    DUK_HBUFFER_EXTERNAL_SET_DATA_PTR(thr->heap, h, ptr);
    DUK_HBUFFER_EXTERNAL_SET_SIZE(h, len);
}
//#line 1 "duk_api_bytecode.c"
/*
 *  Bytecode dump/load
 *
 *  The bytecode load primitive is more important performance-wise than the
 *  dump primitive.
 *
 *  Unlike most Duktape API calls, bytecode dump/load is not guaranteed to be
 *  memory safe for invalid arguments - caller beware!  There's little point
 *  in trying to achieve memory safety unless bytecode instructions are also
 *  validated which is not easy to do with indirect register references etc.
 */

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

#if defined(DUK_USE_BYTECODE_DUMP_SUPPORT)

#define DUK__SER_MARKER  0xbf
#define DUK__SER_STRING  0x00
#define DUK__SER_NUMBER  0x01
#define DUK__BYTECODE_INITIAL_ALLOC 256
#define DUK__NO_FORMALS  0xffffffffUL

/*
 *  Dump/load helpers, xxx_raw() helpers do no buffer checks
 */

DUK_LOCAL const duk_uint8_t *duk__load_string_raw(duk_hthread *thr, const duk_uint8_t *p) {
    duk_uint32_t len;

    len = DUK_RAW_READINC_U32_BE(p);
    duk_push_lstring(thr, (const char *) p, len);
    p += len;
    return p;
}

DUK_LOCAL const duk_uint8_t *duk__load_buffer_raw(duk_hthread *thr, const duk_uint8_t *p) {
    duk_uint32_t len;
    duk_uint8_t *buf;

    len = DUK_RAW_READINC_U32_BE(p);
    buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, (duk_size_t) len);
    DUK_ASSERT(buf != NULL);
    duk_memcpy((void *) buf, (const void *) p, (size_t) len);
    p += len;
    return p;
}

DUK_LOCAL duk_uint8_t *duk__dump_hstring_raw(duk_uint8_t *p, duk_hstring *h) {
    duk_size_t len;
    duk_uint32_t tmp32;

    DUK_ASSERT(h != NULL);

    len = DUK_HSTRING_GET_BYTELEN(h);
    DUK_ASSERT(len <= 0xffffffffUL);  /* string limits */
    tmp32 = (duk_uint32_t) len;
    DUK_RAW_WRITEINC_U32_BE(p, tmp32);
    duk_memcpy((void *) p,
               (const void *) DUK_HSTRING_GET_DATA(h),
               len);
    p += len;
    return p;
}

DUK_LOCAL duk_uint8_t *duk__dump_hbuffer_raw(duk_hthread *thr, duk_uint8_t *p, duk_hbuffer *h) {
    duk_size_t len;
    duk_uint32_t tmp32;

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

    len = DUK_HBUFFER_GET_SIZE(h);
    DUK_ASSERT(len <= 0xffffffffUL);  /* buffer limits */
    tmp32 = (duk_uint32_t) len;
    DUK_RAW_WRITEINC_U32_BE(p, tmp32);
    /* When len == 0, buffer data pointer may be NULL. */
    duk_memcpy_unsafe((void *) p,
                      (const void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h),
                      len);
    p += len;
    return p;
}

DUK_LOCAL duk_uint8_t *duk__dump_string_prop(duk_hthread *thr, duk_uint8_t *p, duk_bufwriter_ctx *bw_ctx, duk_hobject *func, duk_small_uint_t stridx) {
    duk_hstring *h_str;
    duk_tval *tv;

    tv = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, (duk_hobject *) func, stridx);
    if (tv != NULL && DUK_TVAL_IS_STRING(tv)) {
        h_str = DUK_TVAL_GET_STRING(tv);
        DUK_ASSERT(h_str != NULL);
    } else {
        h_str = DUK_HTHREAD_STRING_EMPTY_STRING(thr);
        DUK_ASSERT(h_str != NULL);
    }
    DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL);  /* ensures no overflow */
    p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U + DUK_HSTRING_GET_BYTELEN(h_str), p);
    p = duk__dump_hstring_raw(p, h_str);
    return p;
}

DUK_LOCAL duk_uint8_t *duk__dump_buffer_prop(duk_hthread *thr, duk_uint8_t *p, duk_bufwriter_ctx *bw_ctx, duk_hobject *func, duk_small_uint_t stridx) {
    duk_tval *tv;

    tv = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, (duk_hobject *) func, stridx);
    if (tv != NULL && DUK_TVAL_IS_BUFFER(tv)) {
        duk_hbuffer *h_buf;
        h_buf = DUK_TVAL_GET_BUFFER(tv);
        DUK_ASSERT(h_buf != NULL);
        DUK_ASSERT(DUK_HBUFFER_MAX_BYTELEN <= 0x7fffffffUL);  /* ensures no overflow */
        p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U + DUK_HBUFFER_GET_SIZE(h_buf), p);
        p = duk__dump_hbuffer_raw(thr, p, h_buf);
    } else {
        p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p);
        DUK_RAW_WRITEINC_U32_BE(p, 0);
    }
    return p;
}

DUK_LOCAL duk_uint8_t *duk__dump_uint32_prop(duk_hthread *thr, duk_uint8_t *p, duk_bufwriter_ctx *bw_ctx, duk_hobject *func, duk_small_uint_t stridx, duk_uint32_t def_value) {
    duk_tval *tv;
    duk_uint32_t val;

    tv = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, (duk_hobject *) func, stridx);
    if (tv != NULL && DUK_TVAL_IS_NUMBER(tv)) {
        val = (duk_uint32_t) DUK_TVAL_GET_NUMBER(tv);
    } else {
        val = def_value;
    }
    p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p);
    DUK_RAW_WRITEINC_U32_BE(p, val);
    return p;
}

DUK_LOCAL duk_uint8_t *duk__dump_varmap(duk_hthread *thr, duk_uint8_t *p, duk_bufwriter_ctx *bw_ctx, duk_hobject *func) {
    duk_hobject *h;

    h = duk_hobject_get_varmap(thr, (duk_hobject *) func);
    if (h != NULL) {
        duk_uint_fast32_t i;

        /* We know _Varmap only has own properties so walk property
         * table directly.  We also know _Varmap is dense and all
         * values are numbers; assert for these.  GC and finalizers
         * shouldn't affect _Varmap so side effects should be fine.
         */
        for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(h); i++) {
            duk_hstring *key;
            duk_tval *tv_val;
            duk_uint32_t val;

            key = DUK_HOBJECT_E_GET_KEY(thr->heap, h, i);
            DUK_ASSERT(key != NULL);  /* _Varmap is dense */
            DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, h, i));
            tv_val = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, h, i);
            DUK_ASSERT(tv_val != NULL);
            DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_val));  /* known to be number; in fact an integer */
#if defined(DUK_USE_FASTINT)
            DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv_val));
            DUK_ASSERT(DUK_TVAL_GET_FASTINT(tv_val) == (duk_int64_t) DUK_TVAL_GET_FASTINT_U32(tv_val));  /* known to be 32-bit */
            val = DUK_TVAL_GET_FASTINT_U32(tv_val);
#else
            val = (duk_uint32_t) DUK_TVAL_GET_NUMBER(tv_val);
#endif

            DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL);  /* ensures no overflow */
            p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U + DUK_HSTRING_GET_BYTELEN(key) + 4U, p);
            p = duk__dump_hstring_raw(p, key);
            DUK_RAW_WRITEINC_U32_BE(p, val);
        }
    }
    p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p);
    DUK_RAW_WRITEINC_U32_BE(p, 0);  /* end of _Varmap */
    return p;
}

DUK_LOCAL duk_uint8_t *duk__dump_formals(duk_hthread *thr, duk_uint8_t *p, duk_bufwriter_ctx *bw_ctx, duk_hobject *func) {
    duk_harray *h;

    h = duk_hobject_get_formals(thr, (duk_hobject *) func);
    if (h != NULL) {
        duk_uint32_t i;

        /* Here we rely on _Formals being a dense array containing
         * strings.  This should be the case unless _Formals has been
         * tweaked by the application (which we don't support right
         * now).
         */

        p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p);
        DUK_ASSERT(h->length != DUK__NO_FORMALS);  /* limits */
        DUK_RAW_WRITEINC_U32_BE(p, h->length);

        for (i = 0; i < h->length; i++) {
            duk_tval *tv_val;
            duk_hstring *varname;

            tv_val = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, (duk_hobject *) h, i);
            DUK_ASSERT(tv_val != NULL);
            DUK_ASSERT(DUK_TVAL_IS_STRING(tv_val));

            varname = DUK_TVAL_GET_STRING(tv_val);
            DUK_ASSERT(varname != NULL);
            DUK_ASSERT(DUK_HSTRING_GET_BYTELEN(varname) >= 1);

            DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL);  /* ensures no overflow */
            p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U + DUK_HSTRING_GET_BYTELEN(varname), p);
            p = duk__dump_hstring_raw(p, varname);
        }
    } else {
        DUK_DD(DUK_DDPRINT("dumping function without _Formals, emit marker to indicate missing _Formals"));
        p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p);
        DUK_RAW_WRITEINC_U32_BE(p, DUK__NO_FORMALS);  /* marker: no formals */
    }
    return p;
}

static duk_uint8_t *duk__dump_func(duk_hthread *thr, duk_hcompfunc *func, duk_bufwriter_ctx *bw_ctx, duk_uint8_t *p) {
    duk_tval *tv, *tv_end;
    duk_instr_t *ins, *ins_end;
    duk_hobject **fn, **fn_end;
    duk_hstring *h_str;
    duk_uint32_t count_instr;
    duk_uint32_t tmp32;
    duk_uint16_t tmp16;
    duk_double_t d;

    DUK_DD(DUK_DDPRINT("dumping function %p to %p: "
                       "consts=[%p,%p[ (%ld bytes, %ld items), "
                       "funcs=[%p,%p[ (%ld bytes, %ld items), "
                       "code=[%p,%p[ (%ld bytes, %ld items)",
                       (void *) func,
                       (void *) p,
                       (void *) DUK_HCOMPFUNC_GET_CONSTS_BASE(thr->heap, func),
                       (void *) DUK_HCOMPFUNC_GET_CONSTS_END(thr->heap, func),
                       (long) DUK_HCOMPFUNC_GET_CONSTS_SIZE(thr->heap, func),
                       (long) DUK_HCOMPFUNC_GET_CONSTS_COUNT(thr->heap, func),
                       (void *) DUK_HCOMPFUNC_GET_FUNCS_BASE(thr->heap, func),
                       (void *) DUK_HCOMPFUNC_GET_FUNCS_END(thr->heap, func),
                       (long) DUK_HCOMPFUNC_GET_FUNCS_SIZE(thr->heap, func),
                       (long) DUK_HCOMPFUNC_GET_FUNCS_COUNT(thr->heap, func),
                       (void *) DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, func),
                       (void *) DUK_HCOMPFUNC_GET_CODE_END(thr->heap, func),
                       (long) DUK_HCOMPFUNC_GET_CODE_SIZE(thr->heap, func),
                       (long) DUK_HCOMPFUNC_GET_CODE_COUNT(thr->heap, func)));

    DUK_ASSERT(DUK_USE_ESBC_MAX_BYTES <= 0x7fffffffUL);  /* ensures no overflow */
    count_instr = (duk_uint32_t) DUK_HCOMPFUNC_GET_CODE_COUNT(thr->heap, func);
    p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 3U * 4U + 2U * 2U + 3U * 4U + count_instr * 4U, p);

    /* Fixed header info. */
    tmp32 = count_instr;
    DUK_RAW_WRITEINC_U32_BE(p, tmp32);
    tmp32 = (duk_uint32_t) DUK_HCOMPFUNC_GET_CONSTS_COUNT(thr->heap, func);
    DUK_RAW_WRITEINC_U32_BE(p, tmp32);
    tmp32 = (duk_uint32_t) DUK_HCOMPFUNC_GET_FUNCS_COUNT(thr->heap, func);
    DUK_RAW_WRITEINC_U32_BE(p, tmp32);
    tmp16 = func->nregs;
    DUK_RAW_WRITEINC_U16_BE(p, tmp16);
    tmp16 = func->nargs;
    DUK_RAW_WRITEINC_U16_BE(p, tmp16);
#if defined(DUK_USE_DEBUGGER_SUPPORT)
    tmp32 = func->start_line;
    DUK_RAW_WRITEINC_U32_BE(p, tmp32);
    tmp32 = func->end_line;
    DUK_RAW_WRITEINC_U32_BE(p, tmp32);
#else
    DUK_RAW_WRITEINC_U32_BE(p, 0);
    DUK_RAW_WRITEINC_U32_BE(p, 0);
#endif
    tmp32 = DUK_HEAPHDR_GET_FLAGS((duk_heaphdr *) func);  /* masks flags, only duk_hobject flags */
    tmp32 &= ~(DUK_HOBJECT_FLAG_HAVE_FINALIZER);  /* finalizer flag is lost */
    DUK_RAW_WRITEINC_U32_BE(p, tmp32);

    /* Bytecode instructions: endian conversion needed unless
     * platform is big endian.
     */
    ins = DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, func);
    ins_end = DUK_HCOMPFUNC_GET_CODE_END(thr->heap, func);
    DUK_ASSERT((duk_size_t) (ins_end - ins) == (duk_size_t) count_instr);
#if defined(DUK_USE_INTEGER_BE)
    duk_memcpy_unsafe((void *) p, (const void *) ins, (size_t) (ins_end - ins));
    p += (size_t) (ins_end - ins);
#else
    while (ins != ins_end) {
        tmp32 = (duk_uint32_t) (*ins);
        DUK_RAW_WRITEINC_U32_BE(p, tmp32);
        ins++;
    }
#endif

    /* Constants: variable size encoding. */
    tv = DUK_HCOMPFUNC_GET_CONSTS_BASE(thr->heap, func);
    tv_end = DUK_HCOMPFUNC_GET_CONSTS_END(thr->heap, func);
    while (tv != tv_end) {
        /* constants are strings or numbers now */
        DUK_ASSERT(DUK_TVAL_IS_STRING(tv) ||
                   DUK_TVAL_IS_NUMBER(tv));

        if (DUK_TVAL_IS_STRING(tv)) {
            h_str = DUK_TVAL_GET_STRING(tv);
            DUK_ASSERT(h_str != NULL);
            DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL);  /* ensures no overflow */
            p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 1U + 4U + DUK_HSTRING_GET_BYTELEN(h_str), p);
            *p++ = DUK__SER_STRING;
            p = duk__dump_hstring_raw(p, h_str);
        } else {
            DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
            p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 1U + 8U, p);
            *p++ = DUK__SER_NUMBER;
            d = DUK_TVAL_GET_NUMBER(tv);
            DUK_RAW_WRITEINC_DOUBLE_BE(p, d);
        }
        tv++;
    }

    /* Inner functions recursively. */
    fn = (duk_hobject **) DUK_HCOMPFUNC_GET_FUNCS_BASE(thr->heap, func);
    fn_end = (duk_hobject **) DUK_HCOMPFUNC_GET_FUNCS_END(thr->heap, func);
    while (fn != fn_end) {
        /* XXX: This causes recursion up to inner function depth
         * which is normally not an issue, e.g. mark-and-sweep uses
         * a recursion limiter to avoid C stack issues.  Avoiding
         * this would mean some sort of a work list or just refusing
         * to serialize deep functions.
         */
        DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(*fn));
        p = duk__dump_func(thr, (duk_hcompfunc *) *fn, bw_ctx, p);
        fn++;
    }

    /* Lexenv and varenv are not dumped. */

    /* Object extra properties.
     *
     * There are some difference between function templates and functions.
     * For example, function templates don't have .length and nargs is
     * normally used to instantiate the functions.
     */

    p = duk__dump_uint32_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_LENGTH, (duk_uint32_t) func->nargs);
#if defined(DUK_USE_FUNC_NAME_PROPERTY)
    p = duk__dump_string_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_NAME);
#endif
#if defined(DUK_USE_FUNC_FILENAME_PROPERTY)
    p = duk__dump_string_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_FILE_NAME);
#endif
#if defined(DUK_USE_PC2LINE)
    p = duk__dump_buffer_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_INT_PC2LINE);
#endif
    p = duk__dump_varmap(thr, p, bw_ctx, (duk_hobject *) func);
    p = duk__dump_formals(thr, p, bw_ctx, (duk_hobject *) func);

    DUK_DD(DUK_DDPRINT("serialized function %p -> final pointer %p", (void *) func, (void *) p));

    return p;
}

/* Load a function from bytecode.  The function object returned here must
 * match what is created by duk_js_push_closure() with respect to its flags,
 * properties, etc.
 *
 * NOTE: there are intentionally no input buffer length / bound checks.
 * Adding them would be easy but wouldn't ensure memory safety as untrusted
 * or broken bytecode is unsafe during execution unless the opcodes themselves
 * are validated (which is quite complex, especially for indirect opcodes).
 */

#define DUK__ASSERT_LEFT(n) do { \
        DUK_ASSERT((duk_size_t) (p_end - p) >= (duk_size_t) (n)); \
    } while (0)

static const duk_uint8_t *duk__load_func(duk_hthread *thr, const duk_uint8_t *p, const duk_uint8_t *p_end) {
    duk_hcompfunc *h_fun;
    duk_hbuffer *h_data;
    duk_size_t data_size;
    duk_uint32_t count_instr, count_const, count_funcs;
    duk_uint32_t n;
    duk_uint32_t tmp32;
    duk_small_uint_t const_type;
    duk_uint8_t *fun_data;
    duk_uint8_t *q;
    duk_idx_t idx_base;
    duk_tval *tv1;
    duk_uarridx_t arr_idx;
    duk_uarridx_t arr_limit;
    duk_hobject *func_env;
    duk_bool_t need_pop;

    /* XXX: There's some overlap with duk_js_closure() here, but
     * seems difficult to share code.  Ensure that the final function
     * looks the same as created by duk_js_closure().
     */

    DUK_ASSERT(thr != NULL);

    DUK_DD(DUK_DDPRINT("loading function, p=%p, p_end=%p", (const void *) p, (const void *) p_end));

    DUK__ASSERT_LEFT(3 * 4);
    count_instr = DUK_RAW_READINC_U32_BE(p);
    count_const = DUK_RAW_READINC_U32_BE(p);
    count_funcs = DUK_RAW_READINC_U32_BE(p);

    data_size = sizeof(duk_tval) * count_const +
                sizeof(duk_hobject *) * count_funcs +
                sizeof(duk_instr_t) * count_instr;

    DUK_DD(DUK_DDPRINT("instr=%ld, const=%ld, funcs=%ld, data_size=%ld",
                       (long) count_instr, (long) count_const,
                       (long) count_const, (long) data_size));

    /* Value stack is used to ensure reachability of constants and
     * inner functions being loaded.  Require enough space to handle
     * large functions correctly.
     */
    duk_require_stack(thr, (duk_idx_t) (2 + count_const + count_funcs));
    idx_base = duk_get_top(thr);

    /* Push function object, init flags etc.  This must match
     * duk_js_push_closure() quite carefully.
     */
    h_fun = duk_push_hcompfunc(thr);
    DUK_ASSERT(h_fun != NULL);
    DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) h_fun));
    DUK_ASSERT(DUK_HCOMPFUNC_GET_DATA(thr->heap, h_fun) == NULL);
    DUK_ASSERT(DUK_HCOMPFUNC_GET_FUNCS(thr->heap, h_fun) == NULL);
    DUK_ASSERT(DUK_HCOMPFUNC_GET_BYTECODE(thr->heap, h_fun) == NULL);
    DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_fun) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);

    h_fun->nregs = DUK_RAW_READINC_U16_BE(p);
    h_fun->nargs = DUK_RAW_READINC_U16_BE(p);
#if defined(DUK_USE_DEBUGGER_SUPPORT)
    h_fun->start_line = DUK_RAW_READINC_U32_BE(p);
    h_fun->end_line = DUK_RAW_READINC_U32_BE(p);
#else
    p += 8;  /* skip line info */
#endif

    /* duk_hcompfunc flags; quite version specific */
    tmp32 = DUK_RAW_READINC_U32_BE(p);
    DUK_HEAPHDR_SET_FLAGS((duk_heaphdr *) h_fun, tmp32);  /* masks flags to only change duk_hobject flags */

    /* standard prototype (no need to set here, already set) */
    DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_fun) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
#if 0
    DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, &h_fun->obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
#endif

    /* assert just a few critical flags */
    DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) h_fun) == DUK_HTYPE_OBJECT);
    DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(&h_fun->obj));
    DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(&h_fun->obj));
    DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(&h_fun->obj));
    DUK_ASSERT(!DUK_HOBJECT_IS_THREAD(&h_fun->obj));
    DUK_ASSERT(!DUK_HOBJECT_IS_PROXY(&h_fun->obj));
    DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARRAY(&h_fun->obj));
    DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(&h_fun->obj));
    DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(&h_fun->obj));

    /* Create function 'data' buffer but don't attach it yet. */
    fun_data = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, data_size);
    DUK_ASSERT(fun_data != NULL);

    /* Load bytecode instructions. */
    DUK_ASSERT(sizeof(duk_instr_t) == 4);
    DUK__ASSERT_LEFT(count_instr * sizeof(duk_instr_t));
#if defined(DUK_USE_INTEGER_BE)
    q = fun_data + sizeof(duk_tval) * count_const + sizeof(duk_hobject *) * count_funcs;
    duk_memcpy((void *) q,
               (const void *) p,
               sizeof(duk_instr_t) * count_instr);
    p += sizeof(duk_instr_t) * count_instr;
#else
    q = fun_data + sizeof(duk_tval) * count_const + sizeof(duk_hobject *) * count_funcs;
    for (n = count_instr; n > 0; n--) {
        *((duk_instr_t *) (void *) q) = DUK_RAW_READINC_U32_BE(p);
        q += sizeof(duk_instr_t);
    }
#endif

    /* Load constants onto value stack but don't yet copy to buffer. */
    for (n = count_const; n > 0; n--) {
        DUK__ASSERT_LEFT(1);
        const_type = DUK_RAW_READINC_U8(p);
        switch (const_type) {
        case DUK__SER_STRING: {
            p = duk__load_string_raw(thr, p);
            break;
        }
        case DUK__SER_NUMBER: {
            /* Important to do a fastint check so that constants are
             * properly read back as fastints.
             */
            duk_tval tv_tmp;
            duk_double_t val;
            DUK__ASSERT_LEFT(8);
            val = DUK_RAW_READINC_DOUBLE_BE(p);
            DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(&tv_tmp, val);
            duk_push_tval(thr, &tv_tmp);
            break;
        }
        default: {
            goto format_error;
        }
        }
    }

    /* Load inner functions to value stack, but don't yet copy to buffer. */
    for (n = count_funcs; n > 0; n--) {
        p = duk__load_func(thr, p, p_end);
        if (p == NULL) {
            goto format_error;
        }
    }

    /* With constants and inner functions on value stack, we can now
     * atomically finish the function 'data' buffer, bump refcounts,
     * etc.
     *
     * Here we take advantage of the value stack being just a duk_tval
     * array: we can just memcpy() the constants as long as we incref
     * them afterwards.
     */

    h_data = (duk_hbuffer *) duk_known_hbuffer(thr, idx_base + 1);
    DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC(h_data));
    DUK_HCOMPFUNC_SET_DATA(thr->heap, h_fun, h_data);
    DUK_HBUFFER_INCREF(thr, h_data);

    tv1 = duk_get_tval(thr, idx_base + 2);  /* may be NULL if no constants or inner funcs */
    DUK_ASSERT((count_const == 0 && count_funcs == 0) || tv1 != NULL);

    q = fun_data;
    duk_memcpy_unsafe((void *) q, (const void *) tv1, sizeof(duk_tval) * count_const);
    for (n = count_const; n > 0; n--) {
        DUK_TVAL_INCREF_FAST(thr, (duk_tval *) (void *) q);  /* no side effects */
        q += sizeof(duk_tval);
    }
    tv1 += count_const;

    DUK_HCOMPFUNC_SET_FUNCS(thr->heap, h_fun, (duk_hobject **) (void *) q);
    for (n = count_funcs; n > 0; n--) {
        duk_hobject *h_obj;

        DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv1));
        h_obj = DUK_TVAL_GET_OBJECT(tv1);
        DUK_ASSERT(h_obj != NULL);
        tv1++;
        DUK_HOBJECT_INCREF(thr, h_obj);

        *((duk_hobject **) (void *) q) = h_obj;
        q += sizeof(duk_hobject *);
    }

    DUK_HCOMPFUNC_SET_BYTECODE(thr->heap, h_fun, (duk_instr_t *) (void *) q);

    /* The function object is now reachable and refcounts are fine,
     * so we can pop off all the temporaries.
     */
    DUK_DDD(DUK_DDDPRINT("function is reachable, reset top; func: %!iT", duk_get_tval(thr, idx_base)));
    duk_set_top(thr, idx_base + 1);

    /* Setup function properties. */
    tmp32 = DUK_RAW_READINC_U32_BE(p);
    duk_push_u32(thr, tmp32);
    duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_C);

#if defined(DUK_USE_FUNC_NAME_PROPERTY)
    p = duk__load_string_raw(thr, p);  /* -> [ func funcname ] */
    func_env = thr->builtins[DUK_BIDX_GLOBAL_ENV];
    DUK_ASSERT(func_env != NULL);
    need_pop = 0;
    if (DUK_HOBJECT_HAS_NAMEBINDING((duk_hobject *) h_fun)) {
        /* Original function instance/template had NAMEBINDING.
         * Must create a lexical environment on loading to allow
         * recursive functions like 'function foo() { foo(); }'.
         */
        duk_hdecenv *new_env;

        new_env = duk_hdecenv_alloc(thr,
                                    DUK_HOBJECT_FLAG_EXTENSIBLE |
                                    DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV));
        DUK_ASSERT(new_env != NULL);
        DUK_ASSERT(new_env->thread == NULL);  /* Closed. */
        DUK_ASSERT(new_env->varmap == NULL);
        DUK_ASSERT(new_env->regbase_byteoff == 0);
        DUK_HDECENV_ASSERT_VALID(new_env);
        DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) new_env) == NULL);
        DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) new_env, func_env);
        DUK_HOBJECT_INCREF(thr, func_env);

        func_env = (duk_hobject *) new_env;

        duk_push_hobject(thr, (duk_hobject *) new_env);

        duk_dup_m2(thr);                                  /* -> [ func funcname env funcname ] */
        duk_dup(thr, idx_base);                           /* -> [ func funcname env funcname func ] */
        duk_xdef_prop(thr, -3, DUK_PROPDESC_FLAGS_NONE);  /* -> [ func funcname env ] */

        need_pop = 1;  /* Need to pop env, but -after- updating h_fun and increfs. */
    }
    DUK_ASSERT(func_env != NULL);
    DUK_HCOMPFUNC_SET_LEXENV(thr->heap, h_fun, func_env);
    DUK_HCOMPFUNC_SET_VARENV(thr->heap, h_fun, func_env);
    DUK_HOBJECT_INCREF(thr, func_env);
    DUK_HOBJECT_INCREF(thr, func_env);
    if (need_pop) {
        duk_pop(thr);
    }
    duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C);
#endif  /* DUK_USE_FUNC_NAME_PROPERTY */

#if defined(DUK_USE_FUNC_FILENAME_PROPERTY)
    p = duk__load_string_raw(thr, p);
    duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_C);
#endif  /* DUK_USE_FUNC_FILENAME_PROPERTY */

    if (DUK_HOBJECT_HAS_CONSTRUCTABLE((duk_hobject *) h_fun)) {
        /* Restore empty external .prototype only for constructable
         * functions.  The prototype object should inherit from
         * Object.prototype.
         */
        duk_push_object(thr);
        DUK_ASSERT(!duk_is_bare_object(thr, -1));
        duk_dup_m2(thr);
        duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_CONSTRUCTOR, DUK_PROPDESC_FLAGS_WC);  /* func.prototype.constructor = func */
        duk_compact_m1(thr);
        duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_PROTOTYPE, DUK_PROPDESC_FLAGS_W);
    }

#if defined(DUK_USE_PC2LINE)
    p = duk__load_buffer_raw(thr, p);
    duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_PC2LINE, DUK_PROPDESC_FLAGS_WC);
#endif  /* DUK_USE_PC2LINE */

    duk_push_bare_object(thr);  /* _Varmap */
    for (;;) {
        /* XXX: awkward */
        p = duk__load_string_raw(thr, p);
        if (duk_get_length(thr, -1) == 0) {
            duk_pop(thr);
            break;
        }
        tmp32 = DUK_RAW_READINC_U32_BE(p);
        duk_push_u32(thr, tmp32);
        duk_put_prop(thr, -3);
    }
    duk_compact_m1(thr);
    duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VARMAP, DUK_PROPDESC_FLAGS_NONE);

    /* _Formals may have been missing in the original function, which is
     * handled using a marker length.
     */
    arr_limit = DUK_RAW_READINC_U32_BE(p);
    if (arr_limit != DUK__NO_FORMALS) {
        duk_push_bare_array(thr);  /* _Formals */
        for (arr_idx = 0; arr_idx < arr_limit; arr_idx++) {
            p = duk__load_string_raw(thr, p);
            duk_put_prop_index(thr, -2, arr_idx);
        }
        duk_compact_m1(thr);
        duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_FORMALS, DUK_PROPDESC_FLAGS_NONE);
    } else {
        DUK_DD(DUK_DDPRINT("no _Formals in dumped function"));
    }

    /* Return with final function pushed on stack top. */
    DUK_DD(DUK_DDPRINT("final loaded function: %!iT", duk_get_tval(thr, -1)));
    DUK_ASSERT_TOP(thr, idx_base + 1);
    return p;

 format_error:
    return NULL;
}

DUK_EXTERNAL void duk_dump_function(duk_hthread *thr) {
    duk_hcompfunc *func;
    duk_bufwriter_ctx bw_ctx_alloc;
    duk_bufwriter_ctx *bw_ctx = &bw_ctx_alloc;
    duk_uint8_t *p;

    DUK_ASSERT_API_ENTRY(thr);

    /* Bound functions don't have all properties so we'd either need to
     * lookup the non-bound target function or reject bound functions.
     * For now, bound functions are rejected with TypeError.
     */
    func = duk_require_hcompfunc(thr, -1);
    DUK_ASSERT(func != NULL);
    DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(&func->obj));

    /* Estimating the result size beforehand would be costly, so
     * start with a reasonable size and extend as needed.
     */
    DUK_BW_INIT_PUSHBUF(thr, bw_ctx, DUK__BYTECODE_INITIAL_ALLOC);
    p = DUK_BW_GET_PTR(thr, bw_ctx);
    *p++ = DUK__SER_MARKER;
    p = duk__dump_func(thr, func, bw_ctx, p);
    DUK_BW_SET_PTR(thr, bw_ctx, p);
    DUK_BW_COMPACT(thr, bw_ctx);

    DUK_DD(DUK_DDPRINT("serialized result: %!T", duk_get_tval(thr, -1)));

    duk_remove_m2(thr);  /* [ ... func buf ] -> [ ... buf ] */
}

DUK_EXTERNAL void duk_load_function(duk_hthread *thr) {
    const duk_uint8_t *p_buf, *p, *p_end;
    duk_size_t sz;

    DUK_ASSERT_API_ENTRY(thr);

    p_buf = (duk_uint8_t *) duk_require_buffer(thr, -1, &sz);
    DUK_ASSERT(p_buf != NULL);

    /* The caller is responsible for being sure that bytecode being loaded
     * is valid and trusted.  Invalid bytecode can cause memory unsafe
     * behavior directly during loading or later during bytecode execution
     * (instruction validation would be quite complex to implement).
     *
     * This signature check is the only sanity check for detecting
     * accidental invalid inputs.  The initial byte ensures no ordinary
     * string or Symbol will be accepted by accident.
     */
    p = p_buf;
    p_end = p_buf + sz;
    if (sz < 1 || p[0] != DUK__SER_MARKER) {
        goto format_error;
    }
    p++;

    p = duk__load_func(thr, p, p_end);
    if (p == NULL) {
        goto format_error;
    }

    duk_remove_m2(thr);  /* [ ... buf func ] -> [ ... func ] */
    return;

 format_error:
    DUK_ERROR_TYPE(thr, DUK_STR_INVALID_BYTECODE);
    DUK_WO_NORETURN(return;);
}

#else  /* DUK_USE_BYTECODE_DUMP_SUPPORT */

DUK_EXTERNAL void duk_dump_function(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ERROR_UNSUPPORTED(thr);
    DUK_WO_NORETURN(return;);
}

DUK_EXTERNAL void duk_load_function(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ERROR_UNSUPPORTED(thr);
    DUK_WO_NORETURN(return;);
}

#endif  /* DUK_USE_BYTECODE_DUMP_SUPPORT */

/* automatic undefs */
#undef DUK__ASSERT_LEFT
#undef DUK__BYTECODE_INITIAL_ALLOC
#undef DUK__NO_FORMALS
#undef DUK__SER_MARKER
#undef DUK__SER_NUMBER
#undef DUK__SER_STRING
//#line 1 "duk_api_call.c"
/*
 *  Calls.
 *
 *  Protected variants should avoid ever throwing an error.  Must be careful
 *  to catch errors related to value stack manipulation and property lookup,
 *  not just the call itself.
 *
 *  The only exception is when arguments are insane, e.g. nargs/nrets are out
 *  of bounds; in such cases an error is thrown for two reasons.  First, we
 *  can't always respect the value stack input/output guarantees in such cases
 *  so the caller would end up with the value stack in an unexpected state.
 *  Second, an attempt to create an error might itself fail (although this
 *  could be avoided by pushing a preallocated object/string or a primitive
 *  value).
 */

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

/*
 *  Helpers
 */

struct duk__pcall_prop_args {
    duk_idx_t obj_idx;
    duk_idx_t nargs;
    duk_small_uint_t call_flags;
};
typedef struct duk__pcall_prop_args duk__pcall_prop_args;

struct duk__pcall_method_args {
    duk_idx_t nargs;
    duk_small_uint_t call_flags;
};
typedef struct duk__pcall_method_args duk__pcall_method_args;

struct duk__pcall_args {
    duk_idx_t nargs;
    duk_small_uint_t call_flags;
};
typedef struct duk__pcall_args duk__pcall_args;

/* Compute and validate idx_func for a certain 'nargs' and 'other'
 * parameter count (1 or 2, depending on whether 'this' binding is
 * present).
 */
DUK_LOCAL duk_idx_t duk__call_get_idx_func(duk_hthread *thr, duk_idx_t nargs, duk_idx_t other) {
    duk_idx_t idx_func;

    /* XXX: byte arithmetic? */

    DUK_ASSERT(other >= 0);

    idx_func = duk_get_top(thr) - nargs - other;
    if (DUK_UNLIKELY((idx_func | nargs) < 0)) {  /* idx_func < 0 || nargs < 0; OR sign bits */
        DUK_ERROR_TYPE_INVALID_ARGS(thr);
        DUK_WO_NORETURN(return 0;);
    }
    DUK_ASSERT(duk_is_valid_index(thr, idx_func));
    return idx_func;
}

/* Compute idx_func, assume index will be valid.  This is a valid assumption
 * for protected calls: nargs < 0 is checked explicitly and duk_safe_call()
 * validates the argument count.
 */
DUK_LOCAL duk_idx_t duk__call_get_idx_func_unvalidated(duk_hthread *thr, duk_idx_t nargs, duk_idx_t other) {
    duk_idx_t idx_func;

    /* XXX: byte arithmetic? */

    DUK_ASSERT(nargs >= 0);
    DUK_ASSERT(other >= 0);

    idx_func = duk_get_top(thr) - nargs - other;
    DUK_ASSERT(idx_func >= 0);
    DUK_ASSERT(duk_is_valid_index(thr, idx_func));
    return idx_func;
}

/* Prepare value stack for a method call through an object property.
 * May currently throw an error e.g. when getting the property.
 */
DUK_LOCAL void duk__call_prop_prep_stack(duk_hthread *thr, duk_idx_t normalized_obj_idx, duk_idx_t nargs) {
    DUK_CTX_ASSERT_VALID(thr);
    DUK_ASSERT(nargs >= 0);

    DUK_DDD(DUK_DDDPRINT("duk__call_prop_prep_stack, normalized_obj_idx=%ld, nargs=%ld, stacktop=%ld",
                         (long) normalized_obj_idx, (long) nargs, (long) duk_get_top(thr)));

    /* [... key arg1 ... argN] */

    /* duplicate key */
    duk_dup(thr, -nargs - 1);  /* Note: -nargs alone would fail for nargs == 0, this is OK */
    (void) duk_get_prop(thr, normalized_obj_idx);

    DUK_DDD(DUK_DDDPRINT("func: %!T", (duk_tval *) duk_get_tval(thr, -1)));

#if defined(DUK_USE_VERBOSE_ERRORS)
    if (DUK_UNLIKELY(!duk_is_callable(thr, -1))) {
        duk_tval *tv_base;
        duk_tval *tv_key;

        /* tv_targ is passed on stack top (at index -1). */
        tv_base = DUK_GET_TVAL_POSIDX(thr, normalized_obj_idx);
        tv_key = DUK_GET_TVAL_NEGIDX(thr, -nargs - 2);
        DUK_ASSERT(tv_base >= thr->valstack_bottom && tv_base < thr->valstack_top);
        DUK_ASSERT(tv_key >= thr->valstack_bottom && tv_key < thr->valstack_top);

        duk_call_setup_propcall_error(thr, tv_base, tv_key);
    }
#endif

    /* [... key arg1 ... argN func] */

    duk_replace(thr, -nargs - 2);

    /* [... func arg1 ... argN] */

    duk_dup(thr, normalized_obj_idx);
    duk_insert(thr, -nargs - 1);

    /* [... func this arg1 ... argN] */
}

DUK_EXTERNAL void duk_call(duk_hthread *thr, duk_idx_t nargs) {
    duk_small_uint_t call_flags;
    duk_idx_t idx_func;

    DUK_ASSERT_API_ENTRY(thr);

    idx_func = duk__call_get_idx_func(thr, nargs, 1);
    DUK_ASSERT(duk_is_valid_index(thr, idx_func));

    duk_insert_undefined(thr, idx_func + 1);

    call_flags = 0;  /* not protected, respect reclimit, not constructor */
    duk_handle_call_unprotected(thr, idx_func, call_flags);
}

DUK_EXTERNAL void duk_call_method(duk_hthread *thr, duk_idx_t nargs) {
    duk_small_uint_t call_flags;
    duk_idx_t idx_func;

    DUK_ASSERT_API_ENTRY(thr);

    idx_func = duk__call_get_idx_func(thr, nargs, 2);
    DUK_ASSERT(duk_is_valid_index(thr, idx_func));

    call_flags = 0;  /* not protected, respect reclimit, not constructor */
    duk_handle_call_unprotected(thr, idx_func, call_flags);
}

DUK_EXTERNAL void duk_call_prop(duk_hthread *thr, duk_idx_t obj_idx, duk_idx_t nargs) {
    /*
     *  XXX: if duk_handle_call() took values through indices, this could be
     *  made much more sensible.  However, duk_handle_call() needs to fudge
     *  the 'this' and 'func' values to handle bound functions, which is now
     *  done "in-place", so this is not a trivial change.
     */

    DUK_ASSERT_API_ENTRY(thr);

    obj_idx = duk_require_normalize_index(thr, obj_idx);  /* make absolute */
    if (DUK_UNLIKELY(nargs < 0)) {
        DUK_ERROR_TYPE_INVALID_ARGS(thr);
        DUK_WO_NORETURN(return;);
    }

    duk__call_prop_prep_stack(thr, obj_idx, nargs);

    duk_call_method(thr, nargs);
}

DUK_LOCAL duk_ret_t duk__pcall_raw(duk_hthread *thr, void *udata) {
    duk__pcall_args *args;
    duk_idx_t idx_func;
    duk_int_t ret;

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

    args = (duk__pcall_args *) udata;
    idx_func = duk__call_get_idx_func_unvalidated(thr, args->nargs, 1);
    DUK_ASSERT(duk_is_valid_index(thr, idx_func));

    duk_insert_undefined(thr, idx_func + 1);

    ret = duk_handle_call_unprotected(thr, idx_func, args->call_flags);
    DUK_ASSERT(ret == 0);
    DUK_UNREF(ret);

    return 1;
}

DUK_EXTERNAL duk_int_t duk_pcall(duk_hthread *thr, duk_idx_t nargs) {
    duk__pcall_args args;

    DUK_ASSERT_API_ENTRY(thr);

    args.nargs = nargs;
    if (DUK_UNLIKELY(nargs < 0)) {
        DUK_ERROR_TYPE_INVALID_ARGS(thr);
        DUK_WO_NORETURN(return DUK_EXEC_ERROR;);
    }
    args.call_flags = 0;

    return duk_safe_call(thr, duk__pcall_raw, (void *) &args /*udata*/, nargs + 1 /*nargs*/, 1 /*nrets*/);
}

DUK_LOCAL duk_ret_t duk__pcall_method_raw(duk_hthread *thr, void *udata) {
    duk__pcall_method_args *args;
    duk_idx_t idx_func;
    duk_int_t ret;

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

    args = (duk__pcall_method_args *) udata;

    idx_func = duk__call_get_idx_func_unvalidated(thr, args->nargs, 2);
    DUK_ASSERT(duk_is_valid_index(thr, idx_func));

    ret = duk_handle_call_unprotected(thr, idx_func, args->call_flags);
    DUK_ASSERT(ret == 0);
    DUK_UNREF(ret);

    return 1;
}

DUK_INTERNAL duk_int_t duk_pcall_method_flags(duk_hthread *thr, duk_idx_t nargs, duk_small_uint_t call_flags) {
    duk__pcall_method_args args;

    DUK_ASSERT_API_ENTRY(thr);

    args.nargs = nargs;
    if (DUK_UNLIKELY(nargs < 0)) {
        DUK_ERROR_TYPE_INVALID_ARGS(thr);
        DUK_WO_NORETURN(return DUK_EXEC_ERROR;);
    }
    args.call_flags = call_flags;

    return duk_safe_call(thr, duk__pcall_method_raw, (void *) &args /*udata*/, nargs + 2 /*nargs*/, 1 /*nrets*/);
}

DUK_EXTERNAL duk_int_t duk_pcall_method(duk_hthread *thr, duk_idx_t nargs) {
    DUK_ASSERT_API_ENTRY(thr);

    return duk_pcall_method_flags(thr, nargs, 0);
}

DUK_LOCAL duk_ret_t duk__pcall_prop_raw(duk_hthread *thr, void *udata) {
    duk__pcall_prop_args *args;
    duk_idx_t obj_idx;
    duk_int_t ret;

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

    args = (duk__pcall_prop_args *) udata;

    obj_idx = duk_require_normalize_index(thr, args->obj_idx);  /* make absolute */
    duk__call_prop_prep_stack(thr, obj_idx, args->nargs);

    ret = duk_handle_call_unprotected_nargs(thr, args->nargs, args->call_flags);
    DUK_ASSERT(ret == 0);
    DUK_UNREF(ret);
    return 1;
}

DUK_EXTERNAL duk_int_t duk_pcall_prop(duk_hthread *thr, duk_idx_t obj_idx, duk_idx_t nargs) {
    duk__pcall_prop_args args;

    DUK_ASSERT_API_ENTRY(thr);

    args.obj_idx = obj_idx;
    args.nargs = nargs;
    if (DUK_UNLIKELY(nargs < 0)) {
        DUK_ERROR_TYPE_INVALID_ARGS(thr);
        DUK_WO_NORETURN(return DUK_EXEC_ERROR;);
    }
    args.call_flags = 0;

    return duk_safe_call(thr, duk__pcall_prop_raw, (void *) &args /*udata*/, nargs + 1 /*nargs*/, 1 /*nrets*/);
}

DUK_EXTERNAL duk_int_t duk_safe_call(duk_hthread *thr, duk_safe_call_function func, void *udata, duk_idx_t nargs, duk_idx_t nrets) {
    duk_int_t rc;

    DUK_ASSERT_API_ENTRY(thr);

    /* nargs condition; fail if: top - bottom < nargs
     *                      <=>  top < bottom + nargs
     * nrets condition; fail if: end - (top - nargs) < nrets
     *                      <=>  end - top + nargs < nrets
     *                      <=>  end + nargs < top + nrets
     */
    /* XXX: check for any reserve? */

    if (DUK_UNLIKELY((nargs | nrets) < 0 ||  /* nargs < 0 || nrets < 0; OR sign bits */
                     thr->valstack_top < thr->valstack_bottom + nargs ||        /* nargs too large compared to top */
                     thr->valstack_end + nargs < thr->valstack_top + nrets)) {  /* nrets too large compared to reserve */
        DUK_D(DUK_DPRINT("not enough stack reserve for safe call or invalid arguments: "
                         "nargs=%ld < 0 (?), nrets=%ld < 0 (?), top=%ld < bottom=%ld + nargs=%ld (?), "
                         "end=%ld + nargs=%ld < top=%ld + nrets=%ld (?)",
                          (long) nargs,
                          (long) nrets,
                          (long) (thr->valstack_top - thr->valstack),
                          (long) (thr->valstack_bottom - thr->valstack),
                          (long) nargs,
                          (long) (thr->valstack_end - thr->valstack),
                          (long) nargs,
                          (long) (thr->valstack_top - thr->valstack),
                          (long) nrets));
        DUK_ERROR_TYPE_INVALID_ARGS(thr);
        DUK_WO_NORETURN(return DUK_EXEC_ERROR;);
    }

    rc = duk_handle_safe_call(thr,           /* thread */
                              func,          /* func */
                              udata,         /* udata */
                              nargs,         /* num_stack_args */
                              nrets);        /* num_stack_res */

    return rc;
}

DUK_EXTERNAL void duk_new(duk_hthread *thr, duk_idx_t nargs) {
    duk_idx_t idx_func;

    DUK_ASSERT_API_ENTRY(thr);

    idx_func = duk__call_get_idx_func(thr, nargs, 1);
    DUK_ASSERT(duk_is_valid_index(thr, idx_func));

    duk_push_object(thr);  /* default instance; internal proto updated by call handling */
    duk_insert(thr, idx_func + 1);

    duk_handle_call_unprotected(thr, idx_func, DUK_CALL_FLAG_CONSTRUCT);
}

DUK_LOCAL duk_ret_t duk__pnew_helper(duk_hthread *thr, void *udata) {
    duk_idx_t nargs;

    DUK_ASSERT(udata != NULL);
    nargs = *((duk_idx_t *) udata);

    duk_new(thr, nargs);
    return 1;
}

DUK_EXTERNAL duk_int_t duk_pnew(duk_hthread *thr, duk_idx_t nargs) {
    duk_int_t rc;

    DUK_ASSERT_API_ENTRY(thr);

    /* For now, just use duk_safe_call() to wrap duk_new().  We can't
     * simply use a protected duk_handle_call() because pushing the
     * default instance might throw.
     */

    if (DUK_UNLIKELY(nargs < 0)) {
        DUK_ERROR_TYPE_INVALID_ARGS(thr);
        DUK_WO_NORETURN(return DUK_EXEC_ERROR;);
    }

    rc = duk_safe_call(thr, duk__pnew_helper, (void *) &nargs /*udata*/, nargs + 1 /*nargs*/, 1 /*nrets*/);
    return rc;
}

DUK_EXTERNAL duk_bool_t duk_is_constructor_call(duk_hthread *thr) {
    duk_activation *act;

    DUK_ASSERT_API_ENTRY(thr);

    act = thr->callstack_curr;
    if (act != NULL) {
        return ((act->flags & DUK_ACT_FLAG_CONSTRUCT) != 0 ? 1 : 0);
    }
    return 0;
}

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

    if (!duk_is_constructor_call(thr)) {
        DUK_ERROR_TYPE(thr, DUK_STR_CONSTRUCT_ONLY);
        DUK_WO_NORETURN(return;);
    }
}

DUK_EXTERNAL duk_bool_t duk_is_strict_call(duk_hthread *thr) {
    duk_activation *act;

    /* For user code this could just return 1 (strict) always
     * because all Duktape/C functions are considered strict,
     * and strict is also the default when nothing is running.
     * However, Duktape may call this function internally when
     * the current activation is an ECMAScript function, so
     * this cannot be replaced by a 'return 1' without fixing
     * the internal call sites.
     */

    DUK_ASSERT_API_ENTRY(thr);

    act = thr->callstack_curr;
    if (act != NULL) {
        return ((act->flags & DUK_ACT_FLAG_STRICT) != 0 ? 1 : 0);
    } else {
        /* Strict by default. */
        return 1;
    }
}

/*
 *  Duktape/C function magic
 */

DUK_EXTERNAL duk_int_t duk_get_current_magic(duk_hthread *thr) {
    duk_activation *act;
    duk_hobject *func;

    DUK_ASSERT_API_ENTRY(thr);

    act = thr->callstack_curr;
    if (act) {
        func = DUK_ACT_GET_FUNC(act);
        if (!func) {
            duk_tval *tv = &act->tv_func;
            duk_small_uint_t lf_flags;
            lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv);
            return (duk_int_t) DUK_LFUNC_FLAGS_GET_MAGIC(lf_flags);
        }
        DUK_ASSERT(func != NULL);

        if (DUK_HOBJECT_IS_NATFUNC(func)) {
            duk_hnatfunc *nf = (duk_hnatfunc *) func;
            return (duk_int_t) nf->magic;
        }
    }
    return 0;
}

DUK_EXTERNAL duk_int_t duk_get_magic(duk_hthread *thr, duk_idx_t idx) {
    duk_tval *tv;
    duk_hobject *h;

    DUK_ASSERT_API_ENTRY(thr);

    tv = duk_require_tval(thr, idx);
    if (DUK_TVAL_IS_OBJECT(tv)) {
        h = DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(h != NULL);
        if (!DUK_HOBJECT_HAS_NATFUNC(h)) {
            goto type_error;
        }
        return (duk_int_t) ((duk_hnatfunc *) h)->magic;
    } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
        duk_small_uint_t lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv);
        return (duk_int_t) DUK_LFUNC_FLAGS_GET_MAGIC(lf_flags);
    }

    /* fall through */
 type_error:
    DUK_ERROR_TYPE(thr, DUK_STR_UNEXPECTED_TYPE);
    DUK_WO_NORETURN(return 0;);
}

DUK_EXTERNAL void duk_set_magic(duk_hthread *thr, duk_idx_t idx, duk_int_t magic) {
    duk_hnatfunc *nf;

    DUK_ASSERT_API_ENTRY(thr);

    nf = duk_require_hnatfunc(thr, idx);
    DUK_ASSERT(nf != NULL);
    nf->magic = (duk_int16_t) magic;
}

/*
 *  Misc helpers
 */

/* Resolve a bound function on value stack top to a non-bound target
 * (leave other values as is).
 */
DUK_INTERNAL void duk_resolve_nonbound_function(duk_hthread *thr) {
    duk_tval *tv;

    DUK_HTHREAD_ASSERT_VALID(thr);

    tv = DUK_GET_TVAL_NEGIDX(thr, -1);
    if (DUK_TVAL_IS_OBJECT(tv)) {
        duk_hobject *h;

        h = DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(h != NULL);
        if (DUK_HOBJECT_HAS_BOUNDFUNC(h)) {
            duk_push_tval(thr, &((duk_hboundfunc *) (void *) h)->target);
            duk_replace(thr, -2);
#if 0
            DUK_TVAL_SET_TVAL(tv, &((duk_hboundfunc *) h)->target);
            DUK_TVAL_INCREF(thr, tv);
            DUK_HOBJECT_DECREF_NORZ(thr, h);
#endif
            /* Rely on Function.prototype.bind() on never creating a bound
             * function whose target is not proper.  This is now safe
             * because the target is not even an internal property but a
             * struct member.
             */
            DUK_ASSERT(duk_is_lightfunc(thr, -1) || duk_is_callable(thr, -1));
        }
    }

    /* Lightfuncs cannot be bound but are always callable and
     * constructable.
     */
}
//#line 1 "duk_api_codec.c"
/*
 *  Encoding and decoding basic formats: hex, base64.
 *
 *  These are in-place operations which may allow an optimized implementation.
 *
 *  Base-64: https://tools.ietf.org/html/rfc4648#section-4
 */

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

/*
 *  Misc helpers
 */

/* Shared handling for encode/decode argument.  Fast path handling for
 * buffer and string values because they're the most common.  In particular,
 * avoid creating a temporary string or buffer when possible.  Return value
 * is guaranteed to be non-NULL, even for zero length input.
 */
DUK_LOCAL const duk_uint8_t *duk__prep_codec_arg(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) {
    const void *def_ptr = (const void *) out_len;  /* Any non-NULL pointer will do. */
    const void *ptr;
    duk_bool_t isbuffer;

    DUK_ASSERT(out_len != NULL);
    DUK_ASSERT(def_ptr != NULL);
    DUK_ASSERT(duk_is_valid_index(thr, idx));  /* checked by caller */

    ptr = (const void *) duk_get_buffer_data_raw(thr, idx, out_len, NULL /*def_ptr*/, 0 /*def_size*/, 0 /*throw_flag*/, &isbuffer);
    if (isbuffer) {
        DUK_ASSERT(ptr != NULL || *out_len == 0U);
        if (DUK_UNLIKELY(ptr == NULL)) {
            ptr = def_ptr;
        }
        DUK_ASSERT(ptr != NULL);
    } else {
        /* For strings a non-NULL pointer is always guaranteed because
         * at least a NUL will be present.
         */
        ptr = (const void *) duk_to_lstring(thr, idx, out_len);
        DUK_ASSERT(ptr != NULL);
    }
    DUK_ASSERT(ptr != NULL);
    return (const duk_uint8_t *) ptr;
}

/*
 *  Base64
 */

#if defined(DUK_USE_BASE64_SUPPORT)
/* Bytes emitted for number of padding characters in range [0,4]. */
DUK_LOCAL const duk_int8_t duk__base64_decode_nequal_step[5] = {
    3,   /* #### -> 24 bits, emit 3 bytes */
    2,   /* ###= -> 18 bits, emit 2 bytes */
    1,   /* ##== -> 12 bits, emit 1 byte */
    -1,  /* #=== -> 6 bits, error */
    0,   /* ==== -> 0 bits, emit 0 bytes */
};

#if defined(DUK_USE_BASE64_FASTPATH)
DUK_LOCAL const duk_uint8_t duk__base64_enctab_fast[64] = {
    0x41U, 0x42U, 0x43U, 0x44U, 0x45U, 0x46U, 0x47U, 0x48U, 0x49U, 0x4aU, 0x4bU, 0x4cU, 0x4dU, 0x4eU, 0x4fU, 0x50U,  /* A...P */
    0x51U, 0x52U, 0x53U, 0x54U, 0x55U, 0x56U, 0x57U, 0x58U, 0x59U, 0x5aU, 0x61U, 0x62U, 0x63U, 0x64U, 0x65U, 0x66U,  /* Q...f */
    0x67U, 0x68U, 0x69U, 0x6aU, 0x6bU, 0x6cU, 0x6dU, 0x6eU, 0x6fU, 0x70U, 0x71U, 0x72U, 0x73U, 0x74U, 0x75U, 0x76U,  /* g...v */
    0x77U, 0x78U, 0x79U, 0x7aU, 0x30U, 0x31U, 0x32U, 0x33U, 0x34U, 0x35U, 0x36U, 0x37U, 0x38U, 0x39U, 0x2bU, 0x2fU   /* w.../ */
};
#endif  /* DUK_USE_BASE64_FASTPATH */

#if defined(DUK_USE_BASE64_FASTPATH)
/* Decode table for one byte of input:
 *   -1 = allowed whitespace
 *   -2 = padding
 *   -3 = error
 *    0...63 decoded bytes
 */
DUK_LOCAL const duk_int8_t duk__base64_dectab_fast[256] = {
    -3, -3, -3, -3, -3, -3, -3, -3, -3, -1, -1, -3, -3, -1, -3, -3,  /* 0x00...0x0f */
    -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,  /* 0x10...0x1f */
    -1, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, 62, -3, -3, -3, 63,  /* 0x20...0x2f */
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -3, -3, -3, -2, -3, -3,  /* 0x30...0x3f */
    -3,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,  /* 0x40...0x4f */
    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -3, -3, -3, -3, -3,  /* 0x50...0x5f */
    -3, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,  /* 0x60...0x6f */
    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -3, -3, -3, -3, -3,  /* 0x70...0x7f */
    -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,  /* 0x80...0x8f */
    -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,  /* 0x90...0x9f */
    -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,  /* 0xa0...0xaf */
    -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,  /* 0xb0...0xbf */
    -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,  /* 0xc0...0xcf */
    -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,  /* 0xd0...0xdf */
    -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,  /* 0xe0...0xef */
    -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3   /* 0xf0...0xff */
};
#endif  /* DUK_USE_BASE64_FASTPATH */

#if defined(DUK_USE_BASE64_FASTPATH)
DUK_LOCAL DUK_ALWAYS_INLINE void duk__base64_encode_fast_3(const duk_uint8_t *src, duk_uint8_t *dst) {
    duk_uint_t t;

    t = (duk_uint_t) src[0];
    t = (t << 8) + (duk_uint_t) src[1];
    t = (t << 8) + (duk_uint_t) src[2];

    dst[0] = duk__base64_enctab_fast[t >> 18];
    dst[1] = duk__base64_enctab_fast[(t >> 12) & 0x3fU];
    dst[2] = duk__base64_enctab_fast[(t >> 6) & 0x3fU];
    dst[3] = duk__base64_enctab_fast[t & 0x3fU];

#if 0
    /* Tested: not faster on x64, most likely due to aliasing between
     * output and input index computation.
     */
    /* aaaaaabb bbbbcccc ccdddddd */
    dst[0] = duk__base64_enctab_fast[(src[0] >> 2) & 0x3fU];
    dst[1] = duk__base64_enctab_fast[((src[0] << 4) & 0x30U) | ((src[1] >> 4) & 0x0fU)];
    dst[2] = duk__base64_enctab_fast[((src[1] << 2) & 0x3fU) | ((src[2] >> 6) & 0x03U)];
    dst[3] = duk__base64_enctab_fast[src[2] & 0x3fU];
#endif
}

DUK_LOCAL DUK_ALWAYS_INLINE void duk__base64_encode_fast_2(const duk_uint8_t *src, duk_uint8_t *dst) {
    duk_uint_t t;

    t = (duk_uint_t) src[0];
    t = (t << 8) + (duk_uint_t) src[1];
    dst[0] = duk__base64_enctab_fast[t >> 10];           /* XXXXXX-- -------- */
    dst[1] = duk__base64_enctab_fast[(t >> 4) & 0x3fU];  /* ------XX XXXX---- */
    dst[2] = duk__base64_enctab_fast[(t << 2) & 0x3fU];  /* -------- ----XXXX */
    dst[3] = DUK_ASC_EQUALS;
}

DUK_LOCAL DUK_ALWAYS_INLINE void duk__base64_encode_fast_1(const duk_uint8_t *src, duk_uint8_t *dst) {
    duk_uint_t t;

    t = (duk_uint_t) src[0];
    dst[0] = duk__base64_enctab_fast[t >> 2];            /* XXXXXX-- */
    dst[1] = duk__base64_enctab_fast[(t << 4) & 0x3fU];  /* ------XX */
    dst[2] = DUK_ASC_EQUALS;
    dst[3] = DUK_ASC_EQUALS;
}

DUK_LOCAL void duk__base64_encode_helper(const duk_uint8_t *src, duk_size_t srclen, duk_uint8_t *dst) {
    duk_size_t n;
    const duk_uint8_t *p;
    duk_uint8_t *q;

    n = srclen;
    p = src;
    q = dst;

    if (n >= 16U) {
        /* Fast path, unrolled by 4, allows interleaving.  Process
         * 12-byte input chunks which encode to 16-char output chunks.
         * Only enter when at least one block is emitted (avoids div+mul
         * for short inputs too).
         */
        const duk_uint8_t *p_end_fast;

        p_end_fast = p + ((n / 12U) * 12U);
        DUK_ASSERT(p_end_fast >= p + 12);
        do {
            duk__base64_encode_fast_3(p, q);
            duk__base64_encode_fast_3(p + 3, q + 4);
            duk__base64_encode_fast_3(p + 6, q + 8);
            duk__base64_encode_fast_3(p + 9, q + 12);
            p += 12;
            q += 16;
        } while (DUK_LIKELY(p != p_end_fast));

        DUK_ASSERT(src + srclen >= p);
        n = (duk_size_t) (src + srclen - p);
        DUK_ASSERT(n < 12U);
    }

    /* Remainder. */
    while (n >= 3U) {
        duk__base64_encode_fast_3(p, q);
        p += 3;
        q += 4;
        n -= 3U;
    }
    DUK_ASSERT(n == 0U || n == 1U || n == 2U);
    if (n == 1U) {
        duk__base64_encode_fast_1(p, q);
#if 0  /* Unnecessary. */
        p += 1;
        q += 4;
        n -= 1U;
#endif
    } else if (n == 2U) {
        duk__base64_encode_fast_2(p, q);
#if 0  /* Unnecessary. */
        p += 2;
        q += 4;
        n -= 2U;
#endif
    } else {
        DUK_ASSERT(n == 0U);  /* nothing to do */
        ;
    }
}
#else  /* DUK_USE_BASE64_FASTPATH */
DUK_LOCAL void duk__base64_encode_helper(const duk_uint8_t *src, duk_size_t srclen, duk_uint8_t *dst) {
    duk_small_uint_t i, npad;
    duk_uint_t t, x, y;
    const duk_uint8_t *p;
    const duk_uint8_t *p_end;
    duk_uint8_t *q;

    p = src;
    p_end = src + srclen;
    q = dst;
    npad = 0U;

    while (p < p_end) {
        /* Read 3 bytes into 't', padded by zero. */
        t = 0;
        for (i = 0; i < 3; i++) {
            t = t << 8;
            if (p < p_end) {
                t += (duk_uint_t) (*p++);
            } else {
                /* This only happens on the last loop and we're
                 * guaranteed to exit on the next loop.
                 */
                npad++;
            }
        }
        DUK_ASSERT(npad <= 2U);

        /* Emit 4 encoded characters.  If npad > 0, some of the
         * chars will be incorrect (zero bits) but we fix up the
         * padding after the loop.  A straightforward 64-byte
         * lookup would be faster and cleaner, but this is shorter.
         */
        for (i = 0; i < 4; i++) {
            x = ((t >> 18) & 0x3fU);
            t = t << 6;

            if (x <= 51U) {
                if (x <= 25) {
                    y = x + DUK_ASC_UC_A;
                } else {
                    y = x - 26 + DUK_ASC_LC_A;
                }
            } else {
                if (x <= 61U) {
                    y = x - 52 + DUK_ASC_0;
                } else if (x == 62) {
                    y = DUK_ASC_PLUS;
                } else {
                    DUK_ASSERT(x == 63);
                    y = DUK_ASC_SLASH;
                }
            }

            *q++ = (duk_uint8_t) y;
        }
    }

    /* Handle padding by rewriting 0-2 bogus characters at the end.
     *
     *  Missing bytes    npad     base64 example
     *    0               0         ####
     *    1               1         ###=
     *    2               2         ##==
     */
    DUK_ASSERT(npad <= 2U);
    while (npad > 0U) {
        *(q - npad) = DUK_ASC_EQUALS;
        npad--;
    }
}
#endif  /* DUK_USE_BASE64_FASTPATH */

#if defined(DUK_USE_BASE64_FASTPATH)
DUK_LOCAL duk_bool_t duk__base64_decode_helper(const duk_uint8_t *src, duk_size_t srclen, duk_uint8_t *dst, duk_uint8_t **out_dst_final) {
    duk_int_t x;
    duk_uint_t t;
    duk_small_uint_t n_equal;
    duk_int8_t step;
    const duk_uint8_t *p;
    const duk_uint8_t *p_end;
    const duk_uint8_t *p_end_safe;
    duk_uint8_t *q;

    DUK_ASSERT(src != NULL);  /* Required by pointer arithmetic below, which fails for NULL. */

    p = src;
    p_end = src + srclen;
    p_end_safe = p_end - 8;  /* If 'src <= src_end_safe', safe to read 8 bytes. */
    q = dst;

    /* Alternate between a fast path which processes clean groups with no
     * padding or whitespace, and a slow path which processes one arbitrary
     * group and then re-enters the fast path.  This handles e.g. base64
     * with newlines reasonably well because the majority of a line is in
     * the fast path.
     */
    for (;;) {
        /* Fast path, on each loop handle two 4-char input groups.
         * If both are clean, emit 6 bytes and continue.  If first
         * is clean, emit 3 bytes and drop out; otherwise emit
         * nothing and drop out.  This approach could be extended to
         * more groups per loop, but for inputs with e.g. periodic
         * newlines (which are common) it might not be an improvement.
         */
        while (DUK_LIKELY(p <= p_end_safe)) {
            duk_int_t t1, t2;

            /* The lookup byte is intentionally sign extended to
             * (at least) 32 bits and then ORed.  This ensures
             * that is at least 1 byte is negative, the highest
             * bit of the accumulator will be set at the end and
             * we don't need to check every byte.
             *
             * Read all input bytes first before writing output
             * bytes to minimize aliasing.
             */
            DUK_DDD(DUK_DDDPRINT("fast loop: p=%p, p_end_safe=%p, p_end=%p",
                                 (const void *) p, (const void *) p_end_safe, (const void *) p_end));

            t1 = (duk_int_t) duk__base64_dectab_fast[p[0]];
            t1 = (duk_int_t) ((duk_uint_t) t1 << 6) | (duk_int_t) duk__base64_dectab_fast[p[1]];
            t1 = (duk_int_t) ((duk_uint_t) t1 << 6) | (duk_int_t) duk__base64_dectab_fast[p[2]];
            t1 = (duk_int_t) ((duk_uint_t) t1 << 6) | (duk_int_t) duk__base64_dectab_fast[p[3]];

            t2 = (duk_int_t) duk__base64_dectab_fast[p[4]];
            t2 = (duk_int_t) ((duk_uint_t) t2 << 6) | (duk_int_t) duk__base64_dectab_fast[p[5]];
            t2 = (duk_int_t) ((duk_uint_t) t2 << 6) | (duk_int_t) duk__base64_dectab_fast[p[6]];
            t2 = (duk_int_t) ((duk_uint_t) t2 << 6) | (duk_int_t) duk__base64_dectab_fast[p[7]];

            q[0] = (duk_uint8_t) (((duk_uint_t) t1 >> 16) & 0xffU);
            q[1] = (duk_uint8_t) (((duk_uint_t) t1 >> 8) & 0xffU);
            q[2] = (duk_uint8_t) ((duk_uint_t) t1 & 0xffU);

            q[3] = (duk_uint8_t) (((duk_uint_t) t2 >> 16) & 0xffU);
            q[4] = (duk_uint8_t) (((duk_uint_t) t2 >> 8) & 0xffU);
            q[5] = (duk_uint8_t) ((duk_uint_t) t2 & 0xffU);

            /* Optimistic check using one branch. */
            if (DUK_LIKELY((t1 | t2) >= 0)) {
                p += 8;
                q += 6;
            } else if (t1 >= 0) {
                DUK_DDD(DUK_DDDPRINT("fast loop first group was clean, second was not, process one slow path group"));
                DUK_ASSERT(t2 < 0);
                p += 4;
                q += 3;
                break;
            } else {
                DUK_DDD(DUK_DDDPRINT("fast loop first group was not clean, second does not matter, process one slow path group"));
                DUK_ASSERT(t1 < 0);
                break;
            }
        }  /* fast path */

        /* Slow path step 1: try to scan a 4-character encoded group,
         * end-of-input, or start-of-padding.  We exit with:
         *   1. n_chars == 4: full group, no padding, no end-of-input.
         *   2. n_chars < 4: partial group (may also be 0), encountered
         *      padding or end of input.
         *
         * The accumulator is initialized to 1; this allows us to detect
         * a full group by comparing >= 0x1000000 without an extra
         * counter variable.
         */
        t = 1UL;
        for (;;) {
            DUK_DDD(DUK_DDDPRINT("slow loop: p=%p, p_end=%p, t=%lu",
                                 (const void *) p, (const void *) p_end, (unsigned long) t));

            if (DUK_LIKELY(p < p_end)) {
                x = duk__base64_dectab_fast[*p++];
                if (DUK_LIKELY(x >= 0)) {
                    DUK_ASSERT(x >= 0 && x <= 63);
                    t = (t << 6) + (duk_uint_t) x;
                    if (t >= 0x1000000UL) {
                        break;
                    }
                } else if (x == -1) {
                    continue;  /* allowed ascii whitespace */
                } else if (x == -2) {
                    p--;
                    break;  /* start of padding */
                } else {
                    DUK_ASSERT(x == -3);
                    goto decode_error;
                }
            } else {
                break;  /* end of input */
            }
        }  /* slow path step 1 */

        /* Complete the padding by simulating pad characters,
         * regardless of actual input padding chars.
         */
        n_equal = 0;
        while (t < 0x1000000UL) {
            t = (t << 6) + 0U;
            n_equal++;
        }

        /* Slow path step 2: deal with full/partial group, padding,
         * etc.  Note that for num chars in [0,3] we intentionally emit
         * 3 bytes but don't step forward that much, buffer space is
         * guaranteed in setup.
         *
         *  num chars:
         *   0      ####   no output (= step 0)
         *   1      #===   reject, 6 bits of data
         *   2      ##==   12 bits of data, output 1 byte (= step 1)
         *   3      ###=   18 bits of data, output 2 bytes (= step 2)
         *   4      ####   24 bits of data, output 3 bytes (= step 3)
         */
        q[0] = (duk_uint8_t) ((t >> 16) & 0xffU);
        q[1] = (duk_uint8_t) ((t >> 8) & 0xffU);
        q[2] = (duk_uint8_t) (t & 0xffU);

        DUK_ASSERT(n_equal <= 4);
        step = duk__base64_decode_nequal_step[n_equal];
        if (DUK_UNLIKELY(step < 0)) {
            goto decode_error;
        }
        q += step;

        /* Slow path step 3: read and ignore padding and whitespace
         * until (a) next non-padding and non-whitespace character
         * after which we resume the fast path, or (b) end of input.
         * This allows us to accept missing, partial, full, and extra
         * padding cases uniformly.  We also support concatenated
         * base-64 documents because we resume scanning afterwards.
         *
         * Note that to support concatenated documents well, the '='
         * padding found inside the input must also allow for 'extra'
         * padding.  For example, 'Zm===' decodes to 'f' and has one
         * extra padding char.  So, 'Zm===Zm' should decode 'ff', even
         * though the standard break-up would be 'Zm==' + '=Zm' which
         * doesn't make sense.
         *
         * We also accept prepended padding like '==Zm9', because it
         * is equivalent to an empty document with extra padding ('==')
         * followed by a valid document.
         */

        for (;;) {
            if (DUK_UNLIKELY(p >= p_end)) {
                goto done;
            }
            x = duk__base64_dectab_fast[*p++];
            if (x == -1 || x == -2) {
                ;  /* padding or whitespace, keep eating */
            } else {
                p--;
                break;  /* backtrack and go back to fast path, even for -1 */
            }
        }  /* slow path step 3 */
    }  /* outer fast+slow path loop */

 done:
    DUK_DDD(DUK_DDDPRINT("done; p=%p, p_end=%p",
                         (const void *) p, (const void *) p_end));

    DUK_ASSERT(p == p_end);

    *out_dst_final = q;
    return 1;

 decode_error:
    return 0;
}
#else  /* DUK_USE_BASE64_FASTPATH */
DUK_LOCAL duk_bool_t duk__base64_decode_helper(const duk_uint8_t *src, duk_size_t srclen, duk_uint8_t *dst, duk_uint8_t **out_dst_final) {
    duk_uint_t t, x;
    duk_int_t y;
    duk_int8_t step;
    const duk_uint8_t *p;
    const duk_uint8_t *p_end;
    duk_uint8_t *q;
    /* 0x09, 0x0a, or 0x0d */
    duk_uint32_t mask_white = (1U << 9) | (1U << 10) | (1U << 13);

    /* 't' tracks progress of the decoded group:
     *
     *  t == 1             no valid chars yet
     *  t >= 0x40          1x6 = 6 bits shifted in
     *  t >= 0x1000        2x6 = 12 bits shifted in
     *  t >= 0x40000       3x6 = 18 bits shifted in
     *  t >= 0x1000000     4x6 = 24 bits shifted in
     *
     * By initializing t=1 there's no need for a separate counter for
     * the number of characters found so far.
     */
    p = src;
    p_end = src + srclen;
    q = dst;
    t = 1UL;

    for (;;) {
        duk_small_uint_t n_equal;

        DUK_ASSERT(t >= 1U);
        if (p >= p_end) {
            /* End of input: if input exists, treat like
             * start of padding, finish the block, then
             * re-enter here to see we're done.
             */
            if (t == 1U) {
                break;
            } else {
                goto simulate_padding;
            }
        }

        x = *p++;

        if (x >= 0x41U) {
            /* Valid: a-z and A-Z. */
            DUK_ASSERT(x >= 0x41U && x <= 0xffU);
            if (x >= 0x61U && x <= 0x7aU) {
                y = (duk_int_t) x - 0x61 + 26;
            } else if (x <= 0x5aU) {
                y = (duk_int_t) x - 0x41;
            } else {
                goto decode_error;
            }
        } else if (x >= 0x30U) {
            /* Valid: 0-9 and =. */
            DUK_ASSERT(x >= 0x30U && x <= 0x40U);
            if (x <= 0x39U) {
                y = (duk_int_t) x - 0x30 + 52;
            } else if (x == 0x3dU) {
                /* Skip padding and whitespace unless we're in the
                 * middle of a block.  Otherwise complete group by
                 * simulating shifting in the correct padding.
                 */
                if (t == 1U) {
                    continue;
                }
                goto simulate_padding;
            } else {
                goto decode_error;
            }
        } else if (x >= 0x20U) {
            /* Valid: +, /, and 0x20 whitespace. */
            DUK_ASSERT(x >= 0x20U && x <= 0x2fU);
            if (x == 0x2bU) {
                y = 62;
            } else if (x == 0x2fU) {
                y = 63;
            } else if (x == 0x20U) {
                continue;
            } else {
                goto decode_error;
            }
        } else {
            /* Valid: whitespace. */
            duk_uint32_t m;
            DUK_ASSERT(x < 0x20U);  /* 0x00 to 0x1f */
            m = (1U << x);
            if (mask_white & m) {
                /* Allow basic ASCII whitespace. */
                continue;
            } else {
                goto decode_error;
            }
        }

        DUK_ASSERT(y >= 0 && y <= 63);
        t = (t << 6) + (duk_uint_t) y;
        if (t < 0x1000000UL) {
            continue;
        }
        /* fall through; no padding will be added */

     simulate_padding:
        n_equal = 0;
        while (t < 0x1000000UL) {
            t = (t << 6) + 0U;
            n_equal++;
        }

        /* Output 3 bytes from 't' and advance as needed. */
        q[0] = (duk_uint8_t) ((t >> 16) & 0xffU);
        q[1] = (duk_uint8_t) ((t >> 8) & 0xffU);
        q[2] = (duk_uint8_t) (t & 0xffU);

        DUK_ASSERT(n_equal <= 4U);
        step = duk__base64_decode_nequal_step[n_equal];
        if (step < 0) {
            goto decode_error;
        }
        q += step;

        /* Re-enter loop.  The actual padding characters are skipped
         * by the main loop.  This handles cases like missing, partial,
         * full, and extra padding, and allows parsing of concatenated
         * documents (with extra padding) like: Zm===Zm.  Also extra
         * prepended padding is accepted: ===Zm9v.
         */
        t = 1U;
    }
    DUK_ASSERT(t == 1UL);

    *out_dst_final = q;
    return 1;

 decode_error:
    return 0;
}
#endif  /* DUK_USE_BASE64_FASTPATH */

DUK_EXTERNAL const char *duk_base64_encode(duk_hthread *thr, duk_idx_t idx) {
    const duk_uint8_t *src;
    duk_size_t srclen;
    duk_size_t dstlen;
    duk_uint8_t *dst;
    const char *ret;

    DUK_ASSERT_API_ENTRY(thr);

    idx = duk_require_normalize_index(thr, idx);
    src = duk__prep_codec_arg(thr, idx, &srclen);
    DUK_ASSERT(src != NULL);

    /* Compute exact output length.  Computation must not wrap; this
     * limit works for 32-bit size_t:
     * >>> srclen = 3221225469
     * >>> '%x' % ((srclen + 2) / 3 * 4)
     * 'fffffffc'
     */
    if (srclen > 3221225469UL) {
        goto type_error;
    }
    dstlen = (srclen + 2U) / 3U * 4U;
    dst = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, dstlen);

    duk__base64_encode_helper((const duk_uint8_t *) src, srclen, dst);

    ret = duk_buffer_to_string(thr, -1);  /* Safe, result is ASCII. */
    duk_replace(thr, idx);
    return ret;

 type_error:
    DUK_ERROR_TYPE(thr, DUK_STR_BASE64_ENCODE_FAILED);
    DUK_WO_NORETURN(return NULL;);
}

DUK_EXTERNAL void duk_base64_decode(duk_hthread *thr, duk_idx_t idx) {
    const duk_uint8_t *src;
    duk_size_t srclen;
    duk_size_t dstlen;
    duk_uint8_t *dst;
    duk_uint8_t *dst_final;

    DUK_ASSERT_API_ENTRY(thr);

    idx = duk_require_normalize_index(thr, idx);
    src = duk__prep_codec_arg(thr, idx, &srclen);
    DUK_ASSERT(src != NULL);

    /* Round up and add safety margin.  Avoid addition before division to
     * avoid possibility of wrapping.  Margin includes +3 for rounding up,
     * and +3 for one extra group: the decoder may emit and then backtrack
     * a full group (3 bytes) from zero-sized input for technical reasons.
     * Similarly, 'xx' may ecause 1+3 = bytes to be emitted and then
     * backtracked.
     */
    dstlen = (srclen / 4) * 3 + 6;  /* upper limit, assuming no whitespace etc */
    dst = (duk_uint8_t *) duk_push_dynamic_buffer(thr, dstlen);
    /* Note: for dstlen=0, dst may be NULL */

    if (!duk__base64_decode_helper((const duk_uint8_t *) src, srclen, dst, &dst_final)) {
        goto type_error;
    }

    /* XXX: convert to fixed buffer? */
    (void) duk_resize_buffer(thr, -1, (duk_size_t) (dst_final - dst));
    duk_replace(thr, idx);
    return;

 type_error:
    DUK_ERROR_TYPE(thr, DUK_STR_BASE64_DECODE_FAILED);
    DUK_WO_NORETURN(return;);
}
#else  /* DUK_USE_BASE64_SUPPORT */
DUK_EXTERNAL const char *duk_base64_encode(duk_hthread *thr, duk_idx_t idx) {
    DUK_UNREF(idx);
    DUK_ERROR_UNSUPPORTED(thr);
    DUK_WO_NORETURN(return NULL;);
}

DUK_EXTERNAL void duk_base64_decode(duk_hthread *thr, duk_idx_t idx) {
    DUK_UNREF(idx);
    DUK_ERROR_UNSUPPORTED(thr);
    DUK_WO_NORETURN(return;);
}
#endif  /* DUK_USE_BASE64_SUPPORT */

/*
 *  Hex
 */

#if defined(DUK_USE_HEX_SUPPORT)
DUK_EXTERNAL const char *duk_hex_encode(duk_hthread *thr, duk_idx_t idx) {
    const duk_uint8_t *inp;
    duk_size_t len;
    duk_size_t i;
    duk_uint8_t *buf;
    const char *ret;
#if defined(DUK_USE_HEX_FASTPATH)
    duk_size_t len_safe;
    duk_uint16_t *p16;
#endif

    DUK_ASSERT_API_ENTRY(thr);

    idx = duk_require_normalize_index(thr, idx);
    inp = duk__prep_codec_arg(thr, idx, &len);
    DUK_ASSERT(inp != NULL);

    /* Fixed buffer, no zeroing because we'll fill all the data. */
    buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, len * 2);
    DUK_ASSERT(buf != NULL);

#if defined(DUK_USE_HEX_FASTPATH)
    DUK_ASSERT((((duk_size_t) buf) & 0x01U) == 0);   /* pointer is aligned, guaranteed for fixed buffer */
    p16 = (duk_uint16_t *) (void *) buf;
    len_safe = len & ~0x03U;
    for (i = 0; i < len_safe; i += 4) {
        p16[0] = duk_hex_enctab[inp[i]];
        p16[1] = duk_hex_enctab[inp[i + 1]];
        p16[2] = duk_hex_enctab[inp[i + 2]];
        p16[3] = duk_hex_enctab[inp[i + 3]];
        p16 += 4;
    }
    for (; i < len; i++) {
        *p16++ = duk_hex_enctab[inp[i]];
    }
#else  /* DUK_USE_HEX_FASTPATH */
    for (i = 0; i < len; i++) {
        duk_small_uint_t t;
        t = (duk_small_uint_t) inp[i];
        buf[i*2 + 0] = duk_lc_digits[t >> 4];
        buf[i*2 + 1] = duk_lc_digits[t & 0x0f];
    }
#endif  /* DUK_USE_HEX_FASTPATH */

    /* XXX: Using a string return value forces a string intern which is
     * not always necessary.  As a rough performance measure, hex encode
     * time for tests/perf/test-hex-encode.js dropped from ~35s to ~15s
     * without string coercion.  Change to returning a buffer and let the
     * caller coerce to string if necessary?
     */

    ret = duk_buffer_to_string(thr, -1);  /* Safe, result is ASCII. */
    duk_replace(thr, idx);
    return ret;
}

DUK_EXTERNAL void duk_hex_decode(duk_hthread *thr, duk_idx_t idx) {
    const duk_uint8_t *inp;
    duk_size_t len;
    duk_size_t i;
    duk_int_t t;
    duk_uint8_t *buf;
#if defined(DUK_USE_HEX_FASTPATH)
    duk_int_t chk;
    duk_uint8_t *p;
    duk_size_t len_safe;
#endif

    DUK_ASSERT_API_ENTRY(thr);

    idx = duk_require_normalize_index(thr, idx);
    inp = duk__prep_codec_arg(thr, idx, &len);
    DUK_ASSERT(inp != NULL);

    if (len & 0x01) {
        goto type_error;
    }

    /* Fixed buffer, no zeroing because we'll fill all the data. */
    buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, len / 2);
    DUK_ASSERT(buf != NULL);

#if defined(DUK_USE_HEX_FASTPATH)
    p = buf;
    len_safe = len & ~0x07U;
    for (i = 0; i < len_safe; i += 8) {
        t = ((duk_int_t) duk_hex_dectab_shift4[inp[i]]) |
            ((duk_int_t) duk_hex_dectab[inp[i + 1]]);
        chk = t;
        p[0] = (duk_uint8_t) t;
        t = ((duk_int_t) duk_hex_dectab_shift4[inp[i + 2]]) |
            ((duk_int_t) duk_hex_dectab[inp[i + 3]]);
        chk |= t;
        p[1] = (duk_uint8_t) t;
        t = ((duk_int_t) duk_hex_dectab_shift4[inp[i + 4]]) |
            ((duk_int_t) duk_hex_dectab[inp[i + 5]]);
        chk |= t;
        p[2] = (duk_uint8_t) t;
        t = ((duk_int_t) duk_hex_dectab_shift4[inp[i + 6]]) |
            ((duk_int_t) duk_hex_dectab[inp[i + 7]]);
        chk |= t;
        p[3] = (duk_uint8_t) t;
        p += 4;

        /* Check if any lookup above had a negative result. */
        if (DUK_UNLIKELY(chk < 0)) {
            goto type_error;
        }
    }
    for (; i < len; i += 2) {
        /* First cast to duk_int_t to sign extend, second cast to
         * duk_uint_t to avoid signed left shift, and final cast to
         * duk_int_t result type.
         */
        t = (duk_int_t) ((((duk_uint_t) (duk_int_t) duk_hex_dectab[inp[i]]) << 4U) |
                         ((duk_uint_t) (duk_int_t) duk_hex_dectab[inp[i + 1]]));
        if (DUK_UNLIKELY(t < 0)) {
            goto type_error;
        }
        *p++ = (duk_uint8_t) t;
    }
#else  /* DUK_USE_HEX_FASTPATH */
    for (i = 0; i < len; i += 2) {
        /* For invalid characters the value -1 gets extended to
         * at least 16 bits.  If either nybble is invalid, the
         * resulting 't' will be < 0.
         */
        t = (duk_int_t) ((((duk_uint_t) (duk_int_t) duk_hex_dectab[inp[i]]) << 4U) |
                         ((duk_uint_t) (duk_int_t) duk_hex_dectab[inp[i + 1]]));
        if (DUK_UNLIKELY(t < 0)) {
            goto type_error;
        }
        buf[i >> 1] = (duk_uint8_t) t;
    }
#endif  /* DUK_USE_HEX_FASTPATH */

    duk_replace(thr, idx);
    return;

 type_error:
    DUK_ERROR_TYPE(thr, DUK_STR_HEX_DECODE_FAILED);
    DUK_WO_NORETURN(return;);
}
#else  /* DUK_USE_HEX_SUPPORT */
DUK_EXTERNAL const char *duk_hex_encode(duk_hthread *thr, duk_idx_t idx) {
    DUK_UNREF(idx);
    DUK_ERROR_UNSUPPORTED(thr);
    DUK_WO_NORETURN(return NULL;);
}
DUK_EXTERNAL void duk_hex_decode(duk_hthread *thr, duk_idx_t idx) {
    DUK_UNREF(idx);
    DUK_ERROR_UNSUPPORTED(thr);
    DUK_WO_NORETURN(return;);
}
#endif  /* DUK_USE_HEX_SUPPORT */

/*
 *  JSON
 */

#if defined(DUK_USE_JSON_SUPPORT)
DUK_EXTERNAL const char *duk_json_encode(duk_hthread *thr, duk_idx_t idx) {
#if defined(DUK_USE_ASSERTIONS)
    duk_idx_t top_at_entry;
#endif
    const char *ret;

    DUK_ASSERT_API_ENTRY(thr);
#if defined(DUK_USE_ASSERTIONS)
    top_at_entry = duk_get_top(thr);
#endif

    idx = duk_require_normalize_index(thr, idx);
    duk_bi_json_stringify_helper(thr,
                                 idx /*idx_value*/,
                                 DUK_INVALID_INDEX /*idx_replacer*/,
                                 DUK_INVALID_INDEX /*idx_space*/,
                                 0 /*flags*/);
    DUK_ASSERT(duk_is_string(thr, -1));
    duk_replace(thr, idx);
    ret = duk_get_string(thr, idx);

    DUK_ASSERT(duk_get_top(thr) == top_at_entry);

    return ret;
}

DUK_EXTERNAL void duk_json_decode(duk_hthread *thr, duk_idx_t idx) {
#if defined(DUK_USE_ASSERTIONS)
    duk_idx_t top_at_entry;
#endif

    DUK_ASSERT_API_ENTRY(thr);
#if defined(DUK_USE_ASSERTIONS)
    top_at_entry = duk_get_top(thr);
#endif

    idx = duk_require_normalize_index(thr, idx);
    duk_bi_json_parse_helper(thr,
                             idx /*idx_value*/,
                             DUK_INVALID_INDEX /*idx_reviver*/,
                             0 /*flags*/);
    duk_replace(thr, idx);

    DUK_ASSERT(duk_get_top(thr) == top_at_entry);
}
#else  /* DUK_USE_JSON_SUPPORT */
DUK_EXTERNAL const char *duk_json_encode(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_UNREF(idx);
    DUK_ERROR_UNSUPPORTED(thr);
    DUK_WO_NORETURN(return NULL;);
}

DUK_EXTERNAL void duk_json_decode(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_UNREF(idx);
    DUK_ERROR_UNSUPPORTED(thr);
    DUK_WO_NORETURN(return;);
}
#endif  /* DUK_USE_JSON_SUPPORT */
//#line 1 "duk_api_compile.c"
/*
 *  Compilation and evaluation
 */

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

typedef struct duk__compile_raw_args duk__compile_raw_args;
struct duk__compile_raw_args {
    duk_size_t src_length;  /* should be first on 64-bit platforms */
    const duk_uint8_t *src_buffer;
    duk_uint_t flags;
};

/* Eval is just a wrapper now. */
DUK_EXTERNAL duk_int_t duk_eval_raw(duk_hthread *thr, const char *src_buffer, duk_size_t src_length, duk_uint_t flags) {
    duk_int_t rc;

    DUK_ASSERT_API_ENTRY(thr);

    /* Note: strictness is *not* inherited from the current Duktape/C.
     * This would be confusing because the current strictness state
     * depends on whether we're running inside a Duktape/C activation
     * (= strict mode) or outside of any activation (= non-strict mode).
     * See tests/api/test-eval-strictness.c for more discussion.
     */

    /* [ ... source? filename? ] (depends on flags) */

    rc = duk_compile_raw(thr, src_buffer, src_length, flags | DUK_COMPILE_EVAL);  /* may be safe, or non-safe depending on flags */

    /* [ ... closure/error ] */

    if (rc != DUK_EXEC_SUCCESS) {
        rc = DUK_EXEC_ERROR;
        goto got_rc;
    }

    duk_push_global_object(thr);  /* explicit 'this' binding, see GH-164 */

    if (flags & DUK_COMPILE_SAFE) {
        rc = duk_pcall_method(thr, 0);
    } else {
        duk_call_method(thr, 0);
        rc = DUK_EXEC_SUCCESS;
    }

    /* [ ... result/error ] */

 got_rc:
    if (flags & DUK_COMPILE_NORESULT) {
        duk_pop(thr);
    }

    return rc;
}

/* Helper which can be called both directly and with duk_safe_call(). */
DUK_LOCAL duk_ret_t duk__do_compile(duk_hthread *thr, void *udata) {
    duk__compile_raw_args *comp_args;
    duk_uint_t flags;
    duk_hcompfunc *h_templ;

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

    /* Note: strictness is not inherited from the current Duktape/C
     * context.  Otherwise it would not be possible to compile
     * non-strict code inside a Duktape/C activation (which is
     * always strict now).  See tests/api/test-eval-strictness.c
     * for discussion.
     */

    /* [ ... source? filename? ] (depends on flags) */

    comp_args = (duk__compile_raw_args *) udata;
    flags = comp_args->flags;

    if (flags & DUK_COMPILE_NOFILENAME) {
        /* Automatic filename: 'eval' or 'input'. */
        duk_push_hstring_stridx(thr, (flags & DUK_COMPILE_EVAL) ? DUK_STRIDX_EVAL : DUK_STRIDX_INPUT);
    }

    /* [ ... source? filename ] */

    if (!comp_args->src_buffer) {
        duk_hstring *h_sourcecode;

        h_sourcecode = duk_get_hstring(thr, -2);
        if ((flags & DUK_COMPILE_NOSOURCE) ||  /* args incorrect */
            (h_sourcecode == NULL)) {          /* e.g. duk_push_string_file_raw() pushed undefined */
            DUK_ERROR_TYPE(thr, DUK_STR_NO_SOURCECODE);
            DUK_WO_NORETURN(return 0;);
        }
        DUK_ASSERT(h_sourcecode != NULL);
        comp_args->src_buffer = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_sourcecode);
        comp_args->src_length = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_sourcecode);
    }
    DUK_ASSERT(comp_args->src_buffer != NULL);

    if (flags & DUK_COMPILE_FUNCTION) {
        flags |= DUK_COMPILE_EVAL | DUK_COMPILE_FUNCEXPR;
    }

    /* [ ... source? filename ] */

    duk_js_compile(thr, comp_args->src_buffer, comp_args->src_length, flags);

    /* [ ... source? func_template ] */

    if (flags & DUK_COMPILE_NOSOURCE) {
        ;
    } else {
        duk_remove_m2(thr);
    }

    /* [ ... func_template ] */

    h_templ = (duk_hcompfunc *) duk_known_hobject(thr, -1);
    duk_js_push_closure(thr,
                       h_templ,
                       thr->builtins[DUK_BIDX_GLOBAL_ENV],
                       thr->builtins[DUK_BIDX_GLOBAL_ENV],
                       1 /*add_auto_proto*/);
    duk_remove_m2(thr);   /* -> [ ... closure ] */

    /* [ ... closure ] */

    return 1;
}

DUK_EXTERNAL duk_int_t duk_compile_raw(duk_hthread *thr, const char *src_buffer, duk_size_t src_length, duk_uint_t flags) {
    duk__compile_raw_args comp_args_alloc;
    duk__compile_raw_args *comp_args = &comp_args_alloc;

    DUK_ASSERT_API_ENTRY(thr);

    if ((flags & DUK_COMPILE_STRLEN) && (src_buffer != NULL)) {
        /* String length is computed here to avoid multiple evaluation
         * of a macro argument in the calling side.
         */
        src_length = DUK_STRLEN(src_buffer);
    }

    comp_args->src_buffer = (const duk_uint8_t *) src_buffer;
    comp_args->src_length = src_length;
    comp_args->flags = flags;

    /* [ ... source? filename? ] (depends on flags) */

    if (flags & DUK_COMPILE_SAFE) {
        duk_int_t rc;
        duk_int_t nargs;
        duk_int_t nrets = 1;

        /* Arguments can be: [ source? filename? &comp_args] so that
         * nargs is 1 to 3.  Call site encodes the correct nargs count
         * directly into flags.
         */
        nargs = flags & 0x07;
        DUK_ASSERT(nargs == ((flags & DUK_COMPILE_NOSOURCE) ? 0 : 1) +
                            ((flags & DUK_COMPILE_NOFILENAME) ? 0 : 1));
        rc = duk_safe_call(thr, duk__do_compile, (void *) comp_args, nargs, nrets);

        /* [ ... closure ] */
        return rc;
    }

    (void) duk__do_compile(thr, (void *) comp_args);

    /* [ ... closure ] */
    return DUK_EXEC_SUCCESS;
}
//#line 1 "duk_api_debug.c"
/*
 *  Debugging related API calls
 */

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

#if defined(DUK_USE_JSON_SUPPORT)
DUK_EXTERNAL void duk_push_context_dump(duk_hthread *thr) {
    duk_idx_t idx;
    duk_idx_t top;

    DUK_ASSERT_API_ENTRY(thr);

    /* We don't duk_require_stack() here now, but rely on the caller having
     * enough space.
     */

    top = duk_get_top(thr);
    duk_push_bare_array(thr);
    for (idx = 0; idx < top; idx++) {
        duk_dup(thr, idx);
        duk_put_prop_index(thr, -2, (duk_uarridx_t) idx);
    }

    /* XXX: conversion errors should not propagate outwards.
     * Perhaps values need to be coerced individually?
     */
    duk_bi_json_stringify_helper(thr,
                                 duk_get_top_index(thr),  /*idx_value*/
                                 DUK_INVALID_INDEX,  /*idx_replacer*/
                                 DUK_INVALID_INDEX,  /*idx_space*/
                                 DUK_JSON_FLAG_EXT_CUSTOM |
                                 DUK_JSON_FLAG_ASCII_ONLY |
                                 DUK_JSON_FLAG_AVOID_KEY_QUOTES /*flags*/);

    duk_push_sprintf(thr, "ctx: top=%ld, stack=%s", (long) top, (const char *) duk_safe_to_string(thr, -1));
    duk_replace(thr, -3);  /* [ ... arr jsonx(arr) res ] -> [ ... res jsonx(arr) ] */
    duk_pop(thr);
    DUK_ASSERT(duk_is_string(thr, -1));
}
#else  /* DUK_USE_JSON_SUPPORT */
DUK_EXTERNAL void duk_push_context_dump(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ERROR_UNSUPPORTED(thr);
    DUK_WO_NORETURN(return;);
}
#endif  /* DUK_USE_JSON_SUPPORT */

#if defined(DUK_USE_DEBUGGER_SUPPORT)

DUK_EXTERNAL void duk_debugger_attach(duk_hthread *thr,
                                      duk_debug_read_function read_cb,
                                      duk_debug_write_function write_cb,
                                      duk_debug_peek_function peek_cb,
                                      duk_debug_read_flush_function read_flush_cb,
                                      duk_debug_write_flush_function write_flush_cb,
                                      duk_debug_request_function request_cb,
                                      duk_debug_detached_function detached_cb,
                                      void *udata) {
    duk_heap *heap;
    const char *str;
    duk_size_t len;

    /* XXX: should there be an error or an automatic detach if
     * already attached?
     */

    DUK_D(DUK_DPRINT("application called duk_debugger_attach()"));

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(read_cb != NULL);
    DUK_ASSERT(write_cb != NULL);
    /* Other callbacks are optional. */

    heap = thr->heap;
    heap->dbg_read_cb = read_cb;
    heap->dbg_write_cb = write_cb;
    heap->dbg_peek_cb = peek_cb;
    heap->dbg_read_flush_cb = read_flush_cb;
    heap->dbg_write_flush_cb = write_flush_cb;
    heap->dbg_request_cb = request_cb;
    heap->dbg_detached_cb = detached_cb;
    heap->dbg_udata = udata;
    heap->dbg_have_next_byte = 0;

    /* Start in paused state. */
    heap->dbg_processing = 0;
    heap->dbg_state_dirty = 0;
    heap->dbg_force_restart = 0;
    heap->dbg_pause_flags = 0;
    heap->dbg_pause_act = NULL;
    heap->dbg_pause_startline = 0;
    heap->dbg_exec_counter = 0;
    heap->dbg_last_counter = 0;
    heap->dbg_last_time = 0.0;
    duk_debug_set_paused(heap);  /* XXX: overlap with fields above */

    /* Send version identification and flush right afterwards.  Note that
     * we must write raw, unframed bytes here.
     */
    duk_push_sprintf(thr, "%ld %ld %s %s\n",
                     (long) DUK_DEBUG_PROTOCOL_VERSION,
                     (long) DUK_VERSION,
                     (const char *) DUK_GIT_DESCRIBE,
                     (const char *) DUK_USE_TARGET_INFO);
    str = duk_get_lstring(thr, -1, &len);
    DUK_ASSERT(str != NULL);
    duk_debug_write_bytes(thr, (const duk_uint8_t *) str, len);
    duk_debug_write_flush(thr);
    duk_pop(thr);
}

DUK_EXTERNAL void duk_debugger_detach(duk_hthread *thr) {
    DUK_D(DUK_DPRINT("application called duk_debugger_detach()"));

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(thr->heap != NULL);

    /* Can be called multiple times with no harm. */
    duk_debug_do_detach(thr->heap);
}

DUK_EXTERNAL void duk_debugger_cooperate(duk_hthread *thr) {
    duk_bool_t processed_messages;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(thr->heap != NULL);

    if (!duk_debug_is_attached(thr->heap)) {
        return;
    }
    if (thr->callstack_curr != NULL || thr->heap->dbg_processing) {
        /* Calling duk_debugger_cooperate() while Duktape is being
         * called into is not supported.  This is not a 100% check
         * but prevents any damage in most cases.
         */
        return;
    }

    processed_messages = duk_debug_process_messages(thr, 1 /*no_block*/);
    DUK_UNREF(processed_messages);
}

DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_hthread *thr, duk_idx_t nvalues) {
    duk_idx_t top;
    duk_idx_t idx;
    duk_bool_t ret = 0;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(thr->heap != NULL);

    DUK_D(DUK_DPRINT("application called duk_debugger_notify() with nvalues=%ld", (long) nvalues));

    top = duk_get_top(thr);
    if (top < nvalues) {
        DUK_ERROR_RANGE(thr, "not enough stack values for notify");
        DUK_WO_NORETURN(return 0;);
    }
    if (duk_debug_is_attached(thr->heap)) {
        duk_debug_write_notify(thr, DUK_DBG_CMD_APPNOTIFY);
        for (idx = top - nvalues; idx < top; idx++) {
            duk_tval *tv = DUK_GET_TVAL_POSIDX(thr, idx);
            duk_debug_write_tval(thr, tv);
        }
        duk_debug_write_eom(thr);

        /* Return non-zero (true) if we have a good reason to believe
         * the notify was delivered; if we're still attached at least
         * a transport error was not indicated by the transport write
         * callback.  This is not a 100% guarantee of course.
         */
        if (duk_debug_is_attached(thr->heap)) {
            ret = 1;
        }
    }
    duk_pop_n(thr, nvalues);
    return ret;
}

DUK_EXTERNAL void duk_debugger_pause(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(thr->heap != NULL);

    DUK_D(DUK_DPRINT("application called duk_debugger_pause()"));

    /* Treat like a debugger statement: ignore when not attached. */
    if (duk_debug_is_attached(thr->heap)) {
        if (duk_debug_is_paused(thr->heap)) {
            DUK_D(DUK_DPRINT("duk_debugger_pause() called when already paused; ignoring"));
        } else {
            duk_debug_set_paused(thr->heap);

            /* Pause on the next opcode executed.  This is always safe to do even
             * inside the debugger message loop: the interrupt counter will be reset
             * to its proper value when the message loop exits.
             */
            thr->interrupt_init = 1;
            thr->interrupt_counter = 0;
        }
    }
}

#else  /* DUK_USE_DEBUGGER_SUPPORT */

DUK_EXTERNAL void duk_debugger_attach(duk_hthread *thr,
                                      duk_debug_read_function read_cb,
                                      duk_debug_write_function write_cb,
                                      duk_debug_peek_function peek_cb,
                                      duk_debug_read_flush_function read_flush_cb,
                                      duk_debug_write_flush_function write_flush_cb,
                                      duk_debug_request_function request_cb,
                                      duk_debug_detached_function detached_cb,
                                      void *udata) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_UNREF(read_cb);
    DUK_UNREF(write_cb);
    DUK_UNREF(peek_cb);
    DUK_UNREF(read_flush_cb);
    DUK_UNREF(write_flush_cb);
    DUK_UNREF(request_cb);
    DUK_UNREF(detached_cb);
    DUK_UNREF(udata);
    DUK_ERROR_TYPE(thr, "no debugger support");
    DUK_WO_NORETURN(return;);
}

DUK_EXTERNAL void duk_debugger_detach(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ERROR_TYPE(thr, "no debugger support");
    DUK_WO_NORETURN(return;);
}

DUK_EXTERNAL void duk_debugger_cooperate(duk_hthread *thr) {
    /* nop */
    DUK_ASSERT_API_ENTRY(thr);
    DUK_UNREF(thr);
}

DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_hthread *thr, duk_idx_t nvalues) {
    duk_idx_t top;

    DUK_ASSERT_API_ENTRY(thr);

    top = duk_get_top(thr);
    if (top < nvalues) {
        DUK_ERROR_RANGE_INVALID_COUNT(thr);
        DUK_WO_NORETURN(return 0;);
    }

    /* No debugger support, just pop values. */
    duk_pop_n(thr, nvalues);
    return 0;
}

DUK_EXTERNAL void duk_debugger_pause(duk_hthread *thr) {
    /* Treat like debugger statement: nop */
    DUK_ASSERT_API_ENTRY(thr);
    DUK_UNREF(thr);
}

#endif  /* DUK_USE_DEBUGGER_SUPPORT */
//#line 1 "duk_api_heap.c"
/*
 *  Heap creation and destruction
 */

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

typedef struct duk_internal_thread_state duk_internal_thread_state;

struct duk_internal_thread_state {
    duk_ljstate lj;
    duk_bool_t creating_error;
    duk_hthread *curr_thread;
    duk_uint8_t thread_state;
    duk_int_t call_recursion_depth;
};

DUK_EXTERNAL duk_hthread *duk_create_heap(duk_alloc_function alloc_func,
                                          duk_realloc_function realloc_func,
                                          duk_free_function free_func,
                                          void *heap_udata,
                                          duk_fatal_function fatal_handler) {
    duk_heap *heap = NULL;
    duk_hthread *thr;

    /* Assume that either all memory funcs are NULL or non-NULL, mixed
     * cases will now be unsafe.
     */

    /* XXX: just assert non-NULL values here and make caller arguments
     * do the defaulting to the default implementations (smaller code)?
     */

    if (!alloc_func) {
        DUK_ASSERT(realloc_func == NULL);
        DUK_ASSERT(free_func == NULL);
#if defined(DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS)
        alloc_func = duk_default_alloc_function;
        realloc_func = duk_default_realloc_function;
        free_func = duk_default_free_function;
#else
        DUK_D(DUK_DPRINT("no allocation functions given and no default providers"));
        return NULL;
#endif
    } else {
        DUK_ASSERT(realloc_func != NULL);
        DUK_ASSERT(free_func != NULL);
    }

    if (!fatal_handler) {
        fatal_handler = duk_default_fatal_handler;
    }

    DUK_ASSERT(alloc_func != NULL);
    DUK_ASSERT(realloc_func != NULL);
    DUK_ASSERT(free_func != NULL);
    DUK_ASSERT(fatal_handler != NULL);

    heap = duk_heap_alloc(alloc_func, realloc_func, free_func, heap_udata, fatal_handler);
    if (!heap) {
        return NULL;
    }
    thr = heap->heap_thread;
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    return thr;
}

DUK_EXTERNAL void duk_destroy_heap(duk_hthread *thr) {
    duk_heap *heap;

    if (!thr) {
        return;
    }
    DUK_ASSERT_API_ENTRY(thr);
    heap = thr->heap;
    DUK_ASSERT(heap != NULL);

    duk_heap_free(heap);
}

DUK_EXTERNAL void duk_suspend(duk_hthread *thr, duk_thread_state *state) {
    duk_internal_thread_state *snapshot = (duk_internal_thread_state *) (void *) state;
    duk_heap *heap;
    duk_ljstate *lj;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT(state != NULL);  /* unvalidated */

    /* Currently not supported when called from within a finalizer.
     * If that is done, the finalizer will remain running indefinitely,
     * preventing other finalizers from executing.  The assert is a bit
     * wider, checking that it would be OK to run pending finalizers.
     */
    DUK_ASSERT(thr->heap->pf_prevent_count == 0);

    /* Currently not supported to duk_suspend() from an errCreate()
     * call.
     */
    DUK_ASSERT(thr->heap->creating_error == 0);

    heap = thr->heap;
    lj = &heap->lj;

    duk_push_tval(thr, &lj->value1);
    duk_push_tval(thr, &lj->value2);

    /* XXX: creating_error == 0 is asserted above, so no need to store. */
    duk_memcpy((void *) &snapshot->lj, (const void *) lj, sizeof(duk_ljstate));
    snapshot->creating_error = heap->creating_error;
    snapshot->curr_thread = heap->curr_thread;
    snapshot->thread_state = thr->state;
    snapshot->call_recursion_depth = heap->call_recursion_depth;

    lj->jmpbuf_ptr = NULL;
    lj->type = DUK_LJ_TYPE_UNKNOWN;
    DUK_TVAL_SET_UNDEFINED(&lj->value1);
    DUK_TVAL_SET_UNDEFINED(&lj->value2);
    heap->creating_error = 0;
    heap->curr_thread = NULL;
    heap->call_recursion_depth = 0;

    thr->state = DUK_HTHREAD_STATE_INACTIVE;
}

DUK_EXTERNAL void duk_resume(duk_hthread *thr, const duk_thread_state *state) {
    const duk_internal_thread_state *snapshot = (const duk_internal_thread_state *) (const void *) state;
    duk_heap *heap;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT(state != NULL);  /* unvalidated */

    /* Shouldn't be necessary if duk_suspend() is called before
     * duk_resume(), but assert in case API sequence is incorrect.
     */
    DUK_ASSERT(thr->heap->pf_prevent_count == 0);
    DUK_ASSERT(thr->heap->creating_error == 0);

    thr->state = snapshot->thread_state;

    heap = thr->heap;

    duk_memcpy((void *) &heap->lj, (const void *) &snapshot->lj, sizeof(duk_ljstate));
    heap->creating_error = snapshot->creating_error;
    heap->curr_thread = snapshot->curr_thread;
    heap->call_recursion_depth = snapshot->call_recursion_depth;

    duk_pop_2(thr);
}

/* XXX: better place for this */
DUK_EXTERNAL void duk_set_global_object(duk_hthread *thr) {
    duk_hobject *h_glob;
    duk_hobject *h_prev_glob;
    duk_hobjenv *h_env;
    duk_hobject *h_prev_env;

    DUK_ASSERT_API_ENTRY(thr);

    DUK_D(DUK_DPRINT("replace global object with: %!T", duk_get_tval(thr, -1)));

    h_glob = duk_require_hobject(thr, -1);
    DUK_ASSERT(h_glob != NULL);

    /*
     *  Replace global object.
     */

    h_prev_glob = thr->builtins[DUK_BIDX_GLOBAL];
    DUK_UNREF(h_prev_glob);
    thr->builtins[DUK_BIDX_GLOBAL] = h_glob;
    DUK_HOBJECT_INCREF(thr, h_glob);
    DUK_HOBJECT_DECREF_ALLOWNULL(thr, h_prev_glob);  /* side effects, in theory (referenced by global env) */

    /*
     *  Replace lexical environment for global scope
     *
     *  Create a new object environment for the global lexical scope.
     *  We can't just reset the _Target property of the current one,
     *  because the lexical scope is shared by other threads with the
     *  same (initial) built-ins.
     */

    h_env = duk_hobjenv_alloc(thr,
                              DUK_HOBJECT_FLAG_EXTENSIBLE |
                              DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV));
    DUK_ASSERT(h_env != NULL);
    DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_env) == NULL);

    DUK_ASSERT(h_env->target == NULL);
    DUK_ASSERT(h_glob != NULL);
    h_env->target = h_glob;
    DUK_HOBJECT_INCREF(thr, h_glob);
    DUK_ASSERT(h_env->has_this == 0);

    /* [ ... new_glob ] */

    h_prev_env = thr->builtins[DUK_BIDX_GLOBAL_ENV];
    thr->builtins[DUK_BIDX_GLOBAL_ENV] = (duk_hobject *) h_env;
    DUK_HOBJECT_INCREF(thr, (duk_hobject *) h_env);
    DUK_HOBJECT_DECREF_ALLOWNULL(thr, h_prev_env);  /* side effects */
    DUK_UNREF(h_env);  /* without refcounts */
    DUK_UNREF(h_prev_env);

    /* [ ... new_glob ] */

    duk_pop(thr);

    /* [ ... ] */
}
//#line 1 "duk_api_inspect.c"
/*
 *  Inspection
 */

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

/* For footprint efficient multiple value setting: arrays are much better than
 * varargs, format string with parsing is often better than string pointer arrays.
 */
DUK_LOCAL void duk__inspect_multiple_uint(duk_hthread *thr, const char *fmt, duk_int_t *vals) {
    duk_int_t val;
    const char *p;
    const char *p_curr;
    duk_size_t len;

    for (p = fmt;;) {
        len = DUK_STRLEN(p);
        p_curr = p;
        p += len + 1;
        if (len == 0) {
            /* Double NUL (= empty key) terminates. */
            break;
        }
        val = *vals++;
        if (val >= 0) {
            /* Negative values are markers to skip key. */
            duk_push_string(thr, p_curr);
            duk_push_int(thr, val);
            duk_put_prop(thr, -3);
        }
    }
}

/* Raw helper to extract internal information / statistics about a value.
 * The return value is an object with properties that are version specific.
 * The properties must not expose anything that would lead to security
 * issues (e.g. exposing compiled function 'data' buffer might be an issue).
 * Currently only counts and sizes and such are given so there shouldn't
 * be security implications.
 */

#define DUK__IDX_TYPE     0
#define DUK__IDX_ITAG     1
#define DUK__IDX_REFC     2
#define DUK__IDX_HBYTES   3
#define DUK__IDX_CLASS    4
#define DUK__IDX_PBYTES   5
#define DUK__IDX_ESIZE    6
#define DUK__IDX_ENEXT    7
#define DUK__IDX_ASIZE    8
#define DUK__IDX_HSIZE    9
#define DUK__IDX_BCBYTES  10
#define DUK__IDX_DBYTES   11
#define DUK__IDX_TSTATE   12
#define DUK__IDX_VARIANT  13

DUK_EXTERNAL void duk_inspect_value(duk_hthread *thr, duk_idx_t idx) {
    duk_tval *tv;
    duk_heaphdr *h;
    /* The temporary values should be in an array rather than individual
     * variables which (in practice) ensures that the compiler won't map
     * them to registers and emit a lot of unnecessary shuffling code.
     */
    duk_int_t vals[14];

    DUK_ASSERT_API_ENTRY(thr);

    /* Assume two's complement and set everything to -1. */
    duk_memset((void *) &vals, (int) 0xff, sizeof(vals));
    DUK_ASSERT(vals[DUK__IDX_TYPE] == -1);  /* spot check one */

    tv = duk_get_tval_or_unused(thr, idx);
    h = (DUK_TVAL_IS_HEAP_ALLOCATED(tv) ? DUK_TVAL_GET_HEAPHDR(tv) : NULL);

    vals[DUK__IDX_TYPE] = duk_get_type_tval(tv);
    vals[DUK__IDX_ITAG] = (duk_int_t) DUK_TVAL_GET_TAG(tv);

    duk_push_bare_object(thr);  /* Invalidates 'tv'. */
    tv = NULL;

    if (h == NULL) {
        goto finish;
    }
    duk_push_pointer(thr, (void *) h);
    duk_put_prop_literal(thr, -2, "hptr");

#if 0
    /* Covers a lot of information, e.g. buffer and string variants. */
    duk_push_uint(thr, (duk_uint_t) DUK_HEAPHDR_GET_FLAGS(h));
    duk_put_prop_literal(thr, -2, "hflags");
#endif

#if defined(DUK_USE_REFERENCE_COUNTING)
    vals[DUK__IDX_REFC] = (duk_int_t) DUK_HEAPHDR_GET_REFCOUNT(h);
#endif
    vals[DUK__IDX_VARIANT] = 0;

    /* Heaphdr size and additional allocation size, followed by
     * type specific stuff (with varying value count).
     */
    switch ((duk_small_int_t) DUK_HEAPHDR_GET_TYPE(h)) {
    case DUK_HTYPE_STRING: {
        duk_hstring *h_str = (duk_hstring *) h;
        vals[DUK__IDX_HBYTES] = (duk_int_t) (sizeof(duk_hstring) + DUK_HSTRING_GET_BYTELEN(h_str) + 1);
#if defined(DUK_USE_HSTRING_EXTDATA)
        if (DUK_HSTRING_HAS_EXTDATA(h_str)) {
            vals[DUK__IDX_VARIANT] = 1;
        }
#endif
        break;
    }
    case DUK_HTYPE_OBJECT: {
        duk_hobject *h_obj = (duk_hobject *) h;

        /* XXX: variants here are maybe pointless; class is enough? */
        if (DUK_HOBJECT_IS_ARRAY(h_obj)) {
            vals[DUK__IDX_HBYTES] = sizeof(duk_harray);
        } else if (DUK_HOBJECT_IS_COMPFUNC(h_obj)) {
            vals[DUK__IDX_HBYTES] = sizeof(duk_hcompfunc);
        } else if (DUK_HOBJECT_IS_NATFUNC(h_obj)) {
            vals[DUK__IDX_HBYTES] = sizeof(duk_hnatfunc);
        } else if (DUK_HOBJECT_IS_THREAD(h_obj)) {
            vals[DUK__IDX_HBYTES] = sizeof(duk_hthread);
            vals[DUK__IDX_TSTATE] = ((duk_hthread *) h_obj)->state;
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
        } else if (DUK_HOBJECT_IS_BUFOBJ(h_obj)) {
            vals[DUK__IDX_HBYTES] = sizeof(duk_hbufobj);
            /* XXX: some size information */
#endif
        } else {
            vals[DUK__IDX_HBYTES] = (duk_small_uint_t) sizeof(duk_hobject);
        }

        vals[DUK__IDX_CLASS] = (duk_int_t) DUK_HOBJECT_GET_CLASS_NUMBER(h_obj);
        vals[DUK__IDX_PBYTES] = (duk_int_t) DUK_HOBJECT_P_ALLOC_SIZE(h_obj);
        vals[DUK__IDX_ESIZE] = (duk_int_t) DUK_HOBJECT_GET_ESIZE(h_obj);
        vals[DUK__IDX_ENEXT] = (duk_int_t) DUK_HOBJECT_GET_ENEXT(h_obj);
        vals[DUK__IDX_ASIZE] = (duk_int_t) DUK_HOBJECT_GET_ASIZE(h_obj);
        vals[DUK__IDX_HSIZE] = (duk_int_t) DUK_HOBJECT_GET_HSIZE(h_obj);

        /* Note: e_next indicates the number of gc-reachable entries
         * in the entry part, and also indicates the index where the
         * next new property would be inserted.  It does *not* indicate
         * the number of non-NULL keys present in the object.  That
         * value could be counted separately but requires a pass through
         * the key list.
         */

        if (DUK_HOBJECT_IS_COMPFUNC(h_obj)) {
            duk_hbuffer *h_data = (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(thr->heap, (duk_hcompfunc *) h_obj);
            vals[DUK__IDX_BCBYTES] = (duk_int_t) (h_data ? DUK_HBUFFER_GET_SIZE(h_data) : 0);
        }
        break;
    }
    case DUK_HTYPE_BUFFER: {
        duk_hbuffer *h_buf = (duk_hbuffer *) h;

        if (DUK_HBUFFER_HAS_DYNAMIC(h_buf)) {
            if (DUK_HBUFFER_HAS_EXTERNAL(h_buf)) {
                vals[DUK__IDX_VARIANT] = 2;  /* buffer variant 2: external */
                vals[DUK__IDX_HBYTES] = (duk_uint_t) (sizeof(duk_hbuffer_external));
            } else {
                /* When alloc_size == 0 the second allocation may not
                 * actually exist.
                 */
                vals[DUK__IDX_VARIANT] = 1;  /* buffer variant 1: dynamic */
                vals[DUK__IDX_HBYTES] = (duk_uint_t) (sizeof(duk_hbuffer_dynamic));
            }
            vals[DUK__IDX_DBYTES] = (duk_int_t) (DUK_HBUFFER_GET_SIZE(h_buf));
        } else {
            DUK_ASSERT(vals[DUK__IDX_VARIANT] == 0);  /* buffer variant 0: fixed */
            vals[DUK__IDX_HBYTES] = (duk_int_t) (sizeof(duk_hbuffer_fixed) + DUK_HBUFFER_GET_SIZE(h_buf));
        }
        break;
    }
    }

 finish:
    duk__inspect_multiple_uint(thr,
        "type" "\x00" "itag" "\x00" "refc" "\x00" "hbytes" "\x00" "class" "\x00"
        "pbytes" "\x00" "esize" "\x00" "enext" "\x00" "asize" "\x00" "hsize" "\x00"
        "bcbytes" "\x00" "dbytes" "\x00" "tstate" "\x00" "variant" "\x00" "\x00",
        (duk_int_t *) &vals);
}

DUK_EXTERNAL void duk_inspect_callstack_entry(duk_hthread *thr, duk_int_t level) {
    duk_activation *act;
    duk_uint_fast32_t pc;
    duk_uint_fast32_t line;

    DUK_ASSERT_API_ENTRY(thr);

    /* -1   = top callstack entry
     * -2   = caller of level -1
     * etc
     */
    act = duk_hthread_get_activation_for_level(thr, level);
    if (act == NULL) {
        duk_push_undefined(thr);
        return;
    }
    duk_push_bare_object(thr);

    /* Relevant PC is just before current one because PC is
     * post-incremented.  This should match what error augment
     * code does.
     */
    pc = duk_hthread_get_act_prev_pc(thr, act);

    duk_push_tval(thr, &act->tv_func);

    duk_push_uint(thr, (duk_uint_t) pc);
    duk_put_prop_stridx_short(thr, -3, DUK_STRIDX_PC);

#if defined(DUK_USE_PC2LINE)
    line = duk_hobject_pc2line_query(thr, -1, pc);
#else
    line = 0;
#endif
    duk_push_uint(thr, (duk_uint_t) line);
    duk_put_prop_stridx_short(thr, -3, DUK_STRIDX_LINE_NUMBER);

    duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_LC_FUNCTION);
    /* Providing access to e.g. act->lex_env would be dangerous: these
     * internal structures must never be accessible to the application.
     * Duktape relies on them having consistent data, and this consistency
     * is only asserted for, not checked for.
     */
}

/* automatic undefs */
#undef DUK__IDX_ASIZE
#undef DUK__IDX_BCBYTES
#undef DUK__IDX_CLASS
#undef DUK__IDX_DBYTES
#undef DUK__IDX_ENEXT
#undef DUK__IDX_ESIZE
#undef DUK__IDX_HBYTES
#undef DUK__IDX_HSIZE
#undef DUK__IDX_ITAG
#undef DUK__IDX_PBYTES
#undef DUK__IDX_REFC
#undef DUK__IDX_TSTATE
#undef DUK__IDX_TYPE
#undef DUK__IDX_VARIANT
//#line 1 "duk_api_memory.c"
/*
 *  Memory calls.
 */

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

DUK_EXTERNAL void *duk_alloc_raw(duk_hthread *thr, duk_size_t size) {
    DUK_ASSERT_API_ENTRY(thr);

    return DUK_ALLOC_RAW(thr->heap, size);
}

DUK_EXTERNAL void duk_free_raw(duk_hthread *thr, void *ptr) {
    DUK_ASSERT_API_ENTRY(thr);

    DUK_FREE_RAW(thr->heap, ptr);
}

DUK_EXTERNAL void *duk_realloc_raw(duk_hthread *thr, void *ptr, duk_size_t size) {
    DUK_ASSERT_API_ENTRY(thr);

    return DUK_REALLOC_RAW(thr->heap, ptr, size);
}

DUK_EXTERNAL void *duk_alloc(duk_hthread *thr, duk_size_t size) {
    DUK_ASSERT_API_ENTRY(thr);

    return DUK_ALLOC(thr->heap, size);
}

DUK_EXTERNAL void duk_free(duk_hthread *thr, void *ptr) {
    DUK_ASSERT_API_ENTRY(thr);

    DUK_FREE_CHECKED(thr, ptr);
}

DUK_EXTERNAL void *duk_realloc(duk_hthread *thr, void *ptr, duk_size_t size) {
    DUK_ASSERT_API_ENTRY(thr);

    /*
     *  Note: since this is an exposed API call, there should be
     *  no way a mark-and-sweep could have a side effect on the
     *  memory allocation behind 'ptr'; the pointer should never
     *  be something that Duktape wants to change.
     *
     *  Thus, no need to use DUK_REALLOC_INDIRECT (and we don't
     *  have the storage location here anyway).
     */

    return DUK_REALLOC(thr->heap, ptr, size);
}

DUK_EXTERNAL void duk_get_memory_functions(duk_hthread *thr, duk_memory_functions *out_funcs) {
    duk_heap *heap;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(out_funcs != NULL);
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);

    heap = thr->heap;
    out_funcs->alloc_func = heap->alloc_func;
    out_funcs->realloc_func = heap->realloc_func;
    out_funcs->free_func = heap->free_func;
    out_funcs->udata = heap->heap_udata;
}

DUK_EXTERNAL void duk_gc(duk_hthread *thr, duk_uint_t flags) {
    duk_heap *heap;
    duk_small_uint_t ms_flags;

    DUK_ASSERT_API_ENTRY(thr);
    heap = thr->heap;
    DUK_ASSERT(heap != NULL);

    DUK_D(DUK_DPRINT("mark-and-sweep requested by application"));
    DUK_ASSERT(DUK_GC_COMPACT == DUK_MS_FLAG_EMERGENCY);  /* Compact flag is 1:1 with emergency flag which forces compaction. */
    ms_flags = (duk_small_uint_t) flags;
    duk_heap_mark_and_sweep(heap, ms_flags);
}
//#line 1 "duk_api_object.c"
/*
 *  Object handling: property access and other support functions.
 */

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

/*
 *  Property handling
 *
 *  The API exposes only the most common property handling functions.
 *  The caller can invoke ECMAScript built-ins for full control (e.g.
 *  defineProperty, getOwnPropertyDescriptor).
 */

DUK_EXTERNAL duk_bool_t duk_get_prop(duk_hthread *thr, duk_idx_t obj_idx) {
    duk_tval *tv_obj;
    duk_tval *tv_key;
    duk_bool_t rc;

    DUK_ASSERT_API_ENTRY(thr);

    /* Note: copying tv_obj and tv_key to locals to shield against a valstack
     * resize is not necessary for a property get right now.
     */

    tv_obj = duk_require_tval(thr, obj_idx);
    tv_key = duk_require_tval(thr, -1);

    rc = duk_hobject_getprop(thr, tv_obj, tv_key);
    DUK_ASSERT(rc == 0 || rc == 1);
    /* a value is left on stack regardless of rc */

    duk_remove_m2(thr);  /* remove key */
    DUK_ASSERT(duk_is_undefined(thr, -1) || rc == 1);
    return rc;  /* 1 if property found, 0 otherwise */
}

DUK_EXTERNAL duk_bool_t duk_get_prop_string(duk_hthread *thr, duk_idx_t obj_idx, const char *key) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(key != NULL);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    (void) duk_push_string(thr, key);
    return duk_get_prop(thr, obj_idx);
}

DUK_EXTERNAL duk_bool_t duk_get_prop_lstring(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(key != NULL);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    (void) duk_push_lstring(thr, key, key_len);
    return duk_get_prop(thr, obj_idx);
}

#if !defined(DUK_USE_PREFER_SIZE)
DUK_EXTERNAL duk_bool_t duk_get_prop_literal_raw(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(key != NULL);
    DUK_ASSERT(key[key_len] == (char) 0);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    (void) duk_push_literal_raw(thr, key, key_len);
    return duk_get_prop(thr, obj_idx);
}
#endif

DUK_EXTERNAL duk_bool_t duk_get_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx) {
    DUK_ASSERT_API_ENTRY(thr);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    duk_push_uarridx(thr, arr_idx);
    return duk_get_prop(thr, obj_idx);
}

DUK_EXTERNAL duk_bool_t duk_get_prop_heapptr(duk_hthread *thr, duk_idx_t obj_idx, void *ptr) {
    DUK_ASSERT_API_ENTRY(thr);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    (void) duk_push_heapptr(thr, ptr);  /* NULL -> 'undefined' */
    return duk_get_prop(thr, obj_idx);
}

DUK_INTERNAL duk_bool_t duk_get_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT_STRIDX_VALID(stridx);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    (void) duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx));
    return duk_get_prop(thr, obj_idx);
}

DUK_INTERNAL duk_bool_t duk_get_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) {
    return duk_get_prop_stridx(thr, (duk_idx_t) (duk_int16_t) (packed_args >> 16),
                                    (duk_small_uint_t) (packed_args & 0xffffUL));
}

DUK_INTERNAL duk_bool_t duk_get_prop_stridx_boolean(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_bool_t *out_has_prop) {
    duk_bool_t rc;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT_STRIDX_VALID(stridx);

    rc = duk_get_prop_stridx(thr, obj_idx, stridx);
    if (out_has_prop) {
        *out_has_prop = rc;
    }
    return duk_to_boolean_top_pop(thr);
}

/* This get variant is for internal use, it differs from standard
 * duk_get_prop() in that:
 *   - Object argument must be an object (primitive values not supported).
 *   - Key argument must be a string (no coercion).
 *   - Only own properties are checked (no inheritance).  Only "entry part"
 *     properties are checked (not array index properties).
 *   - Property must be a plain data property, not a getter.
 *   - Proxy traps are not triggered.
 */
DUK_INTERNAL duk_bool_t duk_xget_owndataprop(duk_hthread *thr, duk_idx_t obj_idx) {
    duk_hobject *h_obj;
    duk_hstring *h_key;
    duk_tval *tv_val;

    DUK_ASSERT_API_ENTRY(thr);

    /* Note: copying tv_obj and tv_key to locals to shield against a valstack
     * resize is not necessary for a property get right now.
     */

    h_obj = duk_get_hobject(thr, obj_idx);
    if (h_obj == NULL) {
        return 0;
    }
    h_key = duk_require_hstring(thr, -1);

    tv_val = duk_hobject_find_entry_tval_ptr(thr->heap, h_obj, h_key);
    if (tv_val == NULL) {
        return 0;
    }

    duk_push_tval(thr, tv_val);
    duk_remove_m2(thr);  /* remove key */

    return 1;
}

DUK_INTERNAL duk_bool_t duk_xget_owndataprop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT_STRIDX_VALID(stridx);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    (void) duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx));
    return duk_xget_owndataprop(thr, obj_idx);
}

DUK_INTERNAL duk_bool_t duk_xget_owndataprop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) {
    return duk_xget_owndataprop_stridx(thr, (duk_idx_t) (duk_int16_t) (packed_args >> 16),
                                       (duk_small_uint_t) (packed_args & 0xffffUL));
}

DUK_LOCAL duk_bool_t duk__put_prop_shared(duk_hthread *thr, duk_idx_t obj_idx, duk_idx_t idx_key) {
    duk_tval *tv_obj;
    duk_tval *tv_key;
    duk_tval *tv_val;
    duk_bool_t throw_flag;
    duk_bool_t rc;

    /* Note: copying tv_obj and tv_key to locals to shield against a valstack
     * resize is not necessary for a property put right now (putprop protects
     * against it internally).
     */

    /* Key and value indices are either (-2, -1) or (-1, -2).  Given idx_key,
     * idx_val is always (idx_key ^ 0x01).
     */
    DUK_ASSERT((idx_key == -2 && (idx_key ^ 1) == -1) ||
               (idx_key == -1 && (idx_key ^ 1) == -2));
    /* XXX: Direct access; faster validation. */
    tv_obj = duk_require_tval(thr, obj_idx);
    tv_key = duk_require_tval(thr, idx_key);
    tv_val = duk_require_tval(thr, idx_key ^ 1);
    throw_flag = duk_is_strict_call(thr);

    rc = duk_hobject_putprop(thr, tv_obj, tv_key, tv_val, throw_flag);
    DUK_ASSERT(rc == 0 || rc == 1);

    duk_pop_2(thr);  /* remove key and value */
    return rc;  /* 1 if property found, 0 otherwise */
}

DUK_EXTERNAL duk_bool_t duk_put_prop(duk_hthread *thr, duk_idx_t obj_idx) {
    DUK_ASSERT_API_ENTRY(thr);
    return duk__put_prop_shared(thr, obj_idx, -2);
}

DUK_EXTERNAL duk_bool_t duk_put_prop_string(duk_hthread *thr, duk_idx_t obj_idx, const char *key) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(key != NULL);

    /* Careful here and with other duk_put_prop_xxx() helpers: the
     * target object and the property value may be in the same value
     * stack slot (unusual, but still conceptually clear).
     */
    obj_idx = duk_normalize_index(thr, obj_idx);
    (void) duk_push_string(thr, key);
    return duk__put_prop_shared(thr, obj_idx, -1);
}

DUK_EXTERNAL duk_bool_t duk_put_prop_lstring(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(key != NULL);

    obj_idx = duk_normalize_index(thr, obj_idx);
    (void) duk_push_lstring(thr, key, key_len);
    return duk__put_prop_shared(thr, obj_idx, -1);
}

#if !defined(DUK_USE_PREFER_SIZE)
DUK_EXTERNAL duk_bool_t duk_put_prop_literal_raw(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(key != NULL);
    DUK_ASSERT(key[key_len] == (char) 0);

    obj_idx = duk_normalize_index(thr, obj_idx);
    (void) duk_push_literal_raw(thr, key, key_len);
    return duk__put_prop_shared(thr, obj_idx, -1);
}
#endif

DUK_EXTERNAL duk_bool_t duk_put_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx) {
    DUK_ASSERT_API_ENTRY(thr);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    duk_push_uarridx(thr, arr_idx);
    return duk__put_prop_shared(thr, obj_idx, -1);
}

DUK_EXTERNAL duk_bool_t duk_put_prop_heapptr(duk_hthread *thr, duk_idx_t obj_idx, void *ptr) {
    DUK_ASSERT_API_ENTRY(thr);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    (void) duk_push_heapptr(thr, ptr);  /* NULL -> 'undefined' */
    return duk__put_prop_shared(thr, obj_idx, -1);
}


DUK_INTERNAL duk_bool_t duk_put_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT_STRIDX_VALID(stridx);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx));
    return duk__put_prop_shared(thr, obj_idx, -1);
}

DUK_INTERNAL duk_bool_t duk_put_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) {
    return duk_put_prop_stridx(thr, (duk_idx_t) (duk_int16_t) (packed_args >> 16),
                                    (duk_small_uint_t) (packed_args & 0xffffUL));
}

DUK_EXTERNAL duk_bool_t duk_del_prop(duk_hthread *thr, duk_idx_t obj_idx) {
    duk_tval *tv_obj;
    duk_tval *tv_key;
    duk_bool_t throw_flag;
    duk_bool_t rc;

    DUK_ASSERT_API_ENTRY(thr);

    /* Note: copying tv_obj and tv_key to locals to shield against a valstack
     * resize is not necessary for a property delete right now.
     */

    tv_obj = duk_require_tval(thr, obj_idx);
    tv_key = duk_require_tval(thr, -1);
    throw_flag = duk_is_strict_call(thr);

    rc = duk_hobject_delprop(thr, tv_obj, tv_key, throw_flag);
    DUK_ASSERT(rc == 0 || rc == 1);

    duk_pop(thr);  /* remove key */
    return rc;
}

DUK_EXTERNAL duk_bool_t duk_del_prop_string(duk_hthread *thr, duk_idx_t obj_idx, const char *key) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(key != NULL);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    (void) duk_push_string(thr, key);
    return duk_del_prop(thr, obj_idx);
}

DUK_EXTERNAL duk_bool_t duk_del_prop_lstring(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(key != NULL);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    (void) duk_push_lstring(thr, key, key_len);
    return duk_del_prop(thr, obj_idx);
}

#if !defined(DUK_USE_PREFER_SIZE)
DUK_EXTERNAL duk_bool_t duk_del_prop_literal_raw(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(key != NULL);
    DUK_ASSERT(key[key_len] == (char) 0);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    (void) duk_push_literal_raw(thr, key, key_len);
    return duk_del_prop(thr, obj_idx);
}
#endif

DUK_EXTERNAL duk_bool_t duk_del_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx) {
    DUK_ASSERT_API_ENTRY(thr);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    duk_push_uarridx(thr, arr_idx);
    return duk_del_prop(thr, obj_idx);
}

DUK_EXTERNAL duk_bool_t duk_del_prop_heapptr(duk_hthread *thr, duk_idx_t obj_idx, void *ptr) {
    DUK_ASSERT_API_ENTRY(thr);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    (void) duk_push_heapptr(thr, ptr);  /* NULL -> 'undefined' */
    return duk_del_prop(thr, obj_idx);
}

DUK_INTERNAL duk_bool_t duk_del_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT_STRIDX_VALID(stridx);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx));
    return duk_del_prop(thr, obj_idx);
}

#if 0
DUK_INTERNAL duk_bool_t duk_del_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) {
    return duk_del_prop_stridx(thr, (duk_idx_t) (duk_int16_t) (packed_args >> 16),
                                    (duk_small_uint_t) (packed_args & 0xffffUL));
}
#endif

DUK_EXTERNAL duk_bool_t duk_has_prop(duk_hthread *thr, duk_idx_t obj_idx) {
    duk_tval *tv_obj;
    duk_tval *tv_key;
    duk_bool_t rc;

    DUK_ASSERT_API_ENTRY(thr);

    /* Note: copying tv_obj and tv_key to locals to shield against a valstack
     * resize is not necessary for a property existence check right now.
     */

    tv_obj = duk_require_tval(thr, obj_idx);
    tv_key = duk_require_tval(thr, -1);

    rc = duk_hobject_hasprop(thr, tv_obj, tv_key);
    DUK_ASSERT(rc == 0 || rc == 1);

    duk_pop(thr);  /* remove key */
    return rc;  /* 1 if property found, 0 otherwise */
}

DUK_EXTERNAL duk_bool_t duk_has_prop_string(duk_hthread *thr, duk_idx_t obj_idx, const char *key) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(key != NULL);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    (void) duk_push_string(thr, key);
    return duk_has_prop(thr, obj_idx);
}

DUK_EXTERNAL duk_bool_t duk_has_prop_lstring(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(key != NULL);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    (void) duk_push_lstring(thr, key, key_len);
    return duk_has_prop(thr, obj_idx);
}

#if !defined(DUK_USE_PREFER_SIZE)
DUK_EXTERNAL duk_bool_t duk_has_prop_literal_raw(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(key != NULL);
    DUK_ASSERT(key[key_len] == (char) 0);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    (void) duk_push_literal_raw(thr, key, key_len);
    return duk_has_prop(thr, obj_idx);
}
#endif

DUK_EXTERNAL duk_bool_t duk_has_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx) {
    DUK_ASSERT_API_ENTRY(thr);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    duk_push_uarridx(thr, arr_idx);
    return duk_has_prop(thr, obj_idx);
}

DUK_EXTERNAL duk_bool_t duk_has_prop_heapptr(duk_hthread *thr, duk_idx_t obj_idx, void *ptr) {
    DUK_ASSERT_API_ENTRY(thr);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    (void) duk_push_heapptr(thr, ptr);  /* NULL -> 'undefined' */
    return duk_has_prop(thr, obj_idx);
}

DUK_INTERNAL duk_bool_t duk_has_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT_STRIDX_VALID(stridx);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx));
    return duk_has_prop(thr, obj_idx);
}

#if 0
DUK_INTERNAL duk_bool_t duk_has_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) {
    return duk_has_prop_stridx(thr, (duk_idx_t) (duk_int16_t) (packed_args >> 16),
                                    (duk_small_uint_t) (packed_args & 0xffffUL));
}
#endif

/* Define own property without inheritance lookups and such.  This differs from
 * [[DefineOwnProperty]] because special behaviors (like Array 'length') are
 * not invoked by this method.  The caller must be careful to invoke any such
 * behaviors if necessary.
 */
DUK_INTERNAL void duk_xdef_prop(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t desc_flags) {
    duk_hobject *obj;
    duk_hstring *key;

    DUK_ASSERT_API_ENTRY(thr);

    obj = duk_require_hobject(thr, obj_idx);
    DUK_ASSERT(obj != NULL);
    key = duk_to_property_key_hstring(thr, -2);
    DUK_ASSERT(key != NULL);
    DUK_ASSERT(duk_require_tval(thr, -1) != NULL);

    duk_hobject_define_property_internal(thr, obj, key, desc_flags);

    duk_pop(thr);  /* pop key */
}

DUK_INTERNAL void duk_xdef_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx, duk_small_uint_t desc_flags) {
    duk_hobject *obj;

    DUK_ASSERT_API_ENTRY(thr);

    obj = duk_require_hobject(thr, obj_idx);
    DUK_ASSERT(obj != NULL);

    duk_hobject_define_property_internal_arridx(thr, obj, arr_idx, desc_flags);
    /* value popped by call */
}

DUK_INTERNAL void duk_xdef_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_small_uint_t desc_flags) {
    duk_hobject *obj;
    duk_hstring *key;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT_STRIDX_VALID(stridx);

    obj = duk_require_hobject(thr, obj_idx);
    DUK_ASSERT(obj != NULL);
    key = DUK_HTHREAD_GET_STRING(thr, stridx);
    DUK_ASSERT(key != NULL);
    DUK_ASSERT(duk_require_tval(thr, -1) != NULL);

    duk_hobject_define_property_internal(thr, obj, key, desc_flags);
    /* value popped by call */
}

DUK_INTERNAL void duk_xdef_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) {
    duk_xdef_prop_stridx(thr, (duk_idx_t) (duk_int8_t) (packed_args >> 24),
                              (duk_small_uint_t) (packed_args >> 8) & 0xffffUL,
                              (duk_small_uint_t) (packed_args & 0xffL));
}

#if 0  /*unused*/
DUK_INTERNAL void duk_xdef_prop_stridx_builtin(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_small_int_t builtin_idx, duk_small_uint_t desc_flags) {
    duk_hobject *obj;
    duk_hstring *key;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT_STRIDX_VALID(stridx);
    DUK_ASSERT_BIDX_VALID(builtin_idx);

    obj = duk_require_hobject(thr, obj_idx);
    DUK_ASSERT(obj != NULL);
    key = DUK_HTHREAD_GET_STRING(thr, stridx);
    DUK_ASSERT(key != NULL);

    duk_push_hobject(thr, thr->builtins[builtin_idx]);
    duk_hobject_define_property_internal(thr, obj, key, desc_flags);
    /* value popped by call */
}
#endif

/* This is a rare property helper; it sets the global thrower (E5 Section 13.2.3)
 * setter/getter into an object property.  This is needed by the 'arguments'
 * object creation code, function instance creation code, and Function.prototype.bind().
 */

DUK_INTERNAL void duk_xdef_prop_stridx_thrower(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) {
    DUK_ASSERT_API_ENTRY(thr);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    duk_push_hstring_stridx(thr, stridx);
    duk_push_hobject_bidx(thr, DUK_BIDX_TYPE_ERROR_THROWER);
    duk_dup_top(thr);
    duk_def_prop(thr, obj_idx, DUK_DEFPROP_HAVE_SETTER | DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_FORCE);  /* attributes always 0 */
}

/* Object.getOwnPropertyDescriptor() equivalent C binding. */
DUK_EXTERNAL void duk_get_prop_desc(duk_hthread *thr, duk_idx_t obj_idx, duk_uint_t flags) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_UNREF(flags);  /* no flags defined yet */

    duk_hobject_object_get_own_property_descriptor(thr, obj_idx);  /* [ ... key ] -> [ ... desc ] */
}

/* Object.defineProperty() equivalent C binding. */
DUK_EXTERNAL void duk_def_prop(duk_hthread *thr, duk_idx_t obj_idx, duk_uint_t flags) {
    duk_idx_t idx_base;
    duk_hobject *obj;
    duk_hstring *key;
    duk_idx_t idx_value;
    duk_hobject *get;
    duk_hobject *set;
    duk_uint_t is_data_desc;
    duk_uint_t is_acc_desc;

    DUK_ASSERT_API_ENTRY(thr);

    obj = duk_require_hobject(thr, obj_idx);

    is_data_desc = flags & (DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_HAVE_WRITABLE);
    is_acc_desc = flags & (DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER);
    if (is_data_desc && is_acc_desc) {
        /* "Have" flags must not be conflicting so that they would
         * apply to both a plain property and an accessor at the same
         * time.
         */
        goto fail_invalid_desc;
    }

    idx_base = duk_get_top_index(thr);
    if (flags & DUK_DEFPROP_HAVE_SETTER) {
        duk_require_type_mask(thr, idx_base, DUK_TYPE_MASK_UNDEFINED |
                                             DUK_TYPE_MASK_OBJECT |
                                             DUK_TYPE_MASK_LIGHTFUNC);
        set = duk_get_hobject_promote_lfunc(thr, idx_base);
        if (set != NULL && !DUK_HOBJECT_IS_CALLABLE(set)) {
            goto fail_not_callable;
        }
        idx_base--;
    } else {
        set = NULL;
    }
    if (flags & DUK_DEFPROP_HAVE_GETTER) {
        duk_require_type_mask(thr, idx_base, DUK_TYPE_MASK_UNDEFINED |
                                             DUK_TYPE_MASK_OBJECT |
                                             DUK_TYPE_MASK_LIGHTFUNC);
        get = duk_get_hobject_promote_lfunc(thr, idx_base);
        if (get != NULL && !DUK_HOBJECT_IS_CALLABLE(get)) {
            goto fail_not_callable;
        }
        idx_base--;
    } else {
        get = NULL;
    }
    if (flags & DUK_DEFPROP_HAVE_VALUE) {
        idx_value = idx_base;
        idx_base--;
    } else {
        idx_value = (duk_idx_t) -1;
    }
    key = duk_to_property_key_hstring(thr, idx_base);
    DUK_ASSERT(key != NULL);

    duk_require_valid_index(thr, idx_base);

    duk_hobject_define_property_helper(thr,
                                       flags /*defprop_flags*/,
                                       obj,
                                       key,
                                       idx_value,
                                       get,
                                       set,
                                       1 /*throw_flag*/);

    /* Clean up stack */

    duk_set_top(thr, idx_base);

    /* [ ... obj ... ] */

    return;

 fail_invalid_desc:
    DUK_ERROR_TYPE(thr, DUK_STR_INVALID_DESCRIPTOR);
    DUK_WO_NORETURN(return;);

 fail_not_callable:
    DUK_ERROR_TYPE(thr, DUK_STR_NOT_CALLABLE);
    DUK_WO_NORETURN(return;);
}

/*
 *  Object related
 */

DUK_EXTERNAL void duk_compact(duk_hthread *thr, duk_idx_t obj_idx) {
    duk_hobject *obj;

    DUK_ASSERT_API_ENTRY(thr);

    obj = duk_get_hobject(thr, obj_idx);
    if (obj) {
        /* Note: this may fail, caller should protect the call if necessary */
        duk_hobject_compact_props(thr, obj);
    }
}

DUK_INTERNAL void duk_compact_m1(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);

    duk_compact(thr, -1);
}

/* XXX: the duk_hobject_enum.c stack APIs should be reworked */

DUK_EXTERNAL void duk_enum(duk_hthread *thr, duk_idx_t obj_idx, duk_uint_t enum_flags) {
    DUK_ASSERT_API_ENTRY(thr);

    duk_dup(thr, obj_idx);
    duk_require_hobject_promote_mask(thr, -1, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
    duk_hobject_enumerator_create(thr, enum_flags);   /* [target] -> [enum] */
}

DUK_EXTERNAL duk_bool_t duk_next(duk_hthread *thr, duk_idx_t enum_index, duk_bool_t get_value) {
    DUK_ASSERT_API_ENTRY(thr);

    duk_require_hobject(thr, enum_index);
    duk_dup(thr, enum_index);
    return duk_hobject_enumerator_next(thr, get_value);
}

DUK_INTERNAL void duk_seal_freeze_raw(duk_hthread *thr, duk_idx_t obj_idx, duk_bool_t is_freeze) {
    duk_tval *tv;
    duk_hobject *h;

    DUK_ASSERT_API_ENTRY(thr);

    tv = duk_require_tval(thr, obj_idx);
    DUK_ASSERT(tv != NULL);

    /* Seal/freeze are quite rare in practice so it'd be nice to get the
     * correct behavior simply via automatic promotion (at the cost of some
     * memory churn).  However, the promoted objects don't behave the same,
     * e.g. promoted lightfuncs are extensible.
     */

    switch (DUK_TVAL_GET_TAG(tv)) {
    case DUK_TAG_BUFFER:
        /* Plain buffer: already sealed, but not frozen (and can't be frozen
         * because index properties can't be made non-writable.
         */
        if (is_freeze) {
            goto fail_cannot_freeze;
        }
        break;
    case DUK_TAG_LIGHTFUNC:
        /* Lightfunc: already sealed and frozen, success. */
        break;
    case DUK_TAG_OBJECT:
        h = DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(h != NULL);
        if (is_freeze && DUK_HOBJECT_IS_BUFOBJ(h)) {
            /* Buffer objects cannot be frozen because there's no internal
             * support for making virtual array indices non-writable.
             */
            DUK_DD(DUK_DDPRINT("cannot freeze a buffer object"));
            goto fail_cannot_freeze;
        }
        duk_hobject_object_seal_freeze_helper(thr, h, is_freeze);

        /* Sealed and frozen objects cannot gain any more properties,
         * so this is a good time to compact them.
         */
        duk_hobject_compact_props(thr, h);
        break;
    default:
        /* ES2015 Sections 19.1.2.5, 19.1.2.17 */
        break;
    }
    return;

 fail_cannot_freeze:
    DUK_ERROR_TYPE_INVALID_ARGS(thr);  /* XXX: proper error message */
    DUK_WO_NORETURN(return;);
}

DUK_EXTERNAL void duk_seal(duk_hthread *thr, duk_idx_t obj_idx) {
    DUK_ASSERT_API_ENTRY(thr);

    duk_seal_freeze_raw(thr, obj_idx, 0 /*is_freeze*/);
}

DUK_EXTERNAL void duk_freeze(duk_hthread *thr, duk_idx_t obj_idx) {
    DUK_ASSERT_API_ENTRY(thr);

    duk_seal_freeze_raw(thr, obj_idx, 1 /*is_freeze*/);
}

/*
 *  Helpers for writing multiple properties
 */

DUK_EXTERNAL void duk_put_function_list(duk_hthread *thr, duk_idx_t obj_idx, const duk_function_list_entry *funcs) {
    const duk_function_list_entry *ent = funcs;

    DUK_ASSERT_API_ENTRY(thr);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    if (ent != NULL) {
        while (ent->key != NULL) {
            duk_push_c_function(thr, ent->value, ent->nargs);
            duk_put_prop_string(thr, obj_idx, ent->key);
            ent++;
        }
    }
}

DUK_EXTERNAL void duk_put_number_list(duk_hthread *thr, duk_idx_t obj_idx, const duk_number_list_entry *numbers) {
    const duk_number_list_entry *ent = numbers;
    duk_tval *tv;

    DUK_ASSERT_API_ENTRY(thr);

    obj_idx = duk_require_normalize_index(thr, obj_idx);
    if (ent != NULL) {
        while (ent->key != NULL) {
            tv = thr->valstack_top++;
            DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv));  /* value stack init policy */
            DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(tv, ent->value);  /* no need for decref/incref */
            duk_put_prop_string(thr, obj_idx, ent->key);
            ent++;
        }
    }
}

/*
 *  Shortcut for accessing global object properties
 */

DUK_EXTERNAL duk_bool_t duk_get_global_string(duk_hthread *thr, const char *key) {
    duk_bool_t ret;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL);

    /* XXX: direct implementation */

    duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]);
    ret = duk_get_prop_string(thr, -1, key);
    duk_remove_m2(thr);
    return ret;
}

DUK_EXTERNAL duk_bool_t duk_get_global_lstring(duk_hthread *thr, const char *key, duk_size_t key_len) {
    duk_bool_t ret;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL);

    /* XXX: direct implementation */

    duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]);
    ret = duk_get_prop_lstring(thr, -1, key, key_len);
    duk_remove_m2(thr);
    return ret;
}

#if !defined(DUK_USE_PREFER_SIZE)
DUK_EXTERNAL duk_bool_t duk_get_global_literal_raw(duk_hthread *thr, const char *key, duk_size_t key_len) {
    duk_bool_t ret;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL);
    DUK_ASSERT(key[key_len] == (char) 0);

    /* XXX: direct implementation */

    duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]);
    ret = duk_get_prop_literal_raw(thr, -1, key, key_len);
    duk_remove_m2(thr);
    return ret;
}
#endif

DUK_EXTERNAL duk_bool_t duk_get_global_heapptr(duk_hthread *thr, void *ptr) {
    duk_bool_t ret;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL);

    /* XXX: direct implementation */

    duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]);
    ret = duk_get_prop_heapptr(thr, -1, ptr);
    duk_remove_m2(thr);
    return ret;
}


DUK_EXTERNAL duk_bool_t duk_put_global_string(duk_hthread *thr, const char *key) {
    duk_bool_t ret;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL);

    /* XXX: direct implementation */

    duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]);
    duk_insert(thr, -2);
    ret = duk_put_prop_string(thr, -2, key);  /* [ ... global val ] -> [ ... global ] */
    duk_pop(thr);
    return ret;
}

DUK_EXTERNAL duk_bool_t duk_put_global_lstring(duk_hthread *thr, const char *key, duk_size_t key_len) {
    duk_bool_t ret;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL);

    /* XXX: direct implementation */

    duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]);
    duk_insert(thr, -2);
    ret = duk_put_prop_lstring(thr, -2, key, key_len);  /* [ ... global val ] -> [ ... global ] */
    duk_pop(thr);
    return ret;
}

#if !defined(DUK_USE_PREFER_SIZE)
DUK_EXTERNAL duk_bool_t duk_put_global_literal_raw(duk_hthread *thr, const char *key, duk_size_t key_len) {
    duk_bool_t ret;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL);
    DUK_ASSERT(key[key_len] == (char) 0);

    /* XXX: direct implementation */

    duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]);
    duk_insert(thr, -2);
    ret = duk_put_prop_literal_raw(thr, -2, key, key_len);  /* [ ... global val ] -> [ ... global ] */
    duk_pop(thr);
    return ret;
}
#endif

DUK_EXTERNAL duk_bool_t duk_put_global_heapptr(duk_hthread *thr, void *ptr) {
    duk_bool_t ret;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL);

    /* XXX: direct implementation */

    duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]);
    duk_insert(thr, -2);
    ret = duk_put_prop_heapptr(thr, -2, ptr);  /* [ ... global val ] -> [ ... global ] */
    duk_pop(thr);
    return ret;
}

/*
 *  ES2015 GetMethod()
 */

DUK_INTERNAL duk_bool_t duk_get_method_stridx(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t stridx) {
    (void) duk_get_prop_stridx(thr, idx, stridx);
    if (duk_is_null_or_undefined(thr, -1)) {
        duk_pop_nodecref_unsafe(thr);
        return 0;
    }
    if (!duk_is_callable(thr, -1)) {
        DUK_ERROR_TYPE(thr, DUK_STR_NOT_CALLABLE);
        DUK_WO_NORETURN(return 0;);
    }
    return 1;
}

/*
 *  Object prototype
 */

DUK_EXTERNAL void duk_get_prototype(duk_hthread *thr, duk_idx_t idx) {
    duk_hobject *obj;
    duk_hobject *proto;

    DUK_ASSERT_API_ENTRY(thr);

    obj = duk_require_hobject(thr, idx);
    DUK_ASSERT(obj != NULL);

    /* XXX: shared helper for duk_push_hobject_or_undefined()? */
    proto = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, obj);
    if (proto) {
        duk_push_hobject(thr, proto);
    } else {
        duk_push_undefined(thr);
    }
}

DUK_EXTERNAL void duk_set_prototype(duk_hthread *thr, duk_idx_t idx) {
    duk_hobject *obj;
    duk_hobject *proto;

    DUK_ASSERT_API_ENTRY(thr);

    obj = duk_require_hobject(thr, idx);
    DUK_ASSERT(obj != NULL);
    duk_require_type_mask(thr, -1, DUK_TYPE_MASK_UNDEFINED |
                                   DUK_TYPE_MASK_OBJECT);
    proto = duk_get_hobject(thr, -1);
    /* proto can also be NULL here (allowed explicitly) */

#if defined(DUK_USE_ROM_OBJECTS)
    if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)) {
        DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONFIGURABLE);  /* XXX: "read only object"? */
        DUK_WO_NORETURN(return;);
    }
#endif

    DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, obj, proto);

    duk_pop(thr);
}

DUK_INTERNAL void duk_clear_prototype(duk_hthread *thr, duk_idx_t idx) {
    duk_hobject *obj;

    DUK_ASSERT_API_ENTRY(thr);

    obj = duk_require_hobject(thr, idx);
    DUK_ASSERT(obj != NULL);

#if defined(DUK_USE_ROM_OBJECTS)
    if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)) {
        DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONFIGURABLE);  /* XXX: "read only object"? */
        DUK_WO_NORETURN(return;);
    }
#endif

    DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, obj, NULL);
}

DUK_INTERNAL duk_bool_t duk_is_bare_object(duk_hthread *thr, duk_idx_t idx) {
    duk_hobject *obj;
    duk_hobject *proto;

    DUK_ASSERT_API_ENTRY(thr);

    obj = duk_require_hobject(thr, idx);
    DUK_ASSERT(obj != NULL);

    proto = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, obj);
    return (proto == NULL);
}

/*
 *  Object finalizer
 */

#if defined(DUK_USE_FINALIZER_SUPPORT)
/* XXX: these could be implemented as macros calling an internal function
 * directly.
 * XXX: same issue as with Duktape.fin: there's no way to delete the property
 * now (just set it to undefined).
 */
DUK_EXTERNAL void duk_get_finalizer(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);

    /* This get intentionally walks the inheritance chain at present,
     * which matches how the effective finalizer property is also
     * looked up in GC.
     */
    duk_get_prop_stridx(thr, idx, DUK_STRIDX_INT_FINALIZER);
}

DUK_EXTERNAL void duk_set_finalizer(duk_hthread *thr, duk_idx_t idx) {
    duk_hobject *h;
    duk_bool_t callable;

    DUK_ASSERT_API_ENTRY(thr);

    h = duk_require_hobject(thr, idx);  /* Get before 'put' so that 'idx' is correct. */
    callable = duk_is_callable(thr, -1);

    /* At present finalizer is stored as a hidden Symbol, with normal
     * inheritance and access control.  As a result, finalizer cannot
     * currently be set on a non-extensible (sealed or frozen) object.
     * It might be useful to allow it.
     */
    duk_put_prop_stridx(thr, idx, DUK_STRIDX_INT_FINALIZER);

    /* In addition to setting the finalizer property, keep a "have
     * finalizer" flag in duk_hobject in sync so that refzero can do
     * a very quick finalizer check by walking the prototype chain
     * and checking the flag alone.  (Note that this means that just
     * setting _Finalizer on an object won't affect finalizer checks.)
     *
     * NOTE: if the argument is a Proxy object, this flag will be set
     * on the Proxy, not the target.  As a result, the target won't get
     * a finalizer flag and the Proxy also won't be finalized as there's
     * an explicit Proxy check in finalization now.
     */
    if (callable) {
        DUK_HOBJECT_SET_HAVE_FINALIZER(h);
    } else {
        DUK_HOBJECT_CLEAR_HAVE_FINALIZER(h);
    }
}
#else  /* DUK_USE_FINALIZER_SUPPORT */
DUK_EXTERNAL void duk_get_finalizer(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_UNREF(idx);
    DUK_ERROR_UNSUPPORTED(thr);
    DUK_WO_NORETURN(return;);
}

DUK_EXTERNAL void duk_set_finalizer(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_UNREF(idx);
    DUK_ERROR_UNSUPPORTED(thr);
    DUK_WO_NORETURN(return;);
}
#endif  /* DUK_USE_FINALIZER_SUPPORT */
//#line 1 "duk_api_random.c"
/*
 *  Random numbers
 */

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

DUK_EXTERNAL duk_double_t duk_random(duk_hthread *thr) {
    return (duk_double_t) DUK_UTIL_GET_RANDOM_DOUBLE(thr);
}
//#line 1 "duk_api_stack.c"
/*
 *  API calls related to general value stack manipulation: resizing the value
 *  stack, pushing and popping values, type checking and reading values,
 *  coercing values, etc.
 *
 *  Also contains internal functions (such as duk_get_tval()), defined
 *  in duk_api_internal.h, with semantics similar to the public API.
 */

/* XXX: repetition of stack pre-checks -> helper or macro or inline */
/* XXX: shared api error strings, and perhaps even throw code for rare cases? */

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

/*
 *  Forward declarations
 */

DUK_LOCAL_DECL duk_idx_t duk__push_c_function_raw(duk_hthread *thr, duk_c_function func, duk_idx_t nargs, duk_uint_t flags, duk_small_uint_t proto_bidx);

/*
 *  Global state for working around missing variadic macros
 */

#if !defined(DUK_USE_VARIADIC_MACROS)
DUK_EXTERNAL const char *duk_api_global_filename = NULL;
DUK_EXTERNAL duk_int_t duk_api_global_line = 0;
#endif

/*
 *  Misc helpers
 */

DUK_LOCAL const char * const duk__symbol_type_strings[4] = {
    "hidden", "global", "local", "wellknown"
};

#if !defined(DUK_USE_PACKED_TVAL)
DUK_LOCAL const duk_uint_t duk__type_from_tag[] = {
    DUK_TYPE_NUMBER,
    DUK_TYPE_NUMBER,  /* fastint */
    DUK_TYPE_UNDEFINED,
    DUK_TYPE_NULL,
    DUK_TYPE_BOOLEAN,
    DUK_TYPE_POINTER,
    DUK_TYPE_LIGHTFUNC,
    DUK_TYPE_NONE,
    DUK_TYPE_STRING,
    DUK_TYPE_OBJECT,
    DUK_TYPE_BUFFER,
};
DUK_LOCAL const duk_uint_t duk__type_mask_from_tag[] = {
    DUK_TYPE_MASK_NUMBER,
    DUK_TYPE_MASK_NUMBER,  /* fastint */
    DUK_TYPE_MASK_UNDEFINED,
    DUK_TYPE_MASK_NULL,
    DUK_TYPE_MASK_BOOLEAN,
    DUK_TYPE_MASK_POINTER,
    DUK_TYPE_MASK_LIGHTFUNC,
    DUK_TYPE_MASK_NONE,
    DUK_TYPE_MASK_STRING,
    DUK_TYPE_MASK_OBJECT,
    DUK_TYPE_MASK_BUFFER,
};
#endif  /* !DUK_USE_PACKED_TVAL */

/* Assert that there's room for one value. */
#define DUK__ASSERT_SPACE() do { \
        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)
    if (DUK_TVAL_IS_FASTINT(tv)) {
        duk_int64_t t = DUK_TVAL_GET_FASTINT(tv);
        if (t < 0) {
            t = 0;
        }
#if (DUK_UINT_MAX <= 0xffffffffUL)
        /* Clamping only necessary for 32-bit ints. */
        else if (t > DUK_UINT_MAX) {
            t = DUK_UINT_MAX;
        }
#endif
        return (duk_uint_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 < 0.0) {
            /* covers -Infinity */
            return (duk_uint_t) 0;
        } else if (d > (duk_double_t) DUK_UINT_MAX) {
            /* covers +Infinity */
            return (duk_uint_t) DUK_UINT_MAX;
        } else {
            /* coerce towards zero */
            return (duk_uint_t) d;
        }
    }

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

    return def_value;
}

/*
 *  Stack index validation/normalization and getting a stack duk_tval ptr.
 *
 *  These are called by many API entrypoints so the implementations must be
 *  fast and "inlined".
 *
 *  There's some repetition because of this; keep the functions in sync.
 */

DUK_EXTERNAL duk_idx_t duk_normalize_index(duk_hthread *thr, duk_idx_t idx) {
    duk_uidx_t vs_size;
    duk_uidx_t uidx;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(DUK_INVALID_INDEX < 0);

    /* Care must be taken to avoid pointer wrapping in the index
     * validation.  For instance, on a 32-bit platform with 8-byte
     * duk_tval the index 0x20000000UL would wrap the memory space
     * once.
     */

    /* Assume value stack sizes (in elements) fits into duk_idx_t. */
    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
    vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom);
    DUK_ASSERT_DISABLE(vs_size >= 0);  /* unsigned */

    if (idx < 0) {
        uidx = vs_size + (duk_uidx_t) idx;
    } else {
        /* since index non-negative */
        DUK_ASSERT(idx != DUK_INVALID_INDEX);
        uidx = (duk_uidx_t) idx;
    }

    /* DUK_INVALID_INDEX won't be accepted as a valid index. */
    DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_size);

    if (DUK_LIKELY(uidx < vs_size)) {
        return (duk_idx_t) uidx;
    }
    return DUK_INVALID_INDEX;
}

DUK_EXTERNAL duk_idx_t duk_require_normalize_index(duk_hthread *thr, duk_idx_t idx) {
    duk_uidx_t vs_size;
    duk_uidx_t uidx;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(DUK_INVALID_INDEX < 0);

    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
    vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom);
    DUK_ASSERT_DISABLE(vs_size >= 0);  /* unsigned */

    if (idx < 0) {
        uidx = vs_size + (duk_uidx_t) idx;
    } else {
        DUK_ASSERT(idx != DUK_INVALID_INDEX);
        uidx = (duk_uidx_t) idx;
    }

    /* DUK_INVALID_INDEX won't be accepted as a valid index. */
    DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_size);

    if (DUK_LIKELY(uidx < vs_size)) {
        return (duk_idx_t) uidx;
    }
    DUK_ERROR_RANGE_INDEX(thr, idx);
    DUK_WO_NORETURN(return 0;);
}

DUK_INTERNAL duk_tval *duk_get_tval(duk_hthread *thr, duk_idx_t idx) {
    duk_uidx_t vs_size;
    duk_uidx_t uidx;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(DUK_INVALID_INDEX < 0);

    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
    vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom);
    DUK_ASSERT_DISABLE(vs_size >= 0);  /* unsigned */

    if (idx < 0) {
        uidx = vs_size + (duk_uidx_t) idx;
    } else {
        DUK_ASSERT(idx != DUK_INVALID_INDEX);
        uidx = (duk_uidx_t) idx;
    }

    /* DUK_INVALID_INDEX won't be accepted as a valid index. */
    DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_size);

    if (DUK_LIKELY(uidx < vs_size)) {
        return thr->valstack_bottom + uidx;
    }
    return NULL;
}

/* Variant of duk_get_tval() which is guaranteed to return a valid duk_tval
 * pointer.  When duk_get_tval() would return NULL, this variant returns a
 * pointer to a duk_tval with tag DUK_TAG_UNUSED.  This allows the call site
 * to avoid an unnecessary NULL check which sometimes leads to better code.
 * The return duk_tval is read only (at least for the UNUSED value).
 */
DUK_LOCAL const duk_tval_unused duk__const_tval_unused = DUK_TVAL_UNUSED_INITIALIZER();

DUK_INTERNAL duk_tval *duk_get_tval_or_unused(duk_hthread *thr, duk_idx_t idx) {
    duk_tval *tv;

    DUK_ASSERT_API_ENTRY(thr);

    tv = duk_get_tval(thr, idx);
    if (tv != NULL) {
        return tv;
    }
    return (duk_tval *) DUK_LOSE_CONST(&duk__const_tval_unused);
}

DUK_INTERNAL duk_tval *duk_require_tval(duk_hthread *thr, duk_idx_t idx) {
    duk_uidx_t vs_size;
    duk_uidx_t uidx;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(DUK_INVALID_INDEX < 0);

    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
    vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom);
    DUK_ASSERT_DISABLE(vs_size >= 0);  /* unsigned */

    /* Use unsigned arithmetic to optimize comparison. */
    if (idx < 0) {
        uidx = vs_size + (duk_uidx_t) idx;
    } else {
        DUK_ASSERT(idx != DUK_INVALID_INDEX);
        uidx = (duk_uidx_t) idx;
    }

    /* DUK_INVALID_INDEX won't be accepted as a valid index. */
    DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_size);

    if (DUK_LIKELY(uidx < vs_size)) {
        return thr->valstack_bottom + uidx;
    }
    DUK_ERROR_RANGE_INDEX(thr, idx);
    DUK_WO_NORETURN(return NULL;);
}

/* Non-critical. */
DUK_EXTERNAL duk_bool_t duk_is_valid_index(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(DUK_INVALID_INDEX < 0);

    return (duk_normalize_index(thr, idx) >= 0);
}

/* Non-critical. */
DUK_EXTERNAL void duk_require_valid_index(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(DUK_INVALID_INDEX < 0);

    if (DUK_UNLIKELY(duk_normalize_index(thr, idx) < 0)) {
        DUK_ERROR_RANGE_INDEX(thr, idx);
        DUK_WO_NORETURN(return;);
    }
}

/*
 *  Value stack top handling
 */

DUK_EXTERNAL duk_idx_t duk_get_top(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);

    return (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
}

/* Internal helper to get current top but to require a minimum top value
 * (TypeError if not met).
 */
DUK_INTERNAL duk_idx_t duk_get_top_require_min(duk_hthread *thr, duk_idx_t min_top) {
    duk_idx_t ret;

    DUK_ASSERT_API_ENTRY(thr);

    ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
    if (DUK_UNLIKELY(ret < min_top)) {
        DUK_ERROR_TYPE_INVALID_ARGS(thr);
        DUK_WO_NORETURN(return 0;);
    }
    return ret;
}

/* Set stack top within currently allocated range, but don't reallocate.
 * This is performance critical especially for call handling, so whenever
 * changing, profile and look at generated code.
 */
DUK_EXTERNAL void duk_set_top(duk_hthread *thr, duk_idx_t idx) {
    duk_uidx_t vs_size;
    duk_uidx_t vs_limit;
    duk_uidx_t uidx;
    duk_tval *tv;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(DUK_INVALID_INDEX < 0);

    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
    DUK_ASSERT(thr->valstack_end >= thr->valstack_bottom);
    vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom);
    vs_limit = (duk_uidx_t) (thr->valstack_end - thr->valstack_bottom);

    if (idx < 0) {
        /* Negative indices are always within allocated stack but
         * must not go below zero index.
         */
        uidx = vs_size + (duk_uidx_t) idx;
    } else {
        /* Positive index can be higher than valstack top but must
         * not go above allocated stack (equality is OK).
         */
        uidx = (duk_uidx_t) idx;
    }

    /* DUK_INVALID_INDEX won't be accepted as a valid index. */
    DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_size);
    DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_limit);

#if defined(DUK_USE_VALSTACK_UNSAFE)
    DUK_ASSERT(uidx <= vs_limit);
    DUK_UNREF(vs_limit);
#else
    if (DUK_UNLIKELY(uidx > vs_limit)) {
        DUK_ERROR_RANGE_INDEX(thr, idx);
        DUK_WO_NORETURN(return;);
    }
#endif
    DUK_ASSERT(uidx <= vs_limit);

    /* Handle change in value stack top.  Respect value stack
     * initialization policy: 'undefined' above top.  Note that
     * DECREF may cause a side effect that reallocates valstack,
     * so must relookup after DECREF.
     */

    if (uidx >= vs_size) {
        /* Stack size increases or stays the same. */
#if defined(DUK_USE_ASSERTIONS)
        duk_uidx_t count;

        count = uidx - vs_size;
        while (count != 0) {
            count--;
            tv = thr->valstack_top + count;
            DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv));
        }
#endif
        thr->valstack_top = thr->valstack_bottom + uidx;
    } else {
        /* Stack size decreases. */
#if defined(DUK_USE_REFERENCE_COUNTING)
        duk_uidx_t count;
        duk_tval *tv_end;

        count = vs_size - uidx;
        DUK_ASSERT(count > 0);
        tv = thr->valstack_top;
        tv_end = tv - count;
        DUK_ASSERT(tv > tv_end);  /* Because count > 0. */
        do {
            tv--;
            DUK_ASSERT(tv >= thr->valstack_bottom);
            DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ(thr, tv);
        } while (tv != tv_end);
        thr->valstack_top = tv_end;
        DUK_REFZERO_CHECK_FAST(thr);
#else  /* DUK_USE_REFERENCE_COUNTING */
        duk_uidx_t count;
        duk_tval *tv_end;

        count = vs_size - uidx;
        tv = thr->valstack_top;
        tv_end = tv - count;
        DUK_ASSERT(tv > tv_end);
        do {
            tv--;
            DUK_TVAL_SET_UNDEFINED(tv);
        } while (tv != tv_end);
        thr->valstack_top = tv_end;
#endif  /* DUK_USE_REFERENCE_COUNTING */
    }
}

/* Internal variant with a non-negative index and no runtime size checks. */
#if defined(DUK_USE_PREFER_SIZE)
DUK_INTERNAL void duk_set_top_unsafe(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);

    duk_set_top(thr, idx);
}
#else  /* DUK_USE_PREFER_SIZE */
DUK_INTERNAL void duk_set_top_unsafe(duk_hthread *thr, duk_idx_t idx) {
    duk_uidx_t uidx;
    duk_uidx_t vs_size;
    duk_tval *tv;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
    DUK_ASSERT(thr->valstack_end >= thr->valstack_bottom);
    DUK_ASSERT(idx >= 0);
    DUK_ASSERT(idx <= (duk_idx_t) (thr->valstack_end - thr->valstack_bottom));

    /* XXX: byte arithmetic */
    uidx = (duk_uidx_t) idx;
    vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom);

    if (uidx >= vs_size) {
        /* Stack size increases or stays the same. */
#if defined(DUK_USE_ASSERTIONS)
        duk_uidx_t count;

        count = uidx - vs_size;
        while (count != 0) {
            count--;
            tv = thr->valstack_top + count;
            DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv));
        }
#endif
        thr->valstack_top = thr->valstack_bottom + uidx;
    } else {
        /* Stack size decreases. */
#if defined(DUK_USE_REFERENCE_COUNTING)
        duk_uidx_t count;
        duk_tval *tv_end;

        count = vs_size - uidx;
        DUK_ASSERT(count > 0);
        tv = thr->valstack_top;
        tv_end = tv - count;
        DUK_ASSERT(tv > tv_end);  /* Because count > 0. */
        do {
            tv--;
            DUK_ASSERT(tv >= thr->valstack_bottom);
            DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ(thr, tv);
        } while (tv != tv_end);
        thr->valstack_top = tv_end;
        DUK_REFZERO_CHECK_FAST(thr);
#else  /* DUK_USE_REFERENCE_COUNTING */
        duk_uidx_t count;
        duk_tval *tv_end;

        count = vs_size - uidx;
        tv = thr->valstack_top;
        tv_end = tv - count;
        DUK_ASSERT(tv > tv_end);
        do {
            tv--;
            DUK_TVAL_SET_UNDEFINED(tv);
        } while (tv != tv_end);
        thr->valstack_top = tv_end;
#endif  /* DUK_USE_REFERENCE_COUNTING */
    }
}
#endif  /* DUK_USE_PREFER_SIZE */

/* Internal helper: set top to 'top', and set [idx_wipe_start,top[ to
 * 'undefined' (doing nothing if idx_wipe_start == top).  Indices are
 * positive and within value stack reserve.  This is used by call handling.
 */
DUK_INTERNAL void duk_set_top_and_wipe(duk_hthread *thr, duk_idx_t top, duk_idx_t idx_wipe_start) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(top >= 0);
    DUK_ASSERT(idx_wipe_start >= 0);
    DUK_ASSERT(idx_wipe_start <= top);
    DUK_ASSERT(thr->valstack_bottom + top <= thr->valstack_end);
    DUK_ASSERT(thr->valstack_bottom + idx_wipe_start <= thr->valstack_end);

    duk_set_top_unsafe(thr, idx_wipe_start);
    duk_set_top_unsafe(thr, top);
}

DUK_EXTERNAL duk_idx_t duk_get_top_index(duk_hthread *thr) {
    duk_idx_t ret;

    DUK_ASSERT_API_ENTRY(thr);

    ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom) - 1;
    if (DUK_UNLIKELY(ret < 0)) {
        /* Return invalid index; if caller uses this without checking
         * in another API call, the index won't map to a valid stack
         * entry.
         */
        return DUK_INVALID_INDEX;
    }
    return ret;
}

/* Internal variant: call assumes there is at least one element on the value
 * stack frame; this is only asserted for.
 */
DUK_INTERNAL duk_idx_t duk_get_top_index_unsafe(duk_hthread *thr) {
    duk_idx_t ret;

    DUK_ASSERT_API_ENTRY(thr);

    ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom) - 1;
    return ret;
}

DUK_EXTERNAL duk_idx_t duk_require_top_index(duk_hthread *thr) {
    duk_idx_t ret;

    DUK_ASSERT_API_ENTRY(thr);

    ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom) - 1;
    if (DUK_UNLIKELY(ret < 0)) {
        DUK_ERROR_RANGE_INDEX(thr, -1);
        DUK_WO_NORETURN(return 0;);
    }
    return ret;
}

/*
 *  Value stack resizing.
 *
 *  This resizing happens above the current "top": the value stack can be
 *  grown or shrunk, but the "top" is not affected.  The value stack cannot
 *  be resized to a size below the current reserve.
 *
 *  The low level reallocation primitive must carefully recompute all value
 *  stack pointers, and must also work if ALL pointers are NULL.  The resize
 *  is quite tricky because the valstack realloc may cause a mark-and-sweep,
 *  which may run finalizers.  Running finalizers may resize the valstack
 *  recursively (the same value stack we're working on).  So, after realloc
 *  returns, we know that the valstack bottom, top, and reserve should still
 *  be the same (there should not be live values above the "top"), but its
 *  underlying size, alloc_end, and base pointer may have changed.
 *
 *  'new_size' is known to be <= DUK_USE_VALSTACK_LIMIT, which ensures that
 *  size_t and pointer arithmetic won't wrap in duk__resize_valstack().
 */

/* Low level valstack resize primitive, used for both grow and shrink.  All
 * adjustments for slack etc have already been done.  Doesn't throw but does
 * have allocation side effects.
 */
DUK_LOCAL DUK_COLD DUK_NOINLINE duk_bool_t duk__resize_valstack(duk_hthread *thr, duk_size_t new_size) {
    duk_tval *pre_valstack;
    duk_tval *pre_bottom;
    duk_tval *pre_top;
    duk_tval *pre_end;
    duk_tval *pre_alloc_end;
    duk_ptrdiff_t ptr_diff;
    duk_tval *new_valstack;
    duk_size_t new_alloc_size;
    duk_tval *tv_prev_alloc_end;
    duk_tval *p;

    DUK_HTHREAD_ASSERT_VALID(thr);
    DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
    DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
    DUK_ASSERT(thr->valstack_alloc_end >= thr->valstack_end);
    DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack) <= new_size);  /* can't resize below 'top' */
    DUK_ASSERT(new_size <= DUK_USE_VALSTACK_LIMIT);  /* valstack limit caller has check, prevents wrapping */
    DUK_ASSERT(new_size <= DUK_SIZE_MAX / sizeof(duk_tval));  /* specific assert for wrapping */

    /* Pre-realloc pointer copies for asserts and debug logs. */
    pre_valstack = thr->valstack;
    pre_bottom = thr->valstack_bottom;
    pre_top = thr->valstack_top;
    pre_end = thr->valstack_end;
    pre_alloc_end = thr->valstack_alloc_end;

    DUK_UNREF(pre_valstack);
    DUK_UNREF(pre_bottom);
    DUK_UNREF(pre_top);
    DUK_UNREF(pre_end);
    DUK_UNREF(pre_alloc_end);

    /* If finalizer torture enabled, force base pointer change every time
     * when it would be allowed.
     */
#if defined(DUK_USE_FINALIZER_TORTURE)
    if (thr->heap->pf_prevent_count == 0) {
        duk_hthread_valstack_torture_realloc(thr);
    }
#endif

    /* Allocate a new valstack using DUK_REALLOC_DIRECT() to deal with
     * a side effect changing the base pointer.
     */
    new_alloc_size = sizeof(duk_tval) * new_size;
    new_valstack = (duk_tval *) DUK_REALLOC_INDIRECT(thr->heap, duk_hthread_get_valstack_ptr, (void *) thr, new_alloc_size);
    if (DUK_UNLIKELY(new_valstack == NULL)) {
        /* Because new_size != 0, if condition doesn't need to be
         * (new_valstack != NULL || new_size == 0).
         */
        DUK_ASSERT(new_size != 0);
        DUK_D(DUK_DPRINT("failed to resize valstack to %lu entries (%lu bytes)",
                         (unsigned long) new_size, (unsigned long) new_alloc_size));
        return 0;
    }

    /* Debug log any changes in pointer(s) by side effects.  These don't
     * necessarily imply any incorrect behavior, but should be rare in
     * practice.
     */
#if defined(DUK_USE_DEBUG)
    if (thr->valstack != pre_valstack) {
        DUK_D(DUK_DPRINT("valstack base pointer changed during valstack resize: %p -> %p",
                         (void *) pre_valstack, (void *) thr->valstack));
    }
    if (thr->valstack_bottom != pre_bottom) {
        DUK_D(DUK_DPRINT("valstack bottom pointer changed during valstack resize: %p -> %p",
                         (void *) pre_bottom, (void *) thr->valstack_bottom));
    }
    if (thr->valstack_top != pre_top) {
        DUK_D(DUK_DPRINT("valstack top pointer changed during valstack resize: %p -> %p",
                         (void *) pre_top, (void *) thr->valstack_top));
    }
    if (thr->valstack_end != pre_end) {
        DUK_D(DUK_DPRINT("valstack end pointer changed during valstack resize: %p -> %p",
                         (void *) pre_end, (void *) thr->valstack_end));
    }
    if (thr->valstack_alloc_end != pre_alloc_end) {
        DUK_D(DUK_DPRINT("valstack alloc_end pointer changed during valstack resize: %p -> %p",
                         (void *) pre_alloc_end, (void *) thr->valstack_alloc_end));
    }
#endif

    /* Assertions: offsets for bottom, top, and end (reserve) must not
     * have changed even with side effects because they are always
     * restored in unwind.  For alloc_end there's no guarantee: it may
     * have grown or shrunk (but remain above 'end').
     */
    DUK_ASSERT(thr->valstack_bottom - thr->valstack == pre_bottom - pre_valstack);
    DUK_ASSERT(thr->valstack_top - thr->valstack == pre_top - pre_valstack);
    DUK_ASSERT(thr->valstack_end - thr->valstack == pre_end - pre_valstack);
    DUK_ASSERT(thr->valstack_alloc_end >= thr->valstack_end);

    /* Write new pointers.  Most pointers can be handled as a pointer
     * difference.
     */
    ptr_diff = (duk_ptrdiff_t) ((duk_uint8_t *) new_valstack - (duk_uint8_t *) thr->valstack);
    tv_prev_alloc_end = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_alloc_end + ptr_diff);
    thr->valstack = new_valstack;
    thr->valstack_bottom = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_bottom + ptr_diff);
    thr->valstack_top = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_top + ptr_diff);
    thr->valstack_end = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_end + ptr_diff);
    thr->valstack_alloc_end = (duk_tval *) (void *) ((duk_uint8_t *) new_valstack + new_alloc_size);

    /* Assertions: pointer sanity after pointer updates. */
    DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
    DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
    DUK_ASSERT(thr->valstack_alloc_end >= thr->valstack_end);

    DUK_D(DUK_DPRINT("resized valstack %lu -> %lu elements (%lu -> %lu bytes): "
                     "base=%p -> %p, bottom=%p -> %p (%ld), top=%p -> %p (%ld), "
                     "end=%p -> %p (%ld), alloc_end=%p -> %p (%ld);"
                     " tv_prev_alloc_end=%p (-> %ld inits; <0 means shrink)",
                     (unsigned long) (pre_alloc_end - pre_valstack),
                     (unsigned long) new_size,
                     (unsigned long) ((duk_uint8_t *) pre_alloc_end - (duk_uint8_t *) pre_valstack),
                     (unsigned long) new_alloc_size,
                     (void *) pre_valstack, (void *) thr->valstack,
                     (void *) pre_bottom, (void *) thr->valstack_bottom, (long) (thr->valstack_bottom - thr->valstack),
                     (void *) pre_top, (void *) thr->valstack_top, (long) (thr->valstack_top - thr->valstack),
                     (void *) pre_end, (void *) thr->valstack_end, (long) (thr->valstack_end - thr->valstack),
                     (void *) pre_alloc_end, (void *) thr->valstack_alloc_end, (long) (thr->valstack_alloc_end - thr->valstack),
                     (void *) tv_prev_alloc_end, (long) (thr->valstack_alloc_end - tv_prev_alloc_end)));

    /* If allocation grew, init any new slots to 'undefined'. */
    p = tv_prev_alloc_end;
    while (p < thr->valstack_alloc_end) {
        /* Never executed if new size is smaller. */
        DUK_TVAL_SET_UNDEFINED(p);
        p++;
    }

    /* Assert for value stack initialization policy. */
#if defined(DUK_USE_ASSERTIONS)
    p = thr->valstack_top;
    while (p < thr->valstack_alloc_end) {
        DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(p));
        p++;
    }
#endif

    return 1;
}

DUK_LOCAL DUK_COLD DUK_NOINLINE duk_bool_t duk__valstack_grow(duk_hthread *thr, duk_size_t min_bytes, duk_bool_t throw_on_error) {
    duk_size_t min_size;
    duk_size_t new_size;

    DUK_ASSERT(min_bytes / sizeof(duk_tval) * sizeof(duk_tval) == min_bytes);
    min_size = min_bytes / sizeof(duk_tval);  /* from bytes to slots */

#if defined(DUK_USE_VALSTACK_GROW_SHIFT)
    /* New size is minimum size plus a proportional slack, e.g. shift of
     * 2 means a 25% slack.
     */
    new_size = min_size + (min_size >> DUK_USE_VALSTACK_GROW_SHIFT);
#else
    /* New size is tight with no slack.  This is sometimes preferred in
     * low memory environments.
     */
    new_size = min_size;
#endif

    if (DUK_UNLIKELY(new_size > DUK_USE_VALSTACK_LIMIT || new_size < min_size /*wrap*/)) {
        /* Note: may be triggered even if minimal new_size would not reach the limit,
         * plan limit accordingly.
         */
        if (throw_on_error) {
            DUK_ERROR_RANGE(thr, DUK_STR_VALSTACK_LIMIT);
            DUK_WO_NORETURN(return 0;);
        }
        return 0;
    }

    if (duk__resize_valstack(thr, new_size) == 0) {
        if (throw_on_error) {
            DUK_ERROR_ALLOC_FAILED(thr);
            DUK_WO_NORETURN(return 0;);
        }
        return 0;
    }

    thr->valstack_end = thr->valstack + min_size;
    DUK_ASSERT(thr->valstack_alloc_end >= thr->valstack_end);

    return 1;
}

/* Hot, inlined value stack grow check.  Because value stack almost never
 * grows, the actual resize call is in a NOINLINE helper.
 */
DUK_INTERNAL DUK_INLINE void duk_valstack_grow_check_throw(duk_hthread *thr, duk_size_t min_bytes) {
    duk_tval *tv;

    tv = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + min_bytes);
    if (DUK_LIKELY(thr->valstack_end >= tv)) {
        return;
    }
    if (DUK_LIKELY(thr->valstack_alloc_end >= tv)) {
        /* Values in [valstack_top,valstack_alloc_end[ are initialized
         * to 'undefined' so we can just move the end pointer.
         */
        thr->valstack_end = tv;
        return;
    }
    (void) duk__valstack_grow(thr, min_bytes, 1 /*throw_on_error*/);
}

/* Hot, inlined value stack grow check which doesn't throw. */
DUK_INTERNAL DUK_INLINE duk_bool_t duk_valstack_grow_check_nothrow(duk_hthread *thr, duk_size_t min_bytes) {
    duk_tval *tv;

    tv = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + min_bytes);
    if (DUK_LIKELY(thr->valstack_end >= tv)) {
        return 1;
    }
    if (DUK_LIKELY(thr->valstack_alloc_end >= tv)) {
        thr->valstack_end = tv;
        return 1;
    }
    return duk__valstack_grow(thr, min_bytes, 0 /*throw_on_error*/);
}

/* Value stack shrink check, called from mark-and-sweep. */
DUK_INTERNAL void duk_valstack_shrink_check_nothrow(duk_hthread *thr, duk_bool_t snug) {
    duk_size_t alloc_bytes;
    duk_size_t reserve_bytes;
    duk_size_t shrink_bytes;

    alloc_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_alloc_end - (duk_uint8_t *) thr->valstack);
    reserve_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack);
    DUK_ASSERT(alloc_bytes >= reserve_bytes);

    /* We're free to shrink the value stack allocation down to
     * reserve_bytes but not more.  If 'snug' (emergency GC)
     * shrink whatever we can.  Otherwise only shrink if the new
     * size would be considerably smaller.
     */

#if defined(DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT)
    if (snug) {
        shrink_bytes = reserve_bytes;
    } else {
        duk_size_t proportion, slack;

        /* Require that value stack shrinks by at least X% of its
         * current size.  For example, shift of 2 means at least
         * 25%.  The proportion is computed as bytes and may not
         * be a multiple of sizeof(duk_tval); that's OK here.
         */
        proportion = alloc_bytes >> DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT;
        if (alloc_bytes - reserve_bytes < proportion) {
            /* Too little would be freed, do nothing. */
            return;
        }

        /* Keep a slack after shrinking.  The slack is again a
         * proportion of the current size (the proportion should
         * of course be smaller than the check proportion above).
         */
#if defined(DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT)
        DUK_ASSERT(DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT > DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT);
        slack = alloc_bytes >> DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT;
#else
        slack = 0;
#endif
        shrink_bytes = reserve_bytes +
                       slack / sizeof(duk_tval) * sizeof(duk_tval);  /* multiple of duk_tval */
    }
#else  /* DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT */
    /* Always snug, useful in some low memory environments. */
    DUK_UNREF(snug);
    shrink_bytes = reserve_bytes;
#endif  /* DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT */

    DUK_D(DUK_DPRINT("valstack shrink check: alloc_bytes=%ld, reserve_bytes=%ld, shrink_bytes=%ld (unvalidated)",
                     (long) alloc_bytes, (long) reserve_bytes, (long) shrink_bytes));
    DUK_ASSERT(shrink_bytes >= reserve_bytes);
    if (shrink_bytes >= alloc_bytes) {
        /* Skip if shrink target is same as current one (or higher,
         * though that shouldn't happen in practice).
         */
        return;
    }
    DUK_ASSERT(shrink_bytes / sizeof(duk_tval) * sizeof(duk_tval) == shrink_bytes);

    DUK_D(DUK_DPRINT("valstack shrink check: decided to shrink, snug: %ld", (long) snug));

    duk__resize_valstack(thr, shrink_bytes / sizeof(duk_tval));
}

DUK_EXTERNAL duk_bool_t duk_check_stack(duk_hthread *thr, duk_idx_t extra) {
    duk_size_t min_new_bytes;

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

    if (DUK_UNLIKELY(extra < 0 || extra > DUK_USE_VALSTACK_LIMIT)) {
        if (extra < 0) {
            /* Clamping to zero makes the API more robust to calling code
             * calculation errors.
             */
            extra = 0;
        } else {
            /* Cause grow check to fail without wrapping arithmetic. */
            extra = DUK_USE_VALSTACK_LIMIT;
        }
    }

    min_new_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - (duk_uint8_t *) thr->valstack) +
                    sizeof(duk_tval) * ((duk_size_t) extra + DUK_VALSTACK_INTERNAL_EXTRA);
    return duk_valstack_grow_check_nothrow(thr, min_new_bytes);
}

DUK_EXTERNAL void duk_require_stack(duk_hthread *thr, duk_idx_t extra) {
    duk_size_t min_new_bytes;

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

    if (DUK_UNLIKELY(extra < 0 || extra > DUK_USE_VALSTACK_LIMIT)) {
        if (extra < 0) {
            /* Clamping to zero makes the API more robust to calling code
             * calculation errors.
             */
            extra = 0;
        } else {
            /* Cause grow check to fail without wrapping arithmetic. */
            extra = DUK_USE_VALSTACK_LIMIT;
        }
    }

    min_new_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - (duk_uint8_t *) thr->valstack) +
                    sizeof(duk_tval) * ((duk_size_t) extra + DUK_VALSTACK_INTERNAL_EXTRA);
    duk_valstack_grow_check_throw(thr, min_new_bytes);
}

DUK_EXTERNAL duk_bool_t duk_check_stack_top(duk_hthread *thr, duk_idx_t top) {
    duk_size_t min_new_bytes;

    DUK_ASSERT_API_ENTRY(thr);

    if (DUK_UNLIKELY(top < 0 || top > DUK_USE_VALSTACK_LIMIT)) {
        if (top < 0) {
            /* Clamping to zero makes the API more robust to calling code
             * calculation errors.
             */
            top = 0;
        } else {
            /* Cause grow check to fail without wrapping arithmetic. */
            top = DUK_USE_VALSTACK_LIMIT;
        }
    }

    DUK_ASSERT(top >= 0);
    min_new_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack) +
                    sizeof(duk_tval) * ((duk_size_t) top + DUK_VALSTACK_INTERNAL_EXTRA);
    return duk_valstack_grow_check_nothrow(thr, min_new_bytes);
}

DUK_EXTERNAL void duk_require_stack_top(duk_hthread *thr, duk_idx_t top) {
    duk_size_t min_new_bytes;

    DUK_ASSERT_API_ENTRY(thr);

    if (DUK_UNLIKELY(top < 0 || top > DUK_USE_VALSTACK_LIMIT)) {
        if (top < 0) {
            /* Clamping to zero makes the API more robust to calling code
             * calculation errors.
             */
            top = 0;
        } else {
            /* Cause grow check to fail without wrapping arithmetic. */
            top = DUK_USE_VALSTACK_LIMIT;
        }
    }

    DUK_ASSERT(top >= 0);
    min_new_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack) +
                    sizeof(duk_tval) * ((duk_size_t) top + DUK_VALSTACK_INTERNAL_EXTRA);
    duk_valstack_grow_check_throw(thr, min_new_bytes);
}

/*
 *  Basic stack manipulation: swap, dup, insert, replace, etc
 */

DUK_EXTERNAL void duk_swap(duk_hthread *thr, duk_idx_t idx1, duk_idx_t idx2) {
    duk_tval *tv1;
    duk_tval *tv2;
    duk_tval tv_tmp;

    DUK_ASSERT_API_ENTRY(thr);

    tv1 = duk_require_tval(thr, idx1);
    DUK_ASSERT(tv1 != NULL);
    tv2 = duk_require_tval(thr, idx2);
    DUK_ASSERT(tv2 != NULL);

    /* If tv1==tv2 this is a NOP, no check is needed */
    DUK_TVAL_SET_TVAL(&tv_tmp, tv1);
    DUK_TVAL_SET_TVAL(tv1, tv2);
    DUK_TVAL_SET_TVAL(tv2, &tv_tmp);
}

DUK_EXTERNAL void duk_swap_top(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);

    duk_swap(thr, idx, -1);
}

DUK_EXTERNAL void duk_dup(duk_hthread *thr, duk_idx_t from_idx) {
    duk_tval *tv_from;
    duk_tval *tv_to;

    DUK_ASSERT_API_ENTRY(thr);
    DUK__CHECK_SPACE();

    tv_from = duk_require_tval(thr, from_idx);
    tv_to = thr->valstack_top++;
    DUK_ASSERT(tv_from != NULL);
    DUK_ASSERT(tv_to != NULL);
    DUK_TVAL_SET_TVAL(tv_to, tv_from);
    DUK_TVAL_INCREF(thr, tv_to);  /* no side effects */
}

DUK_EXTERNAL void duk_dup_top(duk_hthread *thr) {
#if defined(DUK_USE_PREFER_SIZE)
    duk_dup(thr, -1);
#else
    duk_tval *tv_from;
    duk_tval *tv_to;

    DUK_ASSERT_API_ENTRY(thr);
    DUK__CHECK_SPACE();

    if (DUK_UNLIKELY(thr->valstack_top - thr->valstack_bottom <= 0)) {
        DUK_ERROR_RANGE_INDEX(thr, -1);
        DUK_WO_NORETURN(return;);
    }
    tv_from = thr->valstack_top - 1;
    tv_to = thr->valstack_top++;
    DUK_ASSERT(tv_from != NULL);
    DUK_ASSERT(tv_to != NULL);
    DUK_TVAL_SET_TVAL(tv_to, tv_from);
    DUK_TVAL_INCREF(thr, tv_to);  /* no side effects */
#endif
}

DUK_INTERNAL void duk_dup_0(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    duk_dup(thr, 0);
}
DUK_INTERNAL void duk_dup_1(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    duk_dup(thr, 1);
}
DUK_INTERNAL void duk_dup_2(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    duk_dup(thr, 2);
}
DUK_INTERNAL void duk_dup_m2(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    duk_dup(thr, -2);
}
DUK_INTERNAL void duk_dup_m3(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    duk_dup(thr, -3);
}
DUK_INTERNAL void duk_dup_m4(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    duk_dup(thr, -4);
}

DUK_EXTERNAL void duk_insert(duk_hthread *thr, duk_idx_t to_idx) {
    duk_tval *p;
    duk_tval *q;
    duk_tval tv_tmp;
    duk_size_t nbytes;

    DUK_ASSERT_API_ENTRY(thr);

    p = duk_require_tval(thr, to_idx);
    DUK_ASSERT(p != NULL);
    q = duk_require_tval(thr, -1);
    DUK_ASSERT(q != NULL);

    DUK_ASSERT(q >= p);

    /*              nbytes
     *           <--------->
     *    [ ... | p | x | x | q ]
     * => [ ... | q | p | x | x ]
     */

    nbytes = (duk_size_t) (((duk_uint8_t *) q) - ((duk_uint8_t *) p));

    DUK_DDD(DUK_DDDPRINT("duk_insert: to_idx=%ld, p=%p, q=%p, nbytes=%lu",
                         (long) to_idx, (void *) p, (void *) q, (unsigned long) nbytes));

    /* No net refcount changes.  No need to special case nbytes == 0
     * (p == q).
     */
    DUK_TVAL_SET_TVAL(&tv_tmp, q);
    duk_memmove((void *) (p + 1), (const void *) p, (size_t) nbytes);
    DUK_TVAL_SET_TVAL(p, &tv_tmp);
}

DUK_INTERNAL void duk_insert_undefined(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(idx >= 0);  /* Doesn't support negative indices. */

    duk_push_undefined(thr);
    duk_insert(thr, idx);
}

DUK_INTERNAL void duk_insert_undefined_n(duk_hthread *thr, duk_idx_t idx, duk_idx_t count) {
    duk_tval *tv, *tv_end;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(idx >= 0);  /* Doesn't support negative indices or count. */
    DUK_ASSERT(count >= 0);

    tv = duk_reserve_gap(thr, idx, count);
    tv_end = tv + count;
    while (tv != tv_end) {
        DUK_TVAL_SET_UNDEFINED(tv);
        tv++;
    }
}

DUK_EXTERNAL void duk_pull(duk_hthread *thr, duk_idx_t from_idx) {
    duk_tval *p;
    duk_tval *q;
    duk_tval tv_tmp;
    duk_size_t nbytes;

    DUK_ASSERT_API_ENTRY(thr);

    /*                         nbytes
     *                       <--------->
     *    [ ... | x | x | p | y | y | q ]
     * => [ ... | x | x | y | y | q | p ]
     */

    p = duk_require_tval(thr, from_idx);
    DUK_ASSERT(p != NULL);
    q = duk_require_tval(thr, -1);
    DUK_ASSERT(q != NULL);

    DUK_ASSERT(q >= p);

    nbytes = (duk_size_t) (((duk_uint8_t *) q) - ((duk_uint8_t *) p));

    DUK_DDD(DUK_DDDPRINT("duk_pull: from_idx=%ld, p=%p, q=%p, nbytes=%lu",
                         (long) from_idx, (void *) p, (void *) q, (unsigned long) nbytes));

    /* No net refcount changes.  No need to special case nbytes == 0
     * (p == q).
     */
    DUK_TVAL_SET_TVAL(&tv_tmp, p);
    duk_memmove((void *) p, (const void *) (p + 1), (size_t) nbytes);
    DUK_TVAL_SET_TVAL(q, &tv_tmp);
}

DUK_EXTERNAL void duk_replace(duk_hthread *thr, duk_idx_t to_idx) {
    duk_tval *tv1;
    duk_tval *tv2;
    duk_tval tv_tmp;

    DUK_ASSERT_API_ENTRY(thr);

    tv1 = duk_require_tval(thr, -1);
    DUK_ASSERT(tv1 != NULL);
    tv2 = duk_require_tval(thr, to_idx);
    DUK_ASSERT(tv2 != NULL);

    /* For tv1 == tv2, both pointing to stack top, the end result
     * is same as duk_pop(thr).
     */
    DUK_TVAL_SET_TVAL(&tv_tmp, tv2);
    DUK_TVAL_SET_TVAL(tv2, tv1);
    DUK_TVAL_SET_UNDEFINED(tv1);
    thr->valstack_top--;
    DUK_TVAL_DECREF(thr, &tv_tmp);  /* side effects */
}

DUK_EXTERNAL void duk_copy(duk_hthread *thr, duk_idx_t from_idx, duk_idx_t to_idx) {
    duk_tval *tv1;
    duk_tval *tv2;

    DUK_ASSERT_API_ENTRY(thr);

    tv1 = duk_require_tval(thr, from_idx);
    DUK_ASSERT(tv1 != NULL);
    tv2 = duk_require_tval(thr, to_idx);
    DUK_ASSERT(tv2 != NULL);

    /* For tv1 == tv2, this is a no-op (no explicit check needed). */
    DUK_TVAL_SET_TVAL_UPDREF(thr, tv2, tv1);  /* side effects */
}

DUK_EXTERNAL void duk_remove(duk_hthread *thr, duk_idx_t idx) {
    duk_tval *p;
    duk_tval *q;
#if defined(DUK_USE_REFERENCE_COUNTING)
    duk_tval tv_tmp;
#endif
    duk_size_t nbytes;

    DUK_ASSERT_API_ENTRY(thr);

    p = duk_require_tval(thr, idx);
    DUK_ASSERT(p != NULL);
    q = duk_require_tval(thr, -1);
    DUK_ASSERT(q != NULL);

    DUK_ASSERT(q >= p);

    /*              nbytes            zero size case
     *           <--------->
     *    [ ... | p | x | x | q ]     [ ... | p==q ]
     * => [ ... | x | x | q ]         [ ... ]
     */

#if defined(DUK_USE_REFERENCE_COUNTING)
    /* use a temp: decref only when valstack reachable values are correct */
    DUK_TVAL_SET_TVAL(&tv_tmp, p);
#endif

    nbytes = (duk_size_t) (((duk_uint8_t *) q) - ((duk_uint8_t *) p));  /* Note: 'q' is top-1 */
    duk_memmove((void *) p, (const void *) (p + 1), (size_t) nbytes);

    DUK_TVAL_SET_UNDEFINED(q);
    thr->valstack_top--;

#if defined(DUK_USE_REFERENCE_COUNTING)
    DUK_TVAL_DECREF(thr, &tv_tmp);  /* side effects */
#endif
}

DUK_INTERNAL void duk_remove_unsafe(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);

    duk_remove(thr, idx);  /* XXX: no optimization for now */
}

DUK_INTERNAL void duk_remove_m2(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);

    duk_remove(thr, -2);
}

DUK_INTERNAL void duk_remove_n(duk_hthread *thr, duk_idx_t idx, duk_idx_t count) {
#if defined(DUK_USE_PREFER_SIZE)
    /* XXX: maybe too slow even when preferring size? */
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(count >= 0);
    DUK_ASSERT(idx >= 0);

    while (count-- > 0) {
        duk_remove(thr, idx);
    }
#else  /* DUK_USE_PREFER_SIZE */
    duk_tval *tv_src;
    duk_tval *tv_dst;
    duk_tval *tv_newtop;
    duk_tval *tv;
    duk_size_t bytes;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(count >= 0);
    DUK_ASSERT(idx >= 0);

    tv_dst = thr->valstack_bottom + idx;
    DUK_ASSERT(tv_dst <= thr->valstack_top);
    tv_src = tv_dst + count;
    DUK_ASSERT(tv_src <= thr->valstack_top);
    bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - (duk_uint8_t *) tv_src);

    for (tv = tv_dst; tv < tv_src; tv++) {
        DUK_TVAL_DECREF_NORZ(thr, tv);
    }

    duk_memmove((void *) tv_dst, (const void *) tv_src, bytes);

    tv_newtop = thr->valstack_top - count;
    for (tv = tv_newtop; tv < thr->valstack_top; tv++) {
        DUK_TVAL_SET_UNDEFINED(tv);
    }
    thr->valstack_top = tv_newtop;

    /* When not preferring size, only NORZ macros are used; caller
     * is expected to DUK_REFZERO_CHECK().
     */
#endif  /* DUK_USE_PREFER_SIZE */
}

DUK_INTERNAL void duk_remove_n_unsafe(duk_hthread *thr, duk_idx_t idx, duk_idx_t count) {
    DUK_ASSERT_API_ENTRY(thr);

    duk_remove_n(thr, idx, count);  /* XXX: no optimization for now */
}

/*
 *  Stack slice primitives
 */

DUK_EXTERNAL void duk_xcopymove_raw(duk_hthread *to_thr, duk_hthread *from_thr, duk_idx_t count, duk_bool_t is_copy) {
    void *src;
    duk_size_t nbytes;
    duk_tval *p;
    duk_tval *q;

    /* XXX: several pointer comparison issues here */

    DUK_ASSERT_API_ENTRY(to_thr);
    DUK_CTX_ASSERT_VALID(to_thr);
    DUK_CTX_ASSERT_VALID(from_thr);
    DUK_ASSERT(to_thr->heap == from_thr->heap);

    if (DUK_UNLIKELY(to_thr == from_thr)) {
        DUK_ERROR_TYPE(to_thr, DUK_STR_INVALID_CONTEXT);
        DUK_WO_NORETURN(return;);
    }
    if (DUK_UNLIKELY((duk_uidx_t) count > (duk_uidx_t) DUK_USE_VALSTACK_LIMIT)) {
        /* Maximum value check ensures 'nbytes' won't wrap below.
         * Also handles negative count.
         */
        DUK_ERROR_RANGE_INVALID_COUNT(to_thr);
        DUK_WO_NORETURN(return;);
    }
    DUK_ASSERT(count >= 0);

    nbytes = sizeof(duk_tval) * (duk_size_t) count;
    if (DUK_UNLIKELY(nbytes == 0)) {
        return;
    }
    DUK_ASSERT(to_thr->valstack_top <= to_thr->valstack_end);
    if (DUK_UNLIKELY((duk_size_t) ((duk_uint8_t *) to_thr->valstack_end - (duk_uint8_t *) to_thr->valstack_top) < nbytes)) {
        DUK_ERROR_RANGE_PUSH_BEYOND(to_thr);
        DUK_WO_NORETURN(return;);
    }
    src = (void *) ((duk_uint8_t *) from_thr->valstack_top - nbytes);
    if (DUK_UNLIKELY(src < (void *) from_thr->valstack_bottom)) {
        DUK_ERROR_RANGE_INVALID_COUNT(to_thr);
        DUK_WO_NORETURN(return;);
    }

    /* Copy values (no overlap even if to_thr == from_thr; that's not
     * allowed now anyway).
     */
    DUK_ASSERT(nbytes > 0);
    duk_memcpy((void *) to_thr->valstack_top, (const void *) src, (size_t) nbytes);

    p = to_thr->valstack_top;
    to_thr->valstack_top = (duk_tval *) (void *) (((duk_uint8_t *) p) + nbytes);

    if (is_copy) {
        /* Incref copies, keep originals. */
        q = to_thr->valstack_top;
        while (p < q) {
            DUK_TVAL_INCREF(to_thr, p);  /* no side effects */
            p++;
        }
    } else {
        /* No net refcount change. */
        p = from_thr->valstack_top;
        q = (duk_tval *) (void *) (((duk_uint8_t *) p) - nbytes);
        from_thr->valstack_top = q;

        while (p > q) {
            p--;
            DUK_TVAL_SET_UNDEFINED(p);
            /* XXX: fast primitive to set a bunch of values to UNDEFINED */
        }
    }
}

/* Internal helper: reserve a gap of 'count' elements at 'idx_base' and return a
 * pointer to the gap.  Values in the gap are garbage and MUST be initialized by
 * the caller before any side effects may occur.  The caller must ensure there's
 * enough stack reserve for 'count' values.
 */
DUK_INTERNAL duk_tval *duk_reserve_gap(duk_hthread *thr, duk_idx_t idx_base, duk_idx_t count) {
    duk_tval *tv_src;
    duk_tval *tv_dst;
    duk_size_t gap_bytes;
    duk_size_t copy_bytes;

    /* Caller is responsible for ensuring there's enough preallocated
     * value stack.
     */
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(count >= 0);
    DUK_ASSERT((duk_size_t) (thr->valstack_end - thr->valstack_top) >= (duk_size_t) count);

    tv_src = thr->valstack_bottom + idx_base;
    gap_bytes = (duk_size_t) count * sizeof(duk_tval);
    tv_dst = (duk_tval *) (void *) ((duk_uint8_t *) tv_src + gap_bytes);
    copy_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - (duk_uint8_t *) tv_src);
    thr->valstack_top = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_top + gap_bytes);
    duk_memmove((void *) tv_dst, (const void *) tv_src, copy_bytes);

    /* Values in the gap are left as garbage: caller must fill them in
     * and INCREF them before any side effects.
     */
    return tv_src;
}

/*
 *  Get/opt/require
 */

DUK_EXTERNAL void duk_require_undefined(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_UNLIKELY(!DUK_TVAL_IS_UNDEFINED(tv))) {
        DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "undefined", DUK_STR_NOT_UNDEFINED);
        DUK_WO_NORETURN(return;);
    }
}

DUK_EXTERNAL void duk_require_null(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_UNLIKELY(!DUK_TVAL_IS_NULL(tv))) {
        DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "null", DUK_STR_NOT_NULL);
        DUK_WO_NORETURN(return;);
    }
}

DUK_LOCAL DUK_ALWAYS_INLINE duk_bool_t duk__get_boolean_raw(duk_hthread *thr, duk_idx_t idx, duk_bool_t def_value) {
    duk_bool_t ret;
    duk_tval *tv;

    DUK_CTX_ASSERT_VALID(thr);

    tv = duk_get_tval_or_unused(thr, idx);
    DUK_ASSERT(tv != NULL);
    if (DUK_TVAL_IS_BOOLEAN(tv)) {
        ret = DUK_TVAL_GET_BOOLEAN(tv);
        DUK_ASSERT(ret == 0 || ret == 1);
    } else {
        ret = def_value;
        /* 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_CTX_ASSERT_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) {
    duk_hstring *h;
    const char *ret;
    duk_size_t len;

    DUK_ASSERT_API_ENTRY(thr);

    h = duk_get_hstring(thr, idx);
    if (h != NULL) {
        len = DUK_HSTRING_GET_BYTELEN(h);
        ret = (const char *) DUK_HSTRING_GET_DATA(h);
    } else {
        len = 0;
        ret = NULL;
    }

    if (out_len != NULL) {
        *out_len = len;
    }
    return ret;
}

DUK_EXTERNAL const char *duk_require_lstring(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) {
    duk_hstring *h;

    DUK_ASSERT_API_ENTRY(thr);

    h = duk_require_hstring(thr, idx);
    DUK_ASSERT(h != NULL);
    if (out_len) {
        *out_len = DUK_HSTRING_GET_BYTELEN(h);
    }
    return (const char *) DUK_HSTRING_GET_DATA(h);
}

DUK_INTERNAL const char *duk_require_lstring_notsymbol(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) {
    duk_hstring *h;

    DUK_ASSERT_API_ENTRY(thr);

    h = duk_require_hstring_notsymbol(thr, idx);
    DUK_ASSERT(h != NULL);
    if (out_len) {
        *out_len = DUK_HSTRING_GET_BYTELEN(h);
    }
    return (const char *) DUK_HSTRING_GET_DATA(h);
}

DUK_EXTERNAL const char *duk_get_string(duk_hthread *thr, duk_idx_t idx) {
    duk_hstring *h;

    DUK_ASSERT_API_ENTRY(thr);

    h = duk_get_hstring(thr, idx);
    if (h != NULL) {
        return (const char *) DUK_HSTRING_GET_DATA(h);
    } else {
        return NULL;
    }
}

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

    if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
        if (out_len != NULL) {
            *out_len = def_len;
        }
        return def_ptr;
    }
    return duk_require_lstring(thr, idx, out_len);
}

DUK_EXTERNAL const char *duk_opt_string(duk_hthread *thr, duk_idx_t idx, const char *def_ptr) {
    DUK_ASSERT_API_ENTRY(thr);

    if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
        return def_ptr;
    }
    return duk_require_string(thr, idx);
}

DUK_EXTERNAL const char *duk_get_lstring_default(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len, const char *def_ptr, duk_size_t def_len) {
    duk_hstring *h;
    const char *ret;
    duk_size_t len;

    DUK_ASSERT_API_ENTRY(thr);

    h = duk_get_hstring(thr, idx);
    if (h != NULL) {
        len = DUK_HSTRING_GET_BYTELEN(h);
        ret = (const char *) DUK_HSTRING_GET_DATA(h);
    } else {
        len = def_len;
        ret = def_ptr;
    }

    if (out_len != NULL) {
        *out_len = len;
    }
    return ret;
}

DUK_EXTERNAL const char *duk_get_string_default(duk_hthread *thr, duk_idx_t idx, const char *def_value) {
    duk_hstring *h;

    DUK_ASSERT_API_ENTRY(thr);

    h = duk_get_hstring(thr, idx);
    if (h != NULL) {
        return (const char *) DUK_HSTRING_GET_DATA(h);
    } else {
        return def_value;
    }
}

DUK_INTERNAL const char *duk_get_string_notsymbol(duk_hthread *thr, duk_idx_t idx) {
    duk_hstring *h;

    DUK_ASSERT_API_ENTRY(thr);

    h = duk_get_hstring_notsymbol(thr, idx);
    if (h) {
        return (const char *) DUK_HSTRING_GET_DATA(h);
    } else {
        return NULL;
    }
}

DUK_EXTERNAL const char *duk_require_string(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);

    return duk_require_lstring(thr, idx, NULL);
}

DUK_INTERNAL const char *duk_require_string_notsymbol(duk_hthread *thr, duk_idx_t idx) {
    duk_hstring *h;

    DUK_ASSERT_API_ENTRY(thr);

    h = duk_require_hstring_notsymbol(thr, idx);
    DUK_ASSERT(h != NULL);
    return (const char *) DUK_HSTRING_GET_DATA(h);
}

DUK_EXTERNAL void duk_require_object(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_UNLIKELY(!DUK_TVAL_IS_OBJECT(tv))) {
        DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "object", DUK_STR_NOT_OBJECT);
        DUK_WO_NORETURN(return;);
    }
}

DUK_LOCAL void *duk__get_pointer_raw(duk_hthread *thr, duk_idx_t idx, void *def_value) {
    duk_tval *tv;
    void *p;

    DUK_CTX_ASSERT_VALID(thr);

    tv = duk_get_tval_or_unused(thr, idx);
    DUK_ASSERT(tv != NULL);
    if (!DUK_TVAL_IS_POINTER(tv)) {
        return def_value;
    }

    p = DUK_TVAL_GET_POINTER(tv);  /* may be NULL */
    return p;
}

DUK_EXTERNAL void *duk_get_pointer(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);
    return duk__get_pointer_raw(thr, idx, NULL /*def_value*/);
}

DUK_EXTERNAL void *duk_opt_pointer(duk_hthread *thr, duk_idx_t idx, void *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_pointer(thr, idx);
}

DUK_EXTERNAL void *duk_get_pointer_default(duk_hthread *thr, duk_idx_t idx, void *def_value) {
    DUK_ASSERT_API_ENTRY(thr);
    return duk__get_pointer_raw(thr, idx, def_value);
}

DUK_EXTERNAL void *duk_require_pointer(duk_hthread *thr, duk_idx_t idx) {
    duk_tval *tv;
    void *p;

    DUK_ASSERT_API_ENTRY(thr);

    /* Note: here we must be wary of the fact that a pointer may be
     * valid and be a NULL.
     */
    tv = duk_get_tval_or_unused(thr, idx);
    DUK_ASSERT(tv != NULL);
    if (DUK_UNLIKELY(!DUK_TVAL_IS_POINTER(tv))) {
        DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "pointer", DUK_STR_NOT_POINTER);
        DUK_WO_NORETURN(return NULL;);
    }
    p = DUK_TVAL_GET_POINTER(tv);  /* may be NULL */
    return p;
}

#if 0  /*unused*/
DUK_INTERNAL void *duk_get_voidptr(duk_hthread *thr, duk_idx_t idx) {
    duk_tval *tv;
    duk_heaphdr *h;

    DUK_ASSERT_API_ENTRY(thr);

    tv = duk_get_tval_or_unused(thr, idx);
    DUK_ASSERT(tv != NULL);
    if (!DUK_TVAL_IS_HEAP_ALLOCATED(tv)) {
        return NULL;
    }

    h = DUK_TVAL_GET_HEAPHDR(tv);
    DUK_ASSERT(h != NULL);
    return (void *) h;
}
#endif

DUK_LOCAL void *duk__get_buffer_helper(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size, duk_bool_t throw_flag) {
    duk_hbuffer *h;
    void *ret;
    duk_size_t len;
    duk_tval *tv;

    DUK_CTX_ASSERT_VALID(thr);

    if (out_size != NULL) {
        *out_size = 0;
    }

    tv = duk_get_tval_or_unused(thr, idx);
    DUK_ASSERT(tv != NULL);
    if (DUK_LIKELY(DUK_TVAL_IS_BUFFER(tv))) {
        h = DUK_TVAL_GET_BUFFER(tv);
        DUK_ASSERT(h != NULL);

        len = DUK_HBUFFER_GET_SIZE(h);
        ret = DUK_HBUFFER_GET_DATA_PTR(thr->heap, h);
    } else {
        if (throw_flag) {
            DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "buffer", DUK_STR_NOT_BUFFER);
            DUK_WO_NORETURN(return NULL;);
        }
        len = def_size;
        ret = def_ptr;
    }

    if (out_size != NULL) {
        *out_size = len;
    }
    return ret;
}

DUK_EXTERNAL void *duk_get_buffer(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size) {
    DUK_ASSERT_API_ENTRY(thr);

    return duk__get_buffer_helper(thr, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 0 /*throw_flag*/);
}

DUK_EXTERNAL void *duk_opt_buffer(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size) {
    DUK_ASSERT_API_ENTRY(thr);

    if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
        if (out_size != NULL) {
            *out_size = def_size;
        }
        return def_ptr;
    }
    return duk_require_buffer(thr, idx, out_size);
}

DUK_EXTERNAL void *duk_get_buffer_default(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_len) {
    DUK_ASSERT_API_ENTRY(thr);

    return duk__get_buffer_helper(thr, idx, out_size, def_ptr, def_len, 0 /*throw_flag*/);
}

DUK_EXTERNAL void *duk_require_buffer(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size) {
    DUK_ASSERT_API_ENTRY(thr);

    return duk__get_buffer_helper(thr, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 1 /*throw_flag*/);
}

/* Get the active buffer data area for a plain buffer or a buffer object.
 * Return NULL if the the value is not a buffer.  Note that a buffer may
 * have a NULL data pointer when its size is zero, the optional 'out_isbuffer'
 * argument allows caller to detect this reliably.
 */
DUK_INTERNAL void *duk_get_buffer_data_raw(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size, duk_bool_t throw_flag, duk_bool_t *out_isbuffer) {
    duk_tval *tv;

    DUK_ASSERT_API_ENTRY(thr);

    if (out_isbuffer != NULL) {
        *out_isbuffer = 0;
    }
    if (out_size != NULL) {
        *out_size = def_size;
    }

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

    if (DUK_TVAL_IS_BUFFER(tv)) {
        duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
        DUK_ASSERT(h != NULL);
        if (out_size != NULL) {
            *out_size = DUK_HBUFFER_GET_SIZE(h);
        }
        if (out_isbuffer != NULL) {
            *out_isbuffer = 1;
        }
        return (void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h);  /* may be NULL (but only if size is 0) */
    }
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
    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)) {
            /* XXX: this is probably a useful shared helper: for a
             * duk_hbufobj, get a validated buffer pointer/length.
             */
            duk_hbufobj *h_bufobj = (duk_hbufobj *) h;
            DUK_HBUFOBJ_ASSERT_VALID(h_bufobj);

            if (h_bufobj->buf != NULL &&
                DUK_HBUFOBJ_VALID_SLICE(h_bufobj)) {
                duk_uint8_t *p;

                p = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_bufobj->buf);
                if (out_size != NULL) {
                    *out_size = (duk_size_t) h_bufobj->length;
                }
                if (out_isbuffer != NULL) {
                    *out_isbuffer = 1;
                }
                return (void *) (p + h_bufobj->offset);
            }
            /* if slice not fully valid, treat as error */
        }
    }
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

    if (throw_flag) {
        DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "buffer", DUK_STR_NOT_BUFFER);
        DUK_WO_NORETURN(return NULL;);
    }
    return def_ptr;
}

DUK_EXTERNAL void *duk_get_buffer_data(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size) {
    DUK_ASSERT_API_ENTRY(thr);
    return duk_get_buffer_data_raw(thr, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 0 /*throw_flag*/, NULL);
}

DUK_EXTERNAL void *duk_get_buffer_data_default(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size) {
    DUK_ASSERT_API_ENTRY(thr);
    return duk_get_buffer_data_raw(thr, idx, out_size, def_ptr, def_size, 0 /*throw_flag*/, NULL);
}

DUK_EXTERNAL void *duk_opt_buffer_data(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size) {
    DUK_ASSERT_API_ENTRY(thr);

    if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
        if (out_size != NULL) {
            *out_size = def_size;
        }
        return def_ptr;
    }
    return duk_require_buffer_data(thr, idx, out_size);
}

DUK_EXTERNAL void *duk_require_buffer_data(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size) {
    DUK_ASSERT_API_ENTRY(thr);
    return duk_get_buffer_data_raw(thr, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 1 /*throw_flag*/, NULL);
}

/* Raw helper for getting a value from the stack, checking its tag.
 * The tag cannot be a number because numbers don't have an internal
 * tag in the packed representation.
 */

DUK_LOCAL duk_heaphdr *duk__get_tagged_heaphdr_raw(duk_hthread *thr, duk_idx_t idx, duk_uint_t tag) {
    duk_tval *tv;
    duk_heaphdr *ret;

    DUK_CTX_ASSERT_VALID(thr);

    tv = duk_get_tval_or_unused(thr, idx);
    DUK_ASSERT(tv != NULL);
    if (DUK_TVAL_GET_TAG(tv) != tag) {
        return (duk_heaphdr *) NULL;
    }

    ret = DUK_TVAL_GET_HEAPHDR(tv);
    DUK_ASSERT(ret != NULL);  /* tagged null pointers should never occur */
    return ret;

}

DUK_INTERNAL duk_hstring *duk_get_hstring(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);
    return (duk_hstring *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_STRING);
}

DUK_INTERNAL duk_hstring *duk_get_hstring_notsymbol(duk_hthread *thr, duk_idx_t idx) {
    duk_hstring *h;

    DUK_ASSERT_API_ENTRY(thr);

    h = (duk_hstring *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_STRING);
    if (DUK_UNLIKELY(h && DUK_HSTRING_HAS_SYMBOL(h))) {
        return NULL;
    }
    return h;
}

DUK_INTERNAL duk_hstring *duk_require_hstring(duk_hthread *thr, duk_idx_t idx) {
    duk_hstring *h;

    DUK_ASSERT_API_ENTRY(thr);

    h = (duk_hstring *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_STRING);
    if (DUK_UNLIKELY(h == NULL)) {
        DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "string", DUK_STR_NOT_STRING);
        DUK_WO_NORETURN(return NULL;);
    }
    return h;
}

DUK_INTERNAL duk_hstring *duk_require_hstring_notsymbol(duk_hthread *thr, duk_idx_t idx) {
    duk_hstring *h;

    DUK_ASSERT_API_ENTRY(thr);

    h = (duk_hstring *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_STRING);
    if (DUK_UNLIKELY(h == NULL || DUK_HSTRING_HAS_SYMBOL(h))) {
        DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "string", DUK_STR_NOT_STRING);
        DUK_WO_NORETURN(return NULL;);
    }
    return h;
}

DUK_INTERNAL duk_hobject *duk_get_hobject(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);
    return (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT);
}

DUK_INTERNAL duk_hobject *duk_require_hobject(duk_hthread *thr, duk_idx_t idx) {
    duk_hobject *h;

    DUK_ASSERT_API_ENTRY(thr);

    h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT);
    if (DUK_UNLIKELY(h == NULL)) {
        DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "object", DUK_STR_NOT_OBJECT);
        DUK_WO_NORETURN(return NULL;);
    }
    return h;
}

DUK_INTERNAL duk_hbuffer *duk_get_hbuffer(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);
    return (duk_hbuffer *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_BUFFER);
}

DUK_INTERNAL duk_hbuffer *duk_require_hbuffer(duk_hthread *thr, duk_idx_t idx) {
    duk_hbuffer *h;

    DUK_ASSERT_API_ENTRY(thr);

    h = (duk_hbuffer *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_BUFFER);
    if (DUK_UNLIKELY(h == NULL)) {
        DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "buffer", DUK_STR_NOT_BUFFER);
        DUK_WO_NORETURN(return NULL;);
    }
    return h;
}

DUK_INTERNAL duk_hthread *duk_get_hthread(duk_hthread *thr, duk_idx_t idx) {
    duk_hobject *h;

    DUK_ASSERT_API_ENTRY(thr);

    h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT);
    if (DUK_UNLIKELY(h != NULL && !DUK_HOBJECT_IS_THREAD(h))) {
        h = NULL;
    }
    return (duk_hthread *) h;
}

DUK_INTERNAL duk_hthread *duk_require_hthread(duk_hthread *thr, duk_idx_t idx) {
    duk_hobject *h;

    DUK_ASSERT_API_ENTRY(thr);

    h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT);
    if (DUK_UNLIKELY(!(h != NULL && DUK_HOBJECT_IS_THREAD(h)))) {
        DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "thread", DUK_STR_NOT_THREAD);
        DUK_WO_NORETURN(return NULL;);
    }
    return (duk_hthread *) h;
}

DUK_INTERNAL duk_hcompfunc *duk_get_hcompfunc(duk_hthread *thr, duk_idx_t idx) {
    duk_hobject *h;

    DUK_ASSERT_API_ENTRY(thr);

    h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT);
    if (DUK_UNLIKELY(h != NULL && !DUK_HOBJECT_IS_COMPFUNC(h))) {
        h = NULL;
    }
    return (duk_hcompfunc *) h;
}

DUK_INTERNAL duk_hcompfunc *duk_require_hcompfunc(duk_hthread *thr, duk_idx_t idx) {
    duk_hobject *h;

    DUK_ASSERT_API_ENTRY(thr);

    h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT);
    if (DUK_UNLIKELY(!(h != NULL && DUK_HOBJECT_IS_COMPFUNC(h)))) {
        DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "compiledfunction", DUK_STR_NOT_COMPFUNC);
        DUK_WO_NORETURN(return NULL;);
    }
    return (duk_hcompfunc *) h;
}

DUK_INTERNAL duk_hnatfunc *duk_get_hnatfunc(duk_hthread *thr, duk_idx_t idx) {
    duk_hobject *h;

    DUK_ASSERT_API_ENTRY(thr);

    h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT);
    if (DUK_UNLIKELY(h != NULL && !DUK_HOBJECT_IS_NATFUNC(h))) {
        h = NULL;
    }
    return (duk_hnatfunc *) h;
}

DUK_INTERNAL duk_hnatfunc *duk_require_hnatfunc(duk_hthread *thr, duk_idx_t idx) {
    duk_hobject *h;

    DUK_ASSERT_API_ENTRY(thr);

    h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT);
    if (DUK_UNLIKELY(!(h != NULL && DUK_HOBJECT_IS_NATFUNC(h)))) {
        DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "nativefunction", DUK_STR_NOT_NATFUNC);
        DUK_WO_NORETURN(return NULL;);
    }
    return (duk_hnatfunc *) h;
}

DUK_EXTERNAL duk_c_function duk_get_c_function(duk_hthread *thr, duk_idx_t idx) {
    duk_tval *tv;
    duk_hobject *h;
    duk_hnatfunc *f;

    DUK_ASSERT_API_ENTRY(thr);

    tv = duk_get_tval_or_unused(thr, idx);
    DUK_ASSERT(tv != NULL);
    if (DUK_UNLIKELY(!DUK_TVAL_IS_OBJECT(tv))) {
        return NULL;
    }
    h = DUK_TVAL_GET_OBJECT(tv);
    DUK_ASSERT(h != NULL);

    if (DUK_UNLIKELY(!DUK_HOBJECT_IS_NATFUNC(h))) {
        return NULL;
    }
    DUK_ASSERT(DUK_HOBJECT_HAS_NATFUNC(h));
    f = (duk_hnatfunc *) h;

    return f->func;
}

DUK_EXTERNAL duk_c_function duk_opt_c_function(duk_hthread *thr, duk_idx_t idx, duk_c_function 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_c_function(thr, idx);
}

DUK_EXTERNAL duk_c_function duk_get_c_function_default(duk_hthread *thr, duk_idx_t idx, duk_c_function def_value) {
    duk_c_function ret;

    DUK_ASSERT_API_ENTRY(thr);

    ret = duk_get_c_function(thr, idx);
    if (ret != NULL) {
        return ret;
    }

    return def_value;
}

DUK_EXTERNAL duk_c_function duk_require_c_function(duk_hthread *thr, duk_idx_t idx) {
    duk_c_function ret;

    DUK_ASSERT_API_ENTRY(thr);

    ret = duk_get_c_function(thr, idx);
    if (DUK_UNLIKELY(!ret)) {
        DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "nativefunction", DUK_STR_NOT_NATFUNC);
        DUK_WO_NORETURN(return ret;);
    }
    return ret;
}

DUK_EXTERNAL void duk_require_function(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);
    if (DUK_UNLIKELY(!duk_is_function(thr, idx))) {
        DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "function", DUK_STR_NOT_FUNCTION);
        DUK_WO_NORETURN(return;);
    }
}

DUK_EXTERNAL void duk_require_constructable(duk_hthread *thr, duk_idx_t idx) {
    duk_hobject *h;

    DUK_ASSERT_API_ENTRY(thr);

    h = duk_require_hobject_accept_mask(thr, idx, DUK_TYPE_MASK_LIGHTFUNC);
    if (DUK_UNLIKELY(h != NULL && !DUK_HOBJECT_HAS_CONSTRUCTABLE(h))) {
        DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "constructable", DUK_STR_NOT_CONSTRUCTABLE);
        DUK_WO_NORETURN(return;);
    }
    /* Lightfuncs (h == NULL) are constructable. */
}

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

    return duk_get_hthread(thr, idx);
}

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

    return duk_require_hthread(thr, idx);
}

DUK_EXTERNAL duk_hthread *duk_opt_context(duk_hthread *thr, duk_idx_t idx, duk_hthread *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_context(thr, idx);
}

DUK_EXTERNAL duk_hthread *duk_get_context_default(duk_hthread *thr, duk_idx_t idx, duk_hthread *def_value) {
    duk_hthread *ret;

    DUK_ASSERT_API_ENTRY(thr);

    ret = duk_get_context(thr, idx);
    if (ret != NULL) {
        return ret;
    }

    return def_value;
}

DUK_EXTERNAL void *duk_get_heapptr(duk_hthread *thr, duk_idx_t idx) {
    duk_tval *tv;
    void *ret;

    DUK_ASSERT_API_ENTRY(thr);

    tv = duk_get_tval_or_unused(thr, idx);
    DUK_ASSERT(tv != NULL);
    if (DUK_UNLIKELY(!DUK_TVAL_IS_HEAP_ALLOCATED(tv))) {
        return (void *) NULL;
    }

    ret = (void *) DUK_TVAL_GET_HEAPHDR(tv);
    DUK_ASSERT(ret != NULL);
    return ret;
}

DUK_EXTERNAL void *duk_opt_heapptr(duk_hthread *thr, duk_idx_t idx, void *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_heapptr(thr, idx);
}

DUK_EXTERNAL void *duk_get_heapptr_default(duk_hthread *thr, duk_idx_t idx, void *def_value) {
    void *ret;

    DUK_ASSERT_API_ENTRY(thr);

    ret = duk_get_heapptr(thr, idx);
    if (ret != NULL) {
        return ret;
    }

    return def_value;
}

DUK_EXTERNAL void *duk_require_heapptr(duk_hthread *thr, duk_idx_t idx) {
    duk_tval *tv;
    void *ret;

    DUK_ASSERT_API_ENTRY(thr);

    tv = duk_get_tval_or_unused(thr, idx);
    DUK_ASSERT(tv != NULL);
    if (DUK_UNLIKELY(!DUK_TVAL_IS_HEAP_ALLOCATED(tv))) {
        DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "heapobject", DUK_STR_UNEXPECTED_TYPE);
        DUK_WO_NORETURN(return NULL;);
    }

    ret = (void *) DUK_TVAL_GET_HEAPHDR(tv);
    DUK_ASSERT(ret != NULL);
    return ret;
}

/* Internal helper for getting/requiring a duk_hobject with possible promotion. */
DUK_LOCAL duk_hobject *duk__get_hobject_promote_mask_raw(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask) {
    duk_uint_t val_mask;
    duk_hobject *res;

    DUK_CTX_ASSERT_VALID(thr);

    res = duk_get_hobject(thr, idx);  /* common case, not promoted */
    if (DUK_LIKELY(res != NULL)) {
        DUK_ASSERT(res != NULL);
        return res;
    }

    val_mask = duk_get_type_mask(thr, idx);
    if (val_mask & type_mask) {
        if (type_mask & DUK_TYPE_MASK_PROMOTE) {
            res = duk_to_hobject(thr, idx);
            DUK_ASSERT(res != NULL);
            return res;
        } else {
            return NULL;  /* accept without promoting */
        }
    }

    if (type_mask & DUK_TYPE_MASK_THROW) {
        DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "object", DUK_STR_NOT_OBJECT);
        DUK_WO_NORETURN(return NULL;);
    }
    return NULL;
}

/* Get a duk_hobject * at 'idx'; if the value is not an object but matches the
 * supplied 'type_mask', promote it to an object and return the duk_hobject *.
 * This is useful for call sites which want an object but also accept a plain
 * buffer and/or a lightfunc which gets automatically promoted to an object.
 * Return value is NULL if value is neither an object nor a plain type allowed
 * by the mask.
 */
DUK_INTERNAL duk_hobject *duk_get_hobject_promote_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask) {
    DUK_ASSERT_API_ENTRY(thr);
    return duk__get_hobject_promote_mask_raw(thr, idx, type_mask | DUK_TYPE_MASK_PROMOTE);
}

/* Like duk_get_hobject_promote_mask() but throw a TypeError instead of
 * returning a NULL.
 */
DUK_INTERNAL duk_hobject *duk_require_hobject_promote_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask) {
    DUK_ASSERT_API_ENTRY(thr);
    return duk__get_hobject_promote_mask_raw(thr, idx, type_mask | DUK_TYPE_MASK_THROW | DUK_TYPE_MASK_PROMOTE);
}

/* Require a duk_hobject * at 'idx'; if the value is not an object but matches the
 * supplied 'type_mask', return a NULL instead.  Otherwise throw a TypeError.
 */
DUK_INTERNAL duk_hobject *duk_require_hobject_accept_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask) {
    DUK_ASSERT_API_ENTRY(thr);
    return duk__get_hobject_promote_mask_raw(thr, idx, type_mask | DUK_TYPE_MASK_THROW);
}

DUK_INTERNAL duk_hobject *duk_get_hobject_with_class(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t classnum) {
    duk_hobject *h;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT_DISABLE(classnum >= 0);  /* unsigned */
    DUK_ASSERT(classnum <= DUK_HOBJECT_CLASS_MAX);

    h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT);
    if (DUK_UNLIKELY(h != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(h) != classnum)) {
        h = NULL;
    }
    return h;
}

DUK_INTERNAL duk_hobject *duk_require_hobject_with_class(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t classnum) {
    duk_hobject *h;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT_DISABLE(classnum >= 0);  /* unsigned */
    DUK_ASSERT(classnum <= DUK_HOBJECT_CLASS_MAX);

    h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT);
    if (DUK_UNLIKELY(!(h != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(h) == classnum))) {
        duk_hstring *h_class;
        h_class = DUK_HTHREAD_GET_STRING(thr, DUK_HOBJECT_CLASS_NUMBER_TO_STRIDX(classnum));
        DUK_UNREF(h_class);

        DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, (const char *) DUK_HSTRING_GET_DATA(h_class), DUK_STR_UNEXPECTED_TYPE);
        DUK_WO_NORETURN(return NULL;);
    }
    return h;
}

DUK_EXTERNAL duk_size_t duk_get_length(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);

    switch (DUK_TVAL_GET_TAG(tv)) {
    case DUK_TAG_UNDEFINED:
    case DUK_TAG_NULL:
    case DUK_TAG_BOOLEAN:
    case DUK_TAG_POINTER:
        return 0;
#if defined(DUK_USE_PREFER_SIZE)
    /* String and buffer have a virtual non-configurable .length property
     * which is within size_t range so it can be looked up without specific
     * type checks.  Lightfuncs inherit from %NativeFunctionPrototype%
     * which provides an inherited .length accessor; it could be overwritten
     * to produce unexpected types or values, but just number convert and
     * duk_size_t cast for now.
     */
    case DUK_TAG_STRING:
    case DUK_TAG_BUFFER:
    case DUK_TAG_LIGHTFUNC: {
        duk_size_t ret;
        duk_get_prop_stridx(thr, idx, DUK_STRIDX_LENGTH);
        ret = (duk_size_t) duk_to_number_m1(thr);
        duk_pop_unsafe(thr);
        return ret;
    }
#else  /* DUK_USE_PREFER_SIZE */
    case DUK_TAG_STRING: {
        duk_hstring *h = DUK_TVAL_GET_STRING(tv);
        DUK_ASSERT(h != NULL);
        if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) {
            return 0;
        }
        return (duk_size_t) DUK_HSTRING_GET_CHARLEN(h);
    }
    case DUK_TAG_BUFFER: {
        duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
        DUK_ASSERT(h != NULL);
        return (duk_size_t) DUK_HBUFFER_GET_SIZE(h);
    }
    case DUK_TAG_LIGHTFUNC: {
        /* We could look up the length from the lightfunc duk_tval,
         * but since Duktape 2.2 lightfunc .length comes from
         * %NativeFunctionPrototype% which can be overridden, so
         * look up the property explicitly.
         */
        duk_size_t ret;
        duk_get_prop_stridx(thr, idx, DUK_STRIDX_LENGTH);
        ret = (duk_size_t) duk_to_number_m1(thr);
        duk_pop_unsafe(thr);
        return ret;
    }
#endif  /* DUK_USE_PREFER_SIZE */
    case DUK_TAG_OBJECT: {
        duk_hobject *h = DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(h != NULL);
        return (duk_size_t) duk_hobject_get_length(thr, h);
    }
#if defined(DUK_USE_FASTINT)
    case DUK_TAG_FASTINT:
#endif
    default:
        /* number or 'unused' */
        DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv) || DUK_TVAL_IS_UNUSED(tv));
        return 0;
    }

    DUK_UNREACHABLE();
}

/*
 *  duk_known_xxx() helpers
 *
 *  Used internally when we're 100% sure that a certain index is valid and
 *  contains an object of a certain type.  For example, if we duk_push_object()
 *  we can then safely duk_known_hobject(thr, -1).  These helpers just assert
 *  for the index and type, and if the assumptions are not valid, memory unsafe
 *  behavior happens.
 */

DUK_LOCAL duk_heaphdr *duk__known_heaphdr(duk_hthread *thr, duk_idx_t idx) {
    duk_tval *tv;
    duk_heaphdr *h;

    DUK_CTX_ASSERT_VALID(thr);
    if (idx < 0) {
        tv = thr->valstack_top + idx;
    } else {
        tv = thr->valstack_bottom + idx;
    }
    DUK_ASSERT(tv >= thr->valstack_bottom);
    DUK_ASSERT(tv < thr->valstack_top);
    h = DUK_TVAL_GET_HEAPHDR(tv);
    DUK_ASSERT(h != NULL);
    return h;
}

DUK_INTERNAL duk_hstring *duk_known_hstring(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(duk_get_hstring(thr, idx) != NULL);
    return (duk_hstring *) duk__known_heaphdr(thr, idx);
}

DUK_INTERNAL duk_hobject *duk_known_hobject(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(duk_get_hobject(thr, idx) != NULL);
    return (duk_hobject *) duk__known_heaphdr(thr, idx);
}

DUK_INTERNAL duk_hbuffer *duk_known_hbuffer(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(duk_get_hbuffer(thr, idx) != NULL);
    return (duk_hbuffer *) duk__known_heaphdr(thr, idx);
}

DUK_INTERNAL duk_hcompfunc *duk_known_hcompfunc(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(duk_get_hcompfunc(thr, idx) != NULL);
    return (duk_hcompfunc *) duk__known_heaphdr(thr, idx);
}

DUK_INTERNAL duk_hnatfunc *duk_known_hnatfunc(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(duk_get_hnatfunc(thr, idx) != NULL);
    return (duk_hnatfunc *) duk__known_heaphdr(thr, idx);
}

DUK_EXTERNAL void duk_set_length(duk_hthread *thr, duk_idx_t idx, duk_size_t len) {
    DUK_ASSERT_API_ENTRY(thr);

    idx = duk_normalize_index(thr, idx);
    duk_push_uint(thr, (duk_uint_t) len);
    duk_put_prop_stridx(thr, idx, DUK_STRIDX_LENGTH);
}

/*
 *  Conversions and coercions
 *
 *  The conversion/coercions are in-place operations on the value stack.
 *  Some operations are implemented here directly, while others call a
 *  helper in duk_js_ops.c after validating arguments.
 */

/* E5 Section 8.12.8 */

DUK_LOCAL duk_bool_t duk__defaultvalue_coerce_attempt(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t func_stridx) {
    if (duk_get_prop_stridx(thr, idx, func_stridx)) {
        /* [ ... func ] */
        if (duk_is_callable(thr, -1)) {
            duk_dup(thr, idx);         /* -> [ ... func this ] */
            duk_call_method(thr, 0);     /* -> [ ... retval ] */
            if (duk_is_primitive(thr, -1)) {
                duk_replace(thr, idx);
                return 1;
            }
            /* [ ... retval ]; popped below */
        }
    }
    duk_pop_unsafe(thr);  /* [ ... func/retval ] -> [ ... ] */
    return 0;
}

DUK_EXTERNAL void duk_to_undefined(duk_hthread *thr, duk_idx_t idx) {
    duk_tval *tv;

    DUK_ASSERT_API_ENTRY(thr);

    tv = duk_require_tval(thr, idx);
    DUK_ASSERT(tv != NULL);
    DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv);  /* side effects */
}

DUK_EXTERNAL void duk_to_null(duk_hthread *thr, duk_idx_t idx) {
    duk_tval *tv;

    DUK_ASSERT_API_ENTRY(thr);

    tv = duk_require_tval(thr, idx);
    DUK_ASSERT(tv != NULL);
    DUK_TVAL_SET_NULL_UPDREF(thr, tv);  /* side effects */
}

/* E5 Section 9.1 */
DUK_LOCAL const char * const duk__toprim_hint_strings[3] = {
    "default", "string", "number"
};
DUK_LOCAL void duk__to_primitive_helper(duk_hthread *thr, duk_idx_t idx, duk_int_t hint, duk_bool_t check_symbol) {
    /* Inline initializer for coercers[] is not allowed by old compilers like BCC. */
    duk_small_uint_t coercers[2];

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(hint == DUK_HINT_NONE || hint == DUK_HINT_NUMBER || hint == DUK_HINT_STRING);

    idx = duk_require_normalize_index(thr, idx);

    /* If already primitive, return as is. */
    if (!duk_check_type_mask(thr, idx, DUK_TYPE_MASK_OBJECT |
                                       DUK_TYPE_MASK_LIGHTFUNC |
                                       DUK_TYPE_MASK_BUFFER)) {
        DUK_ASSERT(!duk_is_buffer(thr, idx));  /* duk_to_string() relies on this behavior */
        return;
    }

    /* @@toPrimitive lookup.  Also do for plain buffers and lightfuncs
     * which mimic objects.
     */
    if (check_symbol && duk_get_method_stridx(thr, idx, DUK_STRIDX_WELLKNOWN_SYMBOL_TO_PRIMITIVE)) {
        DUK_ASSERT(hint >= 0 && (duk_size_t) hint < sizeof(duk__toprim_hint_strings) / sizeof(const char *));
        duk_dup(thr, idx);
        duk_push_string(thr, duk__toprim_hint_strings[hint]);
        duk_call_method(thr, 1);  /* [ ... method value hint ] -> [ ... res] */
        if (duk_check_type_mask(thr, -1, DUK_TYPE_MASK_OBJECT |
                                             DUK_TYPE_MASK_LIGHTFUNC |
                                         DUK_TYPE_MASK_BUFFER)) {
            goto fail;
        }
        duk_replace(thr, idx);
        return;
    }

    /* Objects are coerced based on E5 specification.
     * Lightfuncs are coerced because they behave like
     * objects even if they're internally a primitive
     * type.  Same applies to plain buffers, which behave
     * like ArrayBuffer objects since Duktape 2.x.
     */

    /* Hint magic for Date is unnecessary in ES2015 because of
     * Date.prototype[@@toPrimitive].  However, it is needed if
     * symbol support is not enabled.
     */
#if defined(DUK_USE_SYMBOL_BUILTIN)
    if (hint == DUK_HINT_NONE) {
        hint = DUK_HINT_NUMBER;
    }
#else  /* DUK_USE_SYMBOL_BUILTIN */
    if (hint == DUK_HINT_NONE) {
        duk_small_uint_t class_number;

        class_number = duk_get_class_number(thr, idx);
        if (class_number == DUK_HOBJECT_CLASS_DATE) {
            hint = DUK_HINT_STRING;
        } else {
            hint = DUK_HINT_NUMBER;
        }
    }
#endif  /* DUK_USE_SYMBOL_BUILTIN */

    coercers[0] = DUK_STRIDX_VALUE_OF;
    coercers[1] = DUK_STRIDX_TO_STRING;
    if (hint == DUK_HINT_STRING) {
        coercers[0] = DUK_STRIDX_TO_STRING;
        coercers[1] = DUK_STRIDX_VALUE_OF;
    }

    if (duk__defaultvalue_coerce_attempt(thr, idx, coercers[0])) {
        DUK_ASSERT(!duk_is_buffer(thr, idx));  /* duk_to_string() relies on this behavior */
        return;
    }

    if (duk__defaultvalue_coerce_attempt(thr, idx, coercers[1])) {
        DUK_ASSERT(!duk_is_buffer(thr, idx));  /* duk_to_string() relies on this behavior */
        return;
    }

 fail:
    DUK_ERROR_TYPE(thr, DUK_STR_TOPRIMITIVE_FAILED);
    DUK_WO_NORETURN(return;);
}

DUK_EXTERNAL void duk_to_primitive(duk_hthread *thr, duk_idx_t idx, duk_int_t hint) {
    duk__to_primitive_helper(thr, idx, hint, 1 /*check_symbol*/);
}

#if defined(DUK_USE_SYMBOL_BUILTIN)
DUK_INTERNAL void duk_to_primitive_ordinary(duk_hthread *thr, duk_idx_t idx, duk_int_t hint) {
    duk__to_primitive_helper(thr, idx, hint, 0 /*check_symbol*/);
}
#endif

/* E5 Section 9.2 */
DUK_EXTERNAL duk_bool_t duk_to_boolean(duk_hthread *thr, duk_idx_t idx) {
    duk_tval *tv;
    duk_bool_t val;

    DUK_ASSERT_API_ENTRY(thr);

    idx = duk_require_normalize_index(thr, idx);
    tv = DUK_GET_TVAL_POSIDX(thr, idx);
    DUK_ASSERT(tv != NULL);

    val = duk_js_toboolean(tv);
    DUK_ASSERT(val == 0 || val == 1);

    /* Note: no need to re-lookup tv, conversion is side effect free. */
    DUK_ASSERT(tv != NULL);
    DUK_TVAL_SET_BOOLEAN_UPDREF(thr, tv, val);  /* side effects */
    return val;
}

DUK_INTERNAL duk_bool_t duk_to_boolean_top_pop(duk_hthread *thr) {
    duk_tval *tv;
    duk_bool_t val;

    DUK_ASSERT_API_ENTRY(thr);

    tv = duk_require_tval(thr, -1);
    DUK_ASSERT(tv != NULL);

    val = duk_js_toboolean(tv);
    DUK_ASSERT(val == 0 || val == 1);

    duk_pop_unsafe(thr);
    return val;
}

DUK_EXTERNAL duk_double_t duk_to_number(duk_hthread *thr, duk_idx_t idx) {
    duk_tval *tv;
    duk_double_t d;

    DUK_ASSERT_API_ENTRY(thr);

    /* XXX: No need to normalize; the whole operation could be inlined here to
     * avoid 'tv' re-lookup.
     */
    idx = duk_require_normalize_index(thr, idx);
    tv = DUK_GET_TVAL_POSIDX(thr, idx);
    DUK_ASSERT(tv != NULL);
    d = duk_js_tonumber(thr, tv);  /* XXX: fastint coercion? now result will always be a non-fastint */

    /* ToNumber() may have side effects so must relookup 'tv'. */
    tv = DUK_GET_TVAL_POSIDX(thr, idx);
    DUK_TVAL_SET_NUMBER_UPDREF(thr, tv, d);  /* side effects */
    return d;
}

DUK_INTERNAL duk_double_t duk_to_number_m1(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    return duk_to_number(thr, -1);
}
DUK_INTERNAL duk_double_t duk_to_number_m2(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    return duk_to_number(thr, -2);
}

DUK_INTERNAL duk_double_t duk_to_number_tval(duk_hthread *thr, duk_tval *tv) {
#if defined(DUK_USE_PREFER_SIZE)
    duk_double_t res;

    DUK_ASSERT_API_ENTRY(thr);

    duk_push_tval(thr, tv);
    res = duk_to_number_m1(thr);
    duk_pop_unsafe(thr);
    return res;
#else
    duk_double_t res;
    duk_tval *tv_dst;

    DUK_ASSERT_API_ENTRY(thr);
    DUK__ASSERT_SPACE();

    tv_dst = thr->valstack_top++;
    DUK_TVAL_SET_TVAL(tv_dst, tv);
    DUK_TVAL_INCREF(thr, tv_dst);  /* decref not necessary */
    res = duk_to_number_m1(thr);  /* invalidates tv_dst */

    tv_dst = --thr->valstack_top;
    DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_dst));
    DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv_dst));  /* plain number */
    DUK_TVAL_SET_UNDEFINED(tv_dst);  /* valstack init policy */

    return res;
#endif
}

/* XXX: combine all the integer conversions: they share everything
 * but the helper function for coercion.
 */

typedef duk_double_t (*duk__toint_coercer)(duk_hthread *thr, duk_tval *tv);

DUK_LOCAL duk_double_t duk__to_int_uint_helper(duk_hthread *thr, duk_idx_t idx, duk__toint_coercer coerce_func) {
    duk_tval *tv;
    duk_double_t d;

    DUK_CTX_ASSERT_VALID(thr);

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

#if defined(DUK_USE_FASTINT)
    /* If argument is a fastint, guarantee that it remains one.
     * There's no downgrade check for other cases.
     */
    if (DUK_TVAL_IS_FASTINT(tv)) {
        /* XXX: Unnecessary conversion back and forth. */
        return (duk_double_t) DUK_TVAL_GET_FASTINT(tv);
    }
#endif
    d = coerce_func(thr, tv);

    /* XXX: fastint? */

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

DUK_EXTERNAL duk_int_t duk_to_int(duk_hthread *thr, duk_idx_t idx) {
    /* Value coercion (in stack): ToInteger(), E5 Section 9.4,
     * API return value coercion: custom.
     */
    DUK_ASSERT_API_ENTRY(thr);
    (void) duk__to_int_uint_helper(thr, idx, duk_js_tointeger);
    return (duk_int_t) duk__api_coerce_d2i(thr, idx, 0 /*def_value*/, 0 /*require*/);
}

DUK_EXTERNAL duk_uint_t duk_to_uint(duk_hthread *thr, duk_idx_t idx) {
    /* Value coercion (in stack): ToInteger(), E5 Section 9.4,
     * API return value coercion: custom.
     */
    DUK_ASSERT_API_ENTRY(thr);
    (void) duk__to_int_uint_helper(thr, idx, duk_js_tointeger);
    return (duk_uint_t) duk__api_coerce_d2ui(thr, idx, 0 /*def_value*/, 0 /*require*/);
}

DUK_EXTERNAL duk_int32_t duk_to_int32(duk_hthread *thr, duk_idx_t idx) {
    duk_tval *tv;
    duk_int32_t ret;

    DUK_ASSERT_API_ENTRY(thr);

    tv = duk_require_tval(thr, idx);
    DUK_ASSERT(tv != NULL);
    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 (duk_double_equals(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_CTX_ASSERT_VALID(thr);
    DUK_UNREF(udata);

    (void) 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. */
        ;
    }
    DUK_ASSERT(duk_is_string(thr, -1));

    duk_replace(thr, idx);
    DUK_ASSERT(duk_get_string(thr, idx) != NULL);
    return duk_get_lstring(thr, idx, out_len);
}

DUK_EXTERNAL const char *duk_to_stacktrace(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);
    idx = duk_require_normalize_index(thr, idx);

    /* The expected argument to the call is an Error object.  The stack
     * trace is extracted without an inheritance-based instanceof check
     * so that one can also extract the stack trace of a foreign error
     * created in another Realm.  Accept only a string .stack property.
     */
    if (duk_is_object(thr, idx)) {
        (void) duk_get_prop_string(thr, idx, "stack");
        if (duk_is_string(thr, -1)) {
            duk_replace(thr, idx);
        } else {
            duk_pop(thr);
        }
    }

    return duk_to_string(thr, idx);
}

DUK_LOCAL duk_ret_t duk__safe_to_stacktrace_raw(duk_hthread *thr, void *udata) {
    DUK_CTX_ASSERT_VALID(thr);
    DUK_UNREF(udata);

    (void) duk_to_stacktrace(thr, -1);

    return 1;
}

DUK_EXTERNAL const char *duk_safe_to_stacktrace(duk_hthread *thr, duk_idx_t idx) {
    duk_int_t rc;

    DUK_ASSERT_API_ENTRY(thr);
    idx = duk_require_normalize_index(thr, idx);

    duk_dup(thr, idx);
    rc = duk_safe_call(thr, duk__safe_to_stacktrace_raw, NULL /*udata*/, 1 /*nargs*/, 1 /*nrets*/);
    if (rc != 0) {
        /* Coercion failed.  Try to coerce the coercion itself error
         * to a stack trace once.  If that also fails, return a fixed,
         * preallocated 'Error' string to avoid potential infinite loop.
         */
        rc = duk_safe_call(thr, duk__safe_to_stacktrace_raw, NULL /*udata*/, 1 /*nargs*/, 1 /*nrets*/);
        if (rc != 0) {
            duk_pop_unsafe(thr);
            duk_push_hstring_stridx(thr, DUK_STRIDX_UC_ERROR);
        }
    }
    duk_replace(thr, idx);

    return duk_get_string(thr, idx);
}

DUK_INTERNAL duk_hstring *duk_to_property_key_hstring(duk_hthread *thr, duk_idx_t idx) {
    duk_hstring *h;

    DUK_ASSERT_API_ENTRY(thr);

    duk_to_primitive(thr, idx, DUK_HINT_STRING);  /* needed for e.g. Symbol objects */
    h = duk_get_hstring(thr, idx);
    if (h == NULL) {
        /* The "is string?" check may seem unnecessary, but as things
         * are duk_to_hstring() invokes ToString() which fails for
         * symbols.  But since symbols are already strings for Duktape
         * C API, we check for that before doing the coercion.
         */
        h = duk_to_hstring(thr, idx);
    }
    DUK_ASSERT(h != NULL);
    return h;
}

#if defined(DUK_USE_DEBUGGER_SUPPORT)  /* only needed by debugger for now */
DUK_INTERNAL duk_hstring *duk_safe_to_hstring(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);

    (void) duk_safe_to_string(thr, idx);
    DUK_ASSERT(duk_is_string(thr, idx));
    DUK_ASSERT(duk_get_hstring(thr, idx) != NULL);
    return duk_known_hstring(thr, idx);
}
#endif

/* Push Object.prototype.toString() output for 'tv'. */
DUK_INTERNAL void duk_push_class_string_tval(duk_hthread *thr, duk_tval *tv, duk_bool_t avoid_side_effects) {
    duk_hobject *h_obj;
    duk_small_uint_t classnum;
    duk_small_uint_t stridx;
    duk_tval tv_tmp;

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

    /* Stabilize 'tv', duk_push_literal() may trigger side effects. */
    DUK_TVAL_SET_TVAL(&tv_tmp, tv);
    tv = &tv_tmp;

    /* Conceptually for any non-undefined/null value we should do a
     * ToObject() coercion and look up @@toStringTag (from the object
     * prototype) to see if a custom result should be used, with the
     * exception of Arrays which are handled specially first.
     *
     * We'd like to avoid the actual conversion, but even for primitive
     * types the prototype may have @@toStringTag.  What's worse, the
     * @@toStringTag property may be a getter that must get the object
     * coerced value (not the prototype) as its 'this' binding.
     *
     * For now, do an actual object coercion.  This could be avoided by
     * doing a side effect free lookup to see if a getter would be invoked.
     * If not, the value can be read directly and the object coercion could
     * be avoided.  This may not be worth it in practice, because
     * Object.prototype.toString() is usually not performance critical.
     */

    duk_push_literal(thr, "[object ");  /* -> [ ... "[object" ] */

    switch (DUK_TVAL_GET_TAG(tv)) {
    case DUK_TAG_UNUSED:  /* Treat like 'undefined', shouldn't happen. */
    case DUK_TAG_UNDEFINED: {
        duk_push_hstring_stridx(thr, DUK_STRIDX_UC_UNDEFINED);
        goto finish;
    }
    case DUK_TAG_NULL: {
        duk_push_hstring_stridx(thr, DUK_STRIDX_UC_NULL);
        goto finish;
    }
    }

    duk_push_tval(thr, tv);
    tv = NULL;  /* Invalidated by ToObject(). */
    h_obj = duk_to_hobject(thr, -1);
    DUK_ASSERT(h_obj != NULL);
    if (duk_js_isarray_hobject(h_obj)) {
        stridx = DUK_STRIDX_UC_ARRAY;
    } else {
        /* [ ... "[object" obj ] */

#if defined(DUK_USE_SYMBOL_BUILTIN)
        /* XXX: better handling with avoid_side_effects == 1; lookup tval
         * without Proxy or getter side effects, and use it in sanitized
         * form if it's a string.
         */
        if (!avoid_side_effects) {
            (void) duk_get_prop_stridx(thr, -1, DUK_STRIDX_WELLKNOWN_SYMBOL_TO_STRING_TAG);
            if (duk_is_string_notsymbol(thr, -1)) {
                duk_remove_m2(thr);
                goto finish;
            }
            duk_pop_unsafe(thr);
        }
#else
        DUK_UNREF(avoid_side_effects);
#endif

        classnum = DUK_HOBJECT_GET_CLASS_NUMBER(h_obj);
        stridx = DUK_HOBJECT_CLASS_NUMBER_TO_STRIDX(classnum);
    }
    duk_pop_unsafe(thr);
    duk_push_hstring_stridx(thr, stridx);

 finish:
    /* [ ... "[object" tag ] */
    duk_push_literal(thr, "]");
    duk_concat(thr, 3);  /* [ ... "[object" tag "]" ] -> [ ... res ] */
}

/* XXX: other variants like uint, u32 etc */
DUK_INTERNAL duk_int_t duk_to_int_clamped_raw(duk_hthread *thr, duk_idx_t idx, duk_int_t minval, duk_int_t maxval, duk_bool_t *out_clamped) {
    duk_tval *tv;
    duk_tval tv_tmp;
    duk_double_t d, dmin, dmax;
    duk_int_t res;
    duk_bool_t clamped = 0;

    DUK_ASSERT_API_ENTRY(thr);

    tv = duk_require_tval(thr, idx);
    DUK_ASSERT(tv != NULL);
    d = duk_js_tointeger(thr, tv);  /* E5 Section 9.4, ToInteger() */

    dmin = (duk_double_t) minval;
    dmax = (duk_double_t) maxval;

    if (d < dmin) {
        clamped = 1;
        res = minval;
        d = dmin;
    } else if (d > dmax) {
        clamped = 1;
        res = maxval;
        d = dmax;
    } else {
        res = (duk_int_t) d;
    }
    DUK_UNREF(d);  /* SCANBUILD: with suitable dmin/dmax limits 'd' is unused */
    /* 'd' and 'res' agree here */

    /* Relookup in case duk_js_tointeger() ends up e.g. coercing an object. */
    tv = duk_get_tval(thr, idx);
    DUK_ASSERT(tv != NULL);  /* not popped by side effect */
    DUK_TVAL_SET_TVAL(&tv_tmp, tv);
#if defined(DUK_USE_FASTINT)
#if (DUK_INT_MAX <= 0x7fffffffL)
    DUK_TVAL_SET_I32(tv, res);
#else
    /* Clamping needed if duk_int_t is 64 bits. */
    if (res >= DUK_FASTINT_MIN && res <= DUK_FASTINT_MAX) {
        DUK_TVAL_SET_FASTINT(tv, res);
    } else {
        DUK_TVAL_SET_NUMBER(tv, d);
    }
#endif
#else
    DUK_TVAL_SET_NUMBER(tv, d);  /* no need to incref */
#endif
    DUK_TVAL_DECREF(thr, &tv_tmp);  /* side effects */

    if (out_clamped) {
        *out_clamped = clamped;
    } else {
        /* coerced value is updated to value stack even when RangeError thrown */
        if (clamped) {
            DUK_ERROR_RANGE(thr, DUK_STR_NUMBER_OUTSIDE_RANGE);
            DUK_WO_NORETURN(return 0;);
        }
    }

    return res;
}

DUK_INTERNAL duk_int_t duk_to_int_clamped(duk_hthread *thr, duk_idx_t idx, duk_idx_t minval, duk_idx_t maxval) {
    duk_bool_t dummy;

    DUK_ASSERT_API_ENTRY(thr);

    return duk_to_int_clamped_raw(thr, idx, minval, maxval, &dummy);
}

DUK_INTERNAL duk_int_t duk_to_int_check_range(duk_hthread *thr, duk_idx_t idx, duk_int_t minval, duk_int_t maxval) {
    DUK_ASSERT_API_ENTRY(thr);
    return duk_to_int_clamped_raw(thr, idx, minval, maxval, NULL);  /* out_clamped==NULL -> RangeError if outside range */
}

DUK_EXTERNAL const char *duk_to_string(duk_hthread *thr, duk_idx_t idx) {
    duk_tval *tv;

    DUK_ASSERT_API_ENTRY(thr);

    idx = duk_require_normalize_index(thr, idx);
    tv = DUK_GET_TVAL_POSIDX(thr, idx);
    DUK_ASSERT(tv != NULL);

    switch (DUK_TVAL_GET_TAG(tv)) {
    case DUK_TAG_UNDEFINED: {
        duk_push_hstring_stridx(thr, DUK_STRIDX_LC_UNDEFINED);
        break;
    }
    case DUK_TAG_NULL: {
        duk_push_hstring_stridx(thr, DUK_STRIDX_LC_NULL);
        break;
    }
    case DUK_TAG_BOOLEAN: {
        if (DUK_TVAL_GET_BOOLEAN(tv)) {
            duk_push_hstring_stridx(thr, DUK_STRIDX_TRUE);
        } else {
            duk_push_hstring_stridx(thr, DUK_STRIDX_FALSE);
        }
        break;
    }
    case DUK_TAG_STRING: {
        /* Nop for actual strings, TypeError for Symbols.
         * Because various internals rely on ToString() coercion of
         * internal strings, -allow- (NOP) string coercion for hidden
         * symbols.
         */
#if 1
        duk_hstring *h;
        h = DUK_TVAL_GET_STRING(tv);
        DUK_ASSERT(h != NULL);
        if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) {
            DUK_ERROR_TYPE(thr, DUK_STR_CANNOT_STRING_COERCE_SYMBOL);
            DUK_WO_NORETURN(goto skip_replace;);
        } else {
            goto skip_replace;
        }
#else
        goto skip_replace;
#endif
        break;
    }
    case DUK_TAG_BUFFER: /* Go through Uint8Array.prototype.toString() for coercion. */
    case DUK_TAG_OBJECT: {
        /* Plain buffers: go through ArrayBuffer.prototype.toString()
         * for coercion.
         *
         * Symbol objects: duk_to_primitive() results in a plain symbol
         * value, and duk_to_string() then causes a TypeError.
         */
        duk_to_primitive(thr, idx, DUK_HINT_STRING);
        DUK_ASSERT(!duk_is_buffer(thr, idx));  /* ToPrimitive() must guarantee */
        DUK_ASSERT(!duk_is_object(thr, idx));
        return duk_to_string(thr, idx);  /* Note: recursive call */
    }
    case DUK_TAG_POINTER: {
        void *ptr = DUK_TVAL_GET_POINTER(tv);
        if (ptr != NULL) {
            duk_push_sprintf(thr, DUK_STR_FMT_PTR, (void *) ptr);
        } else {
            /* Represent a null pointer as 'null' to be consistent with
             * the JX format variant.  Native '%p' format for a NULL
             * pointer may be e.g. '(nil)'.
             */
            duk_push_hstring_stridx(thr, DUK_STRIDX_LC_NULL);
        }
        break;
    }
    case DUK_TAG_LIGHTFUNC: {
        /* Should match Function.prototype.toString() */
        duk_push_lightfunc_tostring(thr, tv);
        break;
    }
#if defined(DUK_USE_FASTINT)
    case DUK_TAG_FASTINT:
#endif
    default: {
        /* number */
        DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
        DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
        duk_push_tval(thr, tv);
        duk_numconv_stringify(thr,
                              10 /*radix*/,
                              0 /*precision:shortest*/,
                              0 /*force_exponential*/);
        break;
    }
    }

    duk_replace(thr, idx);

 skip_replace:
    DUK_ASSERT(duk_is_string(thr, idx));
    return duk_require_string(thr, idx);
}

DUK_INTERNAL duk_hstring *duk_to_hstring(duk_hthread *thr, duk_idx_t idx) {
    duk_hstring *ret;

    DUK_ASSERT_API_ENTRY(thr);

    duk_to_string(thr, idx);
    ret = duk_get_hstring(thr, idx);
    DUK_ASSERT(ret != NULL);
    return ret;
}

DUK_INTERNAL duk_hstring *duk_to_hstring_m1(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    return duk_to_hstring(thr, -1);
}

DUK_INTERNAL duk_hstring *duk_to_hstring_acceptsymbol(duk_hthread *thr, duk_idx_t idx) {
    duk_hstring *ret;

    DUK_ASSERT_API_ENTRY(thr);

    ret = duk_get_hstring(thr, idx);
    if (DUK_UNLIKELY(ret && DUK_HSTRING_HAS_SYMBOL(ret))) {
        return ret;
    }
    return duk_to_hstring(thr, idx);
}

/* Convert a plain buffer or any buffer object into a string, using the buffer
 * bytes 1:1 in the internal string representation.  For views the active byte
 * slice (not element slice interpreted as an initializer) is used.  This is
 * necessary in Duktape 2.x because ToString(plainBuffer) no longer creates a
 * string with the same bytes as in the buffer but rather (usually)
 * '[object ArrayBuffer]'.
 */
DUK_EXTERNAL const char *duk_buffer_to_string(duk_hthread *thr, duk_idx_t idx) {
    void *ptr_src;
    duk_size_t len;
    const char *res;

    DUK_ASSERT_API_ENTRY(thr);

    idx = duk_require_normalize_index(thr, idx);

    ptr_src = duk_require_buffer_data(thr, idx, &len);
    DUK_ASSERT(ptr_src != NULL || len == 0);

    res = duk_push_lstring(thr, (const char *) ptr_src, len);
    duk_replace(thr, idx);
    return res;
}

DUK_EXTERNAL void *duk_to_buffer_raw(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, duk_uint_t mode) {
    duk_hbuffer *h_buf;
    const duk_uint8_t *src_data;
    duk_size_t src_size;
    duk_uint8_t *dst_data;

    DUK_ASSERT_API_ENTRY(thr);

    idx = duk_require_normalize_index(thr, idx);

    h_buf = duk_get_hbuffer(thr, idx);
    if (h_buf != NULL) {
        /* Buffer is kept as is, with the fixed/dynamic nature of the
         * buffer only changed if requested.  An external buffer
         * is converted into a non-external dynamic buffer in a
         * duk_to_dynamic_buffer() call.
         */
        duk_uint_t tmp;
        duk_uint8_t *tmp_ptr;

        tmp_ptr = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_buf);
        src_data = (const duk_uint8_t *) tmp_ptr;
        src_size = DUK_HBUFFER_GET_SIZE(h_buf);

        tmp = (DUK_HBUFFER_HAS_DYNAMIC(h_buf) ? DUK_BUF_MODE_DYNAMIC : DUK_BUF_MODE_FIXED);
        if ((tmp == mode && !DUK_HBUFFER_HAS_EXTERNAL(h_buf)) ||
            mode == DUK_BUF_MODE_DONTCARE) {
            /* Note: src_data may be NULL if input is a zero-size
             * dynamic buffer.
             */
            dst_data = tmp_ptr;
            goto skip_copy;
        }
    } else {
        /* Non-buffer value is first ToString() coerced, then converted
         * to a buffer (fixed buffer is used unless a dynamic buffer is
         * explicitly requested).  Symbols are rejected with a TypeError.
         * XXX: C API could maybe allow symbol-to-buffer coercion?
         */
        src_data = (const duk_uint8_t *) duk_to_lstring(thr, idx, &src_size);
    }

    dst_data = (duk_uint8_t *) duk_push_buffer(thr, src_size, (mode == DUK_BUF_MODE_DYNAMIC) /*dynamic*/);
    /* dst_data may be NULL if size is zero. */
    duk_memcpy_unsafe((void *) dst_data, (const void *) src_data, (size_t) src_size);

    duk_replace(thr, idx);
 skip_copy:

    if (out_size) {
        *out_size = src_size;
    }
    return dst_data;
}

DUK_EXTERNAL void *duk_to_pointer(duk_hthread *thr, duk_idx_t idx) {
    duk_tval *tv;
    void *res;

    DUK_ASSERT_API_ENTRY(thr);

    idx = duk_require_normalize_index(thr, idx);
    tv = DUK_GET_TVAL_POSIDX(thr, idx);
    DUK_ASSERT(tv != NULL);

    switch (DUK_TVAL_GET_TAG(tv)) {
    case DUK_TAG_UNDEFINED:
    case DUK_TAG_NULL:
    case DUK_TAG_BOOLEAN:
        res = NULL;
        break;
    case DUK_TAG_POINTER:
        res = DUK_TVAL_GET_POINTER(tv);
        break;
    case DUK_TAG_STRING:
    case DUK_TAG_OBJECT:
    case DUK_TAG_BUFFER:
        /* Heap allocated: return heap pointer which is NOT useful
         * for the caller, except for debugging.
         */
        res = (void *) DUK_TVAL_GET_HEAPHDR(tv);
        break;
    case DUK_TAG_LIGHTFUNC:
        /* Function pointers do not always cast correctly to void *
         * (depends on memory and segmentation model for instance),
         * so they coerce to NULL.
         */
        res = NULL;
        break;
#if defined(DUK_USE_FASTINT)
    case DUK_TAG_FASTINT:
#endif
    default:
        /* number */
        DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
        DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
        res = NULL;
        break;
    }

    duk_push_pointer(thr, res);
    duk_replace(thr, idx);
    return res;
}

DUK_LOCAL void duk__push_func_from_lightfunc(duk_hthread *thr, duk_c_function func, duk_small_uint_t lf_flags) {
    duk_idx_t nargs;
    duk_uint_t flags = 0;   /* shared flags for a subset of types */
    duk_small_uint_t lf_len;
    duk_hnatfunc *nf;

    nargs = (duk_idx_t) DUK_LFUNC_FLAGS_GET_NARGS(lf_flags);
    if (nargs == DUK_LFUNC_NARGS_VARARGS) {
        nargs = (duk_idx_t) DUK_VARARGS;
    }

    flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
            DUK_HOBJECT_FLAG_CONSTRUCTABLE |
            DUK_HOBJECT_FLAG_CALLABLE |
            DUK_HOBJECT_FLAG_FASTREFS |
            DUK_HOBJECT_FLAG_NATFUNC |
            DUK_HOBJECT_FLAG_NEWENV |
            DUK_HOBJECT_FLAG_STRICT |
            DUK_HOBJECT_FLAG_NOTAIL |
            DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION);
    (void) duk__push_c_function_raw(thr, func, nargs, flags, DUK_BIDX_NATIVE_FUNCTION_PROTOTYPE);

    lf_len = DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags);
    if ((duk_idx_t) lf_len != nargs) {
        /* Explicit length is only needed if it differs from 'nargs'. */
        duk_push_int(thr, (duk_int_t) lf_len);
        duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE);
    }

#if defined(DUK_USE_FUNC_NAME_PROPERTY)
    duk_push_lightfunc_name_raw(thr, func, lf_flags);
    duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C);
#endif

    nf = duk_known_hnatfunc(thr, -1);
    nf->magic = (duk_int16_t) DUK_LFUNC_FLAGS_GET_MAGIC(lf_flags);
}

DUK_EXTERNAL void duk_to_object(duk_hthread *thr, duk_idx_t idx) {
    duk_tval *tv;
    duk_uint_t flags = 0;   /* shared flags for a subset of types */
    duk_small_int_t proto = 0;

    DUK_ASSERT_API_ENTRY(thr);

    idx = duk_require_normalize_index(thr, idx);
    tv = DUK_GET_TVAL_POSIDX(thr, idx);
    DUK_ASSERT(tv != NULL);

    switch (DUK_TVAL_GET_TAG(tv)) {
#if !defined(DUK_USE_BUFFEROBJECT_SUPPORT)
    case DUK_TAG_BUFFER:  /* With no bufferobject support, don't object coerce. */
#endif
    case DUK_TAG_UNDEFINED:
    case DUK_TAG_NULL: {
        DUK_ERROR_TYPE(thr, DUK_STR_NOT_OBJECT_COERCIBLE);
        DUK_WO_NORETURN(return;);
        break;
    }
    case DUK_TAG_BOOLEAN: {
        flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
                DUK_HOBJECT_FLAG_FASTREFS |
                DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BOOLEAN);
        proto = DUK_BIDX_BOOLEAN_PROTOTYPE;
        goto create_object;
    }
    case DUK_TAG_STRING: {
        duk_hstring *h;
        h = DUK_TVAL_GET_STRING(tv);
        DUK_ASSERT(h != NULL);
        if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) {
            flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
                    DUK_HOBJECT_FLAG_FASTREFS |
                    DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_SYMBOL);
            proto = DUK_BIDX_SYMBOL_PROTOTYPE;
        } else {
            flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
                    DUK_HOBJECT_FLAG_FASTREFS |
                    DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ |
                    DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_STRING);
            proto = DUK_BIDX_STRING_PROTOTYPE;
        }
        goto create_object;
    }
    case DUK_TAG_OBJECT: {
        /* nop */
        break;
    }
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
    case DUK_TAG_BUFFER: {
        /* A plain buffer object coerces to a full ArrayBuffer which
         * is not fully transparent behavior (ToObject() should be a
         * nop for an object).  This behavior matches lightfuncs which
         * also coerce to an equivalent Function object.  There are
         * also downsides to defining ToObject(plainBuffer) as a no-op;
         * for example duk_to_hobject() could result in a NULL pointer.
         */
        duk_hbuffer *h_buf;

        h_buf = DUK_TVAL_GET_BUFFER(tv);
        DUK_ASSERT(h_buf != NULL);
        duk_hbufobj_push_uint8array_from_plain(thr, h_buf);
        goto replace_value;
    }
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
    case DUK_TAG_POINTER: {
        flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
                DUK_HOBJECT_FLAG_FASTREFS |
                DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_POINTER);
        proto = DUK_BIDX_POINTER_PROTOTYPE;
        goto create_object;
    }
    case DUK_TAG_LIGHTFUNC: {
        /* Lightfunc coerces to a Function instance with concrete
         * properties.  Since 'length' is virtual for Duktape/C
         * functions, don't need to define that.  The result is made
         * extensible to mimic what happens to strings in object
         * coercion:
         *
         *   > Object.isExtensible(Object('foo'))
         *   true
         */
        duk_small_uint_t lf_flags;
        duk_c_function func;

        DUK_TVAL_GET_LIGHTFUNC(tv, func, lf_flags);
        duk__push_func_from_lightfunc(thr, func, lf_flags);
        goto replace_value;
    }
#if defined(DUK_USE_FASTINT)
    case DUK_TAG_FASTINT:
#endif
    default: {
        DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
        DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
        flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
                DUK_HOBJECT_FLAG_FASTREFS |
                DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_NUMBER);
        proto = DUK_BIDX_NUMBER_PROTOTYPE;
        goto create_object;
    }
    }
    DUK_ASSERT(duk_is_object(thr, idx));
    return;

 create_object:
    (void) duk_push_object_helper(thr, flags, proto);

    /* Note: Boolean prototype's internal value property is not writable,
     * but duk_xdef_prop_stridx() disregards the write protection.  Boolean
     * instances are immutable.
     *
     * String and buffer special behaviors are already enabled which is not
     * ideal, but a write to the internal value is not affected by them.
     */
    duk_dup(thr, idx);
    duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE);

 replace_value:
    duk_replace(thr, idx);
    DUK_ASSERT(duk_is_object(thr, idx));
}

DUK_INTERNAL duk_hobject *duk_to_hobject(duk_hthread *thr, duk_idx_t idx) {
    duk_hobject *ret;

    DUK_ASSERT_API_ENTRY(thr);

    duk_to_object(thr, idx);
    ret = duk_known_hobject(thr, idx);
    return ret;
}

/*
 *  Type checking
 */

DUK_LOCAL duk_bool_t duk__tag_check(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t tag) {
    duk_tval *tv;

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

DUK_LOCAL duk_bool_t duk__obj_flag_any_default_false(duk_hthread *thr, duk_idx_t idx, duk_uint_t flag_mask) {
    duk_hobject *obj;

    DUK_ASSERT_API_ENTRY(thr);

    obj = duk_get_hobject(thr, idx);
    if (obj) {
        return (DUK_HEAPHDR_CHECK_FLAG_BITS((duk_heaphdr *) obj, flag_mask) ? 1 : 0);
    }
    return 0;
}

DUK_INTERNAL duk_int_t duk_get_type_tval(duk_tval *tv) {
    DUK_ASSERT(tv != NULL);

#if defined(DUK_USE_PACKED_TVAL)
    switch (DUK_TVAL_GET_TAG(tv)) {
    case DUK_TAG_UNUSED:
        return DUK_TYPE_NONE;
    case DUK_TAG_UNDEFINED:
        return DUK_TYPE_UNDEFINED;
    case DUK_TAG_NULL:
        return DUK_TYPE_NULL;
    case DUK_TAG_BOOLEAN:
        return DUK_TYPE_BOOLEAN;
    case DUK_TAG_STRING:
        return DUK_TYPE_STRING;
    case DUK_TAG_OBJECT:
        return DUK_TYPE_OBJECT;
    case DUK_TAG_BUFFER:
        return DUK_TYPE_BUFFER;
    case DUK_TAG_POINTER:
        return DUK_TYPE_POINTER;
    case DUK_TAG_LIGHTFUNC:
        return DUK_TYPE_LIGHTFUNC;
#if defined(DUK_USE_FASTINT)
    case DUK_TAG_FASTINT:
#endif
    default:
        /* Note: number has no explicit tag (in 8-byte representation) */
        DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
        DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
        return DUK_TYPE_NUMBER;
    }
#else  /* DUK_USE_PACKED_TVAL */
    DUK_ASSERT(DUK_TVAL_IS_VALID_TAG(tv));
    DUK_ASSERT(sizeof(duk__type_from_tag) / sizeof(duk_uint_t) == DUK_TAG_MAX - DUK_TAG_MIN + 1);
    return (duk_int_t) duk__type_from_tag[DUK_TVAL_GET_TAG(tv) - DUK_TAG_MIN];
#endif  /* DUK_USE_PACKED_TVAL */
}

DUK_EXTERNAL duk_int_t duk_get_type(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_tval(tv);
}

#if defined(DUK_USE_VERBOSE_ERRORS) && defined(DUK_USE_PARANOID_ERRORS)
DUK_LOCAL const char * const duk__type_names[] = {
    "none",
    "undefined",
    "null",
    "boolean",
    "number",
    "string",
    "object",
    "buffer",
    "pointer",
    "lightfunc"
};

DUK_INTERNAL const char *duk_get_type_name(duk_hthread *thr, duk_idx_t idx) {
    duk_int_t type_tag;

    DUK_ASSERT_API_ENTRY(thr);

    type_tag = duk_get_type(thr, idx);
    DUK_ASSERT(type_tag >= DUK_TYPE_MIN && type_tag <= DUK_TYPE_MAX);
    DUK_ASSERT(DUK_TYPE_MIN == 0 && sizeof(duk__type_names) / sizeof(const char *) == DUK_TYPE_MAX + 1);

    return duk__type_names[type_tag];
}
#endif  /* DUK_USE_VERBOSE_ERRORS && DUK_USE_PARANOID_ERRORS */

DUK_INTERNAL duk_small_uint_t duk_get_class_number(duk_hthread *thr, duk_idx_t idx) {
    duk_tval *tv;
    duk_hobject *obj;

    DUK_ASSERT_API_ENTRY(thr);

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

    switch (DUK_TVAL_GET_TAG(tv)) {
    case DUK_TAG_OBJECT:
        obj = DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(obj != NULL);
        return DUK_HOBJECT_GET_CLASS_NUMBER(obj);
    case DUK_TAG_BUFFER:
        /* Buffers behave like Uint8Array objects. */
        return DUK_HOBJECT_CLASS_UINT8ARRAY;
    case DUK_TAG_LIGHTFUNC:
        /* Lightfuncs behave like Function objects. */
        return DUK_HOBJECT_CLASS_FUNCTION;
    default:
        /* Primitive or UNUSED, no class number. */
        return DUK_HOBJECT_CLASS_NONE;
    }
}

DUK_EXTERNAL duk_bool_t duk_check_type(duk_hthread *thr, duk_idx_t idx, duk_int_t type) {
    DUK_ASSERT_API_ENTRY(thr);

    return (duk_get_type(thr, idx) == type) ? 1 : 0;
}

DUK_INTERNAL duk_uint_t duk_get_type_mask_tval(duk_tval *tv) {
    DUK_ASSERT(tv != NULL);

#if defined(DUK_USE_PACKED_TVAL)
    switch (DUK_TVAL_GET_TAG(tv)) {
    case DUK_TAG_UNUSED:
        return DUK_TYPE_MASK_NONE;
    case DUK_TAG_UNDEFINED:
        return DUK_TYPE_MASK_UNDEFINED;
    case DUK_TAG_NULL:
        return DUK_TYPE_MASK_NULL;
    case DUK_TAG_BOOLEAN:
        return DUK_TYPE_MASK_BOOLEAN;
    case DUK_TAG_STRING:
        return DUK_TYPE_MASK_STRING;
    case DUK_TAG_OBJECT:
        return DUK_TYPE_MASK_OBJECT;
    case DUK_TAG_BUFFER:
        return DUK_TYPE_MASK_BUFFER;
    case DUK_TAG_POINTER:
        return DUK_TYPE_MASK_POINTER;
    case DUK_TAG_LIGHTFUNC:
        return DUK_TYPE_MASK_LIGHTFUNC;
#if defined(DUK_USE_FASTINT)
    case DUK_TAG_FASTINT:
#endif
    default:
        /* Note: number has no explicit tag (in 8-byte representation) */
        DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
        DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
        return DUK_TYPE_MASK_NUMBER;
    }
#else  /* DUK_USE_PACKED_TVAL */
    DUK_ASSERT(DUK_TVAL_IS_VALID_TAG(tv));
    DUK_ASSERT(sizeof(duk__type_mask_from_tag) / sizeof(duk_uint_t) == DUK_TAG_MAX - DUK_TAG_MIN + 1);
    return duk__type_mask_from_tag[DUK_TVAL_GET_TAG(tv) - DUK_TAG_MIN];
#endif  /* DUK_USE_PACKED_TVAL */
}

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);

    return duk_is_buffer(thr, idx);
}

#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

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

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

DUK_EXTERNAL duk_bool_t duk_is_symbol(duk_hthread *thr, duk_idx_t idx) {
    duk_hstring *h;

    DUK_ASSERT_API_ENTRY(thr);
    h = duk_get_hstring(thr, idx);
    /* Use DUK_LIKELY() here because caller may be more likely to type
     * check an expected symbol than not.
     */
    if (DUK_LIKELY(h != NULL && DUK_HSTRING_HAS_SYMBOL(h))) {
        return 1;
    }
    return 0;
}

/* IsArray(), returns true for Array instance or Proxy of Array instance. */
DUK_EXTERNAL duk_bool_t duk_is_array(duk_hthread *thr, duk_idx_t idx) {
    duk_tval *tv;

    DUK_ASSERT_API_ENTRY(thr);

    tv = duk_get_tval(thr, idx);
    if (tv) {
        return duk_js_isarray(tv);
    }
    return 0;
}

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

    DUK_ASSERT_API_ENTRY(thr);

    tv = duk_get_tval_or_unused(thr, idx);
    if (DUK_TVAL_IS_OBJECT(tv)) {
        duk_hobject *h;
        h = DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(h != NULL);
        return DUK_HOBJECT_HAS_CALLABLE(h) ? 1 : 0;
    }
    if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
        return 1;
    }
    return 0;
}

DUK_INTERNAL duk_bool_t duk_is_callable_tval(duk_hthread *thr, duk_tval *tv) {
    DUK_ASSERT_API_ENTRY(thr);

    DUK_UNREF(thr);

    if (DUK_TVAL_IS_OBJECT(tv)) {
        duk_hobject *h;
        h = DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(h != NULL);
        return DUK_HOBJECT_HAS_CALLABLE(h) ? 1 : 0;
    }
    if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
        return 1;
    }
    return 0;
}

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

    DUK_ASSERT_API_ENTRY(thr);

    tv = duk_get_tval_or_unused(thr, idx);
    if (DUK_TVAL_IS_OBJECT(tv)) {
        duk_hobject *h;
        h = DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(h != NULL);
        return DUK_HOBJECT_HAS_CONSTRUCTABLE(h) ? 1 : 0;
    }
    if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
        return 1;
    }
    return 0;
}

DUK_EXTERNAL duk_bool_t duk_is_c_function(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);
    return duk__obj_flag_any_default_false(thr,
                                           idx,
                                           DUK_HOBJECT_FLAG_NATFUNC);
}

DUK_EXTERNAL duk_bool_t duk_is_ecmascript_function(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);
    return duk__obj_flag_any_default_false(thr,
                                           idx,
                                           DUK_HOBJECT_FLAG_COMPFUNC);
}

DUK_EXTERNAL duk_bool_t duk_is_bound_function(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);
    return duk__obj_flag_any_default_false(thr,
                                           idx,
                                           DUK_HOBJECT_FLAG_BOUNDFUNC);
}

DUK_EXTERNAL duk_bool_t duk_is_thread(duk_hthread *thr, duk_idx_t idx) {
    duk_hobject *obj;

    DUK_ASSERT_API_ENTRY(thr);

    obj = duk_get_hobject(thr, idx);
    if (obj) {
        return (DUK_HOBJECT_GET_CLASS_NUMBER(obj) == DUK_HOBJECT_CLASS_THREAD ? 1 : 0);
    }
    return 0;
}

DUK_EXTERNAL duk_bool_t duk_is_fixed_buffer(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)) {
        duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
        DUK_ASSERT(h != NULL);
        return (DUK_HBUFFER_HAS_DYNAMIC(h) ? 0 : 1);
    }
    return 0;
}

DUK_EXTERNAL duk_bool_t duk_is_dynamic_buffer(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)) {
        duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
        DUK_ASSERT(h != NULL);
        return (DUK_HBUFFER_HAS_DYNAMIC(h) && !DUK_HBUFFER_HAS_EXTERNAL(h) ? 1 : 0);
    }
    return 0;
}

DUK_EXTERNAL duk_bool_t duk_is_external_buffer(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)) {
        duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
        DUK_ASSERT(h != NULL);
        return (DUK_HBUFFER_HAS_DYNAMIC(h) && DUK_HBUFFER_HAS_EXTERNAL(h) ? 1 : 0);
    }
    return 0;
}

DUK_EXTERNAL duk_errcode_t duk_get_error_code(duk_hthread *thr, duk_idx_t idx) {
    duk_hobject *h;
    duk_uint_t sanity;

    DUK_ASSERT_API_ENTRY(thr);

    h = duk_get_hobject(thr, idx);

    sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
    do {
        if (!h) {
            return DUK_ERR_NONE;
        }

        /* XXX: something more convenient? */

        if (h == thr->builtins[DUK_BIDX_EVAL_ERROR_PROTOTYPE]) {
            return DUK_ERR_EVAL_ERROR;
        }
        if (h == thr->builtins[DUK_BIDX_RANGE_ERROR_PROTOTYPE]) {
            return DUK_ERR_RANGE_ERROR;
        }
        if (h == thr->builtins[DUK_BIDX_REFERENCE_ERROR_PROTOTYPE]) {
            return DUK_ERR_REFERENCE_ERROR;
        }
        if (h == thr->builtins[DUK_BIDX_SYNTAX_ERROR_PROTOTYPE]) {
            return DUK_ERR_SYNTAX_ERROR;
        }
        if (h == thr->builtins[DUK_BIDX_TYPE_ERROR_PROTOTYPE]) {
            return DUK_ERR_TYPE_ERROR;
        }
        if (h == thr->builtins[DUK_BIDX_URI_ERROR_PROTOTYPE]) {
            return DUK_ERR_URI_ERROR;
        }
        if (h == thr->builtins[DUK_BIDX_ERROR_PROTOTYPE]) {
            return DUK_ERR_ERROR;
        }

        h = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h);
    } while (--sanity > 0);

    return DUK_ERR_NONE;
}

/*
 *  Pushers
 */

DUK_INTERNAL void duk_push_tval(duk_hthread *thr, duk_tval *tv) {
    duk_tval *tv_slot;

    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 */
    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_nan(duk_hthread *thr) {
    duk_tval *tv_slot;
    duk_double_union du;

    DUK_ASSERT_API_ENTRY(thr);
    DUK__CHECK_SPACE();
    DUK_DBLUNION_SET_NAN(&du);
    DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du));
    tv_slot = thr->valstack_top++;
    DUK_TVAL_SET_NUMBER(tv_slot, du.d);
}

DUK_EXTERNAL const char *duk_push_lstring(duk_hthread *thr, const char *str, duk_size_t len) {
    duk_hstring *h;
    duk_tval *tv_slot;

    DUK_ASSERT_API_ENTRY(thr);

    /* Check stack before interning (avoid hanging temp). */
    DUK__CHECK_SPACE();

    /* NULL with zero length represents an empty string; NULL with higher
     * length is also now treated like an empty string although it is
     * a bit dubious.  This is unlike duk_push_string() which pushes a
     * 'null' if the input string is a NULL.
     */
    if (DUK_UNLIKELY(str == NULL)) {
        len = 0U;
    }

    /* Check for maximum string length. */
    if (DUK_UNLIKELY(len > DUK_HSTRING_MAX_BYTELEN)) {
        DUK_ERROR_RANGE(thr, DUK_STR_STRING_TOO_LONG);
        DUK_WO_NORETURN(return NULL;);
    }

    h = duk_heap_strtable_intern_checked(thr, (const duk_uint8_t *) str, (duk_uint32_t) len);
    DUK_ASSERT(h != NULL);

    tv_slot = thr->valstack_top++;
    DUK_TVAL_SET_STRING(tv_slot, h);
    DUK_HSTRING_INCREF(thr, h);  /* no side effects */

    return (const char *) DUK_HSTRING_GET_DATA(h);
}

DUK_EXTERNAL const char *duk_push_string(duk_hthread *thr, const char *str) {
    DUK_ASSERT_API_ENTRY(thr);

    if (str) {
        return duk_push_lstring(thr, str, DUK_STRLEN(str));
    } else {
        duk_push_null(thr);
        return NULL;
    }
}

#if !defined(DUK_USE_PREFER_SIZE)
#if defined(DUK_USE_LITCACHE_SIZE)
DUK_EXTERNAL const char *duk_push_literal_raw(duk_hthread *thr, const char *str, duk_size_t len) {
    duk_hstring *h;
    duk_tval *tv_slot;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(str != NULL);
    DUK_ASSERT(str[len] == (char) 0);

    /* Check for maximum string length. */
    if (DUK_UNLIKELY(len > DUK_HSTRING_MAX_BYTELEN)) {
        DUK_ERROR_RANGE(thr, DUK_STR_STRING_TOO_LONG);
        DUK_WO_NORETURN(return NULL;);
    }

    h = duk_heap_strtable_intern_literal_checked(thr, (const duk_uint8_t *) str, (duk_uint32_t) len);
    DUK_ASSERT(h != NULL);

    tv_slot = thr->valstack_top++;
    DUK_TVAL_SET_STRING(tv_slot, h);
    DUK_HSTRING_INCREF(thr, h);  /* no side effects */

    return (const char *) DUK_HSTRING_GET_DATA(h);
}
#else  /* DUK_USE_LITCACHE_SIZE */
DUK_EXTERNAL const char *duk_push_literal_raw(duk_hthread *thr, const char *str, duk_size_t len) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(str != NULL);
    DUK_ASSERT(str[len] == (char) 0);

    return duk_push_lstring(thr, str, len);
}
#endif  /* DUK_USE_LITCACHE_SIZE */
#endif  /* !DUK_USE_PREFER_SIZE */

DUK_EXTERNAL void duk_push_pointer(duk_hthread *thr, void *val) {
    duk_tval *tv_slot;

    DUK_ASSERT_API_ENTRY(thr);
    DUK__CHECK_SPACE();
    tv_slot = thr->valstack_top++;
    DUK_TVAL_SET_POINTER(tv_slot, val);
}

DUK_INTERNAL duk_hstring *duk_push_uint_to_hstring(duk_hthread *thr, duk_uint_t i) {
    duk_hstring *h_tmp;

    DUK_ASSERT_API_ENTRY(thr);

    /* XXX: this could be a direct DUK_SPRINTF to a buffer followed by duk_push_string() */
    duk_push_uint(thr, (duk_uint_t) i);
    h_tmp = duk_to_hstring_m1(thr);
    DUK_ASSERT(h_tmp != NULL);
    return h_tmp;
}

DUK_LOCAL void duk__push_this_helper(duk_hthread *thr, duk_small_uint_t check_object_coercible) {
    duk_tval *tv_slot;

    DUK__CHECK_SPACE();

    DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top));  /* because of valstack init policy */
    tv_slot = thr->valstack_top++;

    if (DUK_UNLIKELY(thr->callstack_curr == NULL)) {
        if (check_object_coercible) {
            goto type_error;
        }
        /* 'undefined' already on stack top */
    } else {
        duk_tval *tv;

        /* 'this' binding is just before current activation's bottom */
        DUK_ASSERT(thr->valstack_bottom > thr->valstack);
        tv = thr->valstack_bottom - 1;
        if (check_object_coercible &&
            (DUK_TVAL_IS_UNDEFINED(tv) || DUK_TVAL_IS_NULL(tv))) {
            /* XXX: better macro for DUK_TVAL_IS_UNDEFINED_OR_NULL(tv) */
            goto type_error;
        }

        DUK_TVAL_SET_TVAL(tv_slot, tv);
        DUK_TVAL_INCREF(thr, tv);
    }
    return;

 type_error:
    DUK_ERROR_TYPE(thr, DUK_STR_NOT_OBJECT_COERCIBLE);
    DUK_WO_NORETURN(return;);
}

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

    duk__push_this_helper(thr, 0 /*check_object_coercible*/);
}

DUK_INTERNAL void duk_push_this_check_object_coercible(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);

    duk__push_this_helper(thr, 1 /*check_object_coercible*/);
}

DUK_INTERNAL duk_hobject *duk_push_this_coercible_to_object(duk_hthread *thr) {
    duk_hobject *h;

    DUK_ASSERT_API_ENTRY(thr);

    duk__push_this_helper(thr, 1 /*check_object_coercible*/);
    h = duk_to_hobject(thr, -1);
    DUK_ASSERT(h != NULL);
    return h;
}

DUK_INTERNAL duk_hstring *duk_push_this_coercible_to_string(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);

    duk__push_this_helper(thr, 1 /*check_object_coercible*/);
    return duk_to_hstring_m1(thr);  /* This will reject all Symbol values; accepts Symbol objects. */
}

DUK_INTERNAL duk_tval *duk_get_borrowed_this_tval(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);

    DUK_ASSERT(thr->callstack_top > 0);  /* caller required to know */
    DUK_ASSERT(thr->callstack_curr != NULL);  /* caller required to know */
    DUK_ASSERT(thr->valstack_bottom > thr->valstack);  /* consequence of above */
    DUK_ASSERT(thr->valstack_bottom - 1 >= thr->valstack);  /* 'this' binding exists */

    return thr->valstack_bottom - 1;
}

DUK_EXTERNAL void duk_push_new_target(duk_hthread *thr) {
    duk_activation *act;

    DUK_ASSERT_API_ENTRY(thr);

    /* https://www.ecma-international.org/ecma-262/6.0/#sec-meta-properties-runtime-semantics-evaluation
     * https://www.ecma-international.org/ecma-262/6.0/#sec-getnewtarget
     *
     * No newTarget support now, so as a first approximation
     * use the resolved (non-bound) target function.
     *
     * Check CONSTRUCT flag from current function, or if running
     * direct eval, from a non-direct-eval parent (with possibly
     * more than one nested direct eval).  An alternative to this
     * would be to store [[NewTarget]] as a hidden symbol of the
     * lexical scope, and then just look up that variable.
     *
     * Calls from the application will either be for an empty
     * call stack, or a Duktape/C function as the top activation.
     */

    act = thr->callstack_curr;
    for (;;) {
        if (act == NULL) {
            break;
        }

        if (act->flags & DUK_ACT_FLAG_CONSTRUCT) {
            duk_push_tval(thr, &act->tv_func);
            return;
        } else if (act->flags & DUK_ACT_FLAG_DIRECT_EVAL) {
            act = act->parent;
        } else {
            break;
        }
    }

    duk_push_undefined(thr);
}

DUK_EXTERNAL void duk_push_current_function(duk_hthread *thr) {
    duk_activation *act;

    DUK_ASSERT_API_ENTRY(thr);

    act = thr->callstack_curr;
    if (act != NULL) {
        duk_push_tval(thr, &act->tv_func);
    } else {
        duk_push_undefined(thr);
    }
}

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

    if (thr->heap->curr_thread) {
        duk_push_hobject(thr, (duk_hobject *) thr->heap->curr_thread);
    } else {
        duk_push_undefined(thr);
    }
}

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

    duk_push_hobject_bidx(thr, DUK_BIDX_GLOBAL);
}

/* XXX: size optimize */
DUK_LOCAL void duk__push_stash(duk_hthread *thr) {
    if (!duk_xget_owndataprop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE)) {
        DUK_DDD(DUK_DDDPRINT("creating heap/global/thread stash on first use"));
        duk_pop_unsafe(thr);
        duk_push_bare_object(thr);
        duk_dup_top(thr);
        duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_C);  /* [ ... parent stash stash ] -> [ ... parent stash ] */
    }
    duk_remove_m2(thr);
}

DUK_EXTERNAL void duk_push_heap_stash(duk_hthread *thr) {
    duk_heap *heap;
    DUK_ASSERT_API_ENTRY(thr);
    heap = thr->heap;
    DUK_ASSERT(heap->heap_object != NULL);
    duk_push_hobject(thr, heap->heap_object);
    duk__push_stash(thr);
}

DUK_EXTERNAL void duk_push_global_stash(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    duk_push_global_object(thr);
    duk__push_stash(thr);
}

DUK_EXTERNAL void duk_push_thread_stash(duk_hthread *thr, duk_hthread *target_thr) {
    DUK_ASSERT_API_ENTRY(thr);
    if (DUK_UNLIKELY(target_thr == NULL)) {
        DUK_ERROR_TYPE_INVALID_ARGS(thr);
        DUK_WO_NORETURN(return;);
    }
    duk_push_hobject(thr, (duk_hobject *) target_thr);
    duk__push_stash(thr);
}

/* XXX: duk_ssize_t would be useful here */
DUK_LOCAL duk_int_t duk__try_push_vsprintf(duk_hthread *thr, void *buf, duk_size_t sz, const char *fmt, va_list ap) {
    duk_int_t len;

    DUK_CTX_ASSERT_VALID(thr);
    DUK_UNREF(thr);

    /* NUL terminator handling doesn't matter here */
    len = DUK_VSNPRINTF((char *) buf, sz, fmt, ap);
    if (len < (duk_int_t) sz) {
        /* Return value of 'sz' or more indicates output was (potentially)
         * truncated.
         */
        return (duk_int_t) len;
    }
    return -1;
}

DUK_EXTERNAL const char *duk_push_vsprintf(duk_hthread *thr, const char *fmt, va_list ap) {
    duk_uint8_t stack_buf[DUK_PUSH_SPRINTF_INITIAL_SIZE];
    duk_size_t sz = DUK_PUSH_SPRINTF_INITIAL_SIZE;
    duk_bool_t pushed_buf = 0;
    void *buf;
    duk_int_t len;  /* XXX: duk_ssize_t */
    const char *res;

    DUK_ASSERT_API_ENTRY(thr);

    /* special handling of fmt==NULL */
    if (!fmt) {
        duk_hstring *h_str;
        duk_push_hstring_empty(thr);
        h_str = duk_known_hstring(thr, -1);
        return (const char *) DUK_HSTRING_GET_DATA(h_str);
    }

    /* initial estimate based on format string */
    sz = DUK_STRLEN(fmt) + 16;  /* format plus something to avoid just missing */
    if (sz < DUK_PUSH_SPRINTF_INITIAL_SIZE) {
        sz = DUK_PUSH_SPRINTF_INITIAL_SIZE;
    }
    DUK_ASSERT(sz > 0);

    /* Try to make do with a stack buffer to avoid allocating a temporary buffer.
     * This works 99% of the time which is quite nice.
     */
    for (;;) {
        va_list ap_copy;  /* copied so that 'ap' can be reused */

        if (sz <= sizeof(stack_buf)) {
            buf = stack_buf;
        } else if (!pushed_buf) {
            pushed_buf = 1;
            buf = duk_push_dynamic_buffer(thr, sz);
        } else {
            buf = duk_resize_buffer(thr, -1, sz);
        }
        DUK_ASSERT(buf != NULL);

        DUK_VA_COPY(ap_copy, ap);
        len = duk__try_push_vsprintf(thr, buf, sz, fmt, ap_copy);
        va_end(ap_copy);
        if (len >= 0) {
            break;
        }

        /* failed, resize and try again */
        sz = sz * 2;
        if (DUK_UNLIKELY(sz >= DUK_PUSH_SPRINTF_SANITY_LIMIT)) {
            DUK_ERROR_RANGE(thr, DUK_STR_RESULT_TOO_LONG);
            DUK_WO_NORETURN(return NULL;);
        }
    }

    /* Cannot use duk_buffer_to_string() on the buffer because it is
     * usually larger than 'len'; 'buf' is also usually a stack buffer.
     */
    res = duk_push_lstring(thr, (const char *) buf, (duk_size_t) len);  /* [ buf? res ] */
    if (pushed_buf) {
        duk_remove_m2(thr);
    }
    return res;
}

DUK_EXTERNAL const char *duk_push_sprintf(duk_hthread *thr, const char *fmt, ...) {
    va_list ap;
    const char *ret;

    DUK_ASSERT_API_ENTRY(thr);

    /* allow fmt==NULL */
    va_start(ap, fmt);
    ret = duk_push_vsprintf(thr, fmt, ap);
    va_end(ap);

    return ret;
}

DUK_INTERNAL duk_hobject *duk_push_object_helper(duk_hthread *thr, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx) {
    duk_tval *tv_slot;
    duk_hobject *h;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(prototype_bidx == -1 ||
               (prototype_bidx >= 0 && prototype_bidx < DUK_NUM_BUILTINS));

    DUK__CHECK_SPACE();

    h = duk_hobject_alloc(thr, hobject_flags_and_class);
    DUK_ASSERT(h != NULL);

    DUK_DDD(DUK_DDDPRINT("created object with flags: 0x%08lx", (unsigned long) h->hdr.h_flags));

    tv_slot = thr->valstack_top;
    DUK_TVAL_SET_OBJECT(tv_slot, h);
    DUK_HOBJECT_INCREF(thr, h);  /* no side effects */
    thr->valstack_top++;

    /* object is now reachable */

    if (prototype_bidx >= 0) {
        DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, h, thr->builtins[prototype_bidx]);
    } else {
        DUK_ASSERT(prototype_bidx == -1);
        DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h) == NULL);
    }

    return h;
}

DUK_INTERNAL duk_hobject *duk_push_object_helper_proto(duk_hthread *thr, duk_uint_t hobject_flags_and_class, duk_hobject *proto) {
    duk_hobject *h;

    DUK_ASSERT_API_ENTRY(thr);

    h = duk_push_object_helper(thr, hobject_flags_and_class, -1);
    DUK_ASSERT(h != NULL);
    DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, h, proto);
    return h;
}

DUK_EXTERNAL duk_idx_t duk_push_object(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);

    (void) duk_push_object_helper(thr,
                                  DUK_HOBJECT_FLAG_EXTENSIBLE |
                                  DUK_HOBJECT_FLAG_FASTREFS |
                                  DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
                                  DUK_BIDX_OBJECT_PROTOTYPE);
    return duk_get_top_index_unsafe(thr);
}

DUK_EXTERNAL duk_idx_t duk_push_array(duk_hthread *thr) {
    duk_uint_t flags;
    duk_harray *obj;
    duk_idx_t ret;
    duk_tval *tv_slot;

    DUK_ASSERT_API_ENTRY(thr);

    flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
            DUK_HOBJECT_FLAG_FASTREFS |
            DUK_HOBJECT_FLAG_ARRAY_PART |
            DUK_HOBJECT_FLAG_EXOTIC_ARRAY |
            DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAY);

    obj = duk_harray_alloc(thr, flags);
    DUK_ASSERT(obj != NULL);

    DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) obj, thr->builtins[DUK_BIDX_ARRAY_PROTOTYPE]);

    tv_slot = thr->valstack_top;
    DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj);
    DUK_HOBJECT_INCREF(thr, obj);  /* XXX: could preallocate with refcount = 1 */
    ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
    thr->valstack_top++;

    DUK_ASSERT(obj->length == 0);  /* Array .length starts at zero. */
    return ret;
}

DUK_EXTERNAL duk_idx_t duk_push_bare_array(duk_hthread *thr) {
    duk_uint_t flags;
    duk_harray *obj;
    duk_idx_t ret;
    duk_tval *tv_slot;

    DUK_ASSERT_API_ENTRY(thr);

    flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
            DUK_HOBJECT_FLAG_FASTREFS |
            DUK_HOBJECT_FLAG_ARRAY_PART |
            DUK_HOBJECT_FLAG_EXOTIC_ARRAY |
            DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAY);

    obj = duk_harray_alloc(thr, flags);
    DUK_ASSERT(obj != NULL);

    tv_slot = thr->valstack_top;
    DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj);
    DUK_HOBJECT_INCREF(thr, obj);  /* XXX: could preallocate with refcount = 1 */
    ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
    thr->valstack_top++;

    DUK_ASSERT(obj->length == 0);  /* Array .length starts at zero. */
    return ret;
}

DUK_INTERNAL duk_harray *duk_push_harray(duk_hthread *thr) {
    /* XXX: API call could do this directly, cast to void in API macro. */
    duk_harray *a;

    DUK_ASSERT_API_ENTRY(thr);

    (void) duk_push_array(thr);
    DUK_ASSERT(DUK_TVAL_IS_OBJECT(thr->valstack_top - 1));
    a = (duk_harray *) DUK_TVAL_GET_OBJECT(thr->valstack_top - 1);
    DUK_ASSERT(a != NULL);
    return a;
}

/* Push a duk_harray with preallocated size (.length also set to match size).
 * Caller may then populate array part of the duk_harray directly.
 */
DUK_INTERNAL duk_harray *duk_push_harray_with_size(duk_hthread *thr, duk_uint32_t size) {
    duk_harray *a;

    DUK_ASSERT_API_ENTRY(thr);

    a = duk_push_harray(thr);

    duk_hobject_realloc_props(thr,
                              (duk_hobject *) a,
                              0,
                              size,
                              0,
                              0);
    a->length = size;
    return a;
}

DUK_INTERNAL duk_tval *duk_push_harray_with_size_outptr(duk_hthread *thr, duk_uint32_t size) {
    duk_harray *a;

    DUK_ASSERT_API_ENTRY(thr);

    a = duk_push_harray_with_size(thr, size);
    DUK_ASSERT(a != NULL);
    return DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) a);
}

DUK_EXTERNAL duk_idx_t duk_push_thread_raw(duk_hthread *thr, duk_uint_t flags) {
    duk_hthread *obj;
    duk_idx_t ret;
    duk_tval *tv_slot;

    DUK_ASSERT_API_ENTRY(thr);

    DUK__CHECK_SPACE();

    obj = duk_hthread_alloc(thr,
                            DUK_HOBJECT_FLAG_EXTENSIBLE |
                            DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_THREAD));
    DUK_ASSERT(obj != NULL);
    obj->state = DUK_HTHREAD_STATE_INACTIVE;
#if defined(DUK_USE_ROM_STRINGS)
    /* Nothing to initialize, strs[] is in ROM. */
#else
#if defined(DUK_USE_HEAPPTR16)
    obj->strs16 = thr->strs16;
#else
    obj->strs = thr->strs;
#endif
#endif
    DUK_DDD(DUK_DDDPRINT("created thread object with flags: 0x%08lx", (unsigned long) obj->obj.hdr.h_flags));

    /* make the new thread reachable */
    tv_slot = thr->valstack_top;
    DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj);
    DUK_HTHREAD_INCREF(thr, obj);
    ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
    thr->valstack_top++;

    /* important to do this *after* pushing, to make the thread reachable for gc */
    if (DUK_UNLIKELY(!duk_hthread_init_stacks(thr->heap, obj))) {
        DUK_ERROR_ALLOC_FAILED(thr);
        DUK_WO_NORETURN(return 0;);
    }

    /* initialize built-ins - either by copying or creating new ones */
    if (flags & DUK_THREAD_NEW_GLOBAL_ENV) {
        duk_hthread_create_builtin_objects(obj);
    } else {
        duk_hthread_copy_builtin_objects(thr, obj);
    }

    /* default prototype */
    DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) obj, obj->builtins[DUK_BIDX_THREAD_PROTOTYPE]);

    /* Initial stack size satisfies the stack slack constraints so there
     * is no need to require stack here.
     */
    DUK_ASSERT(DUK_VALSTACK_INITIAL_SIZE >=
               DUK_VALSTACK_API_ENTRY_MINIMUM + DUK_VALSTACK_INTERNAL_EXTRA);

    return ret;
}

DUK_INTERNAL duk_hcompfunc *duk_push_hcompfunc(duk_hthread *thr) {
    duk_hcompfunc *obj;
    duk_tval *tv_slot;

    DUK_ASSERT_API_ENTRY(thr);

    DUK__CHECK_SPACE();

    /* Template functions are not strictly constructable (they don't
     * have a "prototype" property for instance), so leave the
     * DUK_HOBJECT_FLAG_CONSRUCTABLE flag cleared here.
     */

    obj = duk_hcompfunc_alloc(thr,
                              DUK_HOBJECT_FLAG_EXTENSIBLE |
                              DUK_HOBJECT_FLAG_CALLABLE |
                              DUK_HOBJECT_FLAG_COMPFUNC |
                              DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION));
    if (DUK_UNLIKELY(obj == NULL)) {
        DUK_ERROR_ALLOC_FAILED(thr);
        DUK_WO_NORETURN(return NULL;);
    }

    DUK_DDD(DUK_DDDPRINT("created compiled function object with flags: 0x%08lx", (unsigned long) obj->obj.hdr.h_flags));

    tv_slot = thr->valstack_top;
    DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj);
    DUK_HOBJECT_INCREF(thr, obj);
    thr->valstack_top++;

    /* default prototype */
    DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) obj) == NULL);
    DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);

    return obj;
}

DUK_INTERNAL duk_hboundfunc *duk_push_hboundfunc(duk_hthread *thr) {
    duk_hboundfunc *obj;
    duk_tval *tv_slot;

    DUK_ASSERT_API_ENTRY(thr);

    DUK__CHECK_SPACE();
    obj = duk_hboundfunc_alloc(thr->heap,
                               DUK_HOBJECT_FLAG_EXTENSIBLE |
                               DUK_HOBJECT_FLAG_BOUNDFUNC |
                               DUK_HOBJECT_FLAG_CONSTRUCTABLE |
                               DUK_HOBJECT_FLAG_CALLABLE |
                               DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION));
    if (!obj) {
        DUK_ERROR_ALLOC_FAILED(thr);
        DUK_WO_NORETURN(return NULL;);
    }

    tv_slot = thr->valstack_top++;
    DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj);
    DUK_HOBJECT_INCREF(thr, obj);

    /* Prototype is left as NULL because the caller always sets it (and
     * it depends on the target function).
     */
    DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) obj) == NULL);

    return obj;
}

DUK_LOCAL duk_idx_t duk__push_c_function_raw(duk_hthread *thr, duk_c_function func, duk_idx_t nargs, duk_uint_t flags, duk_small_uint_t proto_bidx) {
    duk_hnatfunc *obj;
    duk_idx_t ret;
    duk_tval *tv_slot;
    duk_int16_t func_nargs;

    DUK_CTX_ASSERT_VALID(thr);

    DUK__CHECK_SPACE();

    if (DUK_UNLIKELY(func == NULL)) {
        goto api_error;
    }
    if (nargs >= 0 && nargs < DUK_HNATFUNC_NARGS_MAX) {
        func_nargs = (duk_int16_t) nargs;
    } else if (nargs == DUK_VARARGS) {
        func_nargs = DUK_HNATFUNC_NARGS_VARARGS;
    } else {
        goto api_error;
    }

    obj = duk_hnatfunc_alloc(thr, flags);
    DUK_ASSERT(obj != NULL);

    obj->func = func;
    obj->nargs = func_nargs;

    DUK_DDD(DUK_DDDPRINT("created native function object with flags: 0x%08lx, nargs=%ld",
                         (unsigned long) obj->obj.hdr.h_flags, (long) obj->nargs));

    tv_slot = thr->valstack_top;
    DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj);
    DUK_HOBJECT_INCREF(thr, obj);
    ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
    thr->valstack_top++;

    DUK_ASSERT_BIDX_VALID(proto_bidx);
    DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) obj, thr->builtins[proto_bidx]);
    return ret;

 api_error:
    DUK_ERROR_TYPE_INVALID_ARGS(thr);
    DUK_WO_NORETURN(return 0;);
}

DUK_EXTERNAL duk_idx_t duk_push_c_function(duk_hthread *thr, duk_c_function func, duk_int_t nargs) {
    duk_uint_t flags;

    DUK_ASSERT_API_ENTRY(thr);

    flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
            DUK_HOBJECT_FLAG_CONSTRUCTABLE |
            DUK_HOBJECT_FLAG_CALLABLE |
            DUK_HOBJECT_FLAG_FASTREFS |
            DUK_HOBJECT_FLAG_NATFUNC |
            DUK_HOBJECT_FLAG_NEWENV |
            DUK_HOBJECT_FLAG_STRICT |
            DUK_HOBJECT_FLAG_NOTAIL |
            DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION);

    /* Default prototype is a Duktape specific %NativeFunctionPrototype%
     * which provides .length and .name getters.
     */
    return duk__push_c_function_raw(thr, func, nargs, flags, DUK_BIDX_NATIVE_FUNCTION_PROTOTYPE);
}

DUK_INTERNAL void duk_push_c_function_builtin(duk_hthread *thr, duk_c_function func, duk_int_t nargs) {
    duk_uint_t flags;

    DUK_ASSERT_API_ENTRY(thr);

    flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
            DUK_HOBJECT_FLAG_CONSTRUCTABLE |
            DUK_HOBJECT_FLAG_CALLABLE |
            DUK_HOBJECT_FLAG_FASTREFS |
            DUK_HOBJECT_FLAG_NATFUNC |
            DUK_HOBJECT_FLAG_NEWENV |
            DUK_HOBJECT_FLAG_STRICT |
            DUK_HOBJECT_FLAG_NOTAIL |
            DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION);

    /* Must use Function.prototype for standard built-in functions. */
    (void) duk__push_c_function_raw(thr, func, nargs, flags, DUK_BIDX_FUNCTION_PROTOTYPE);
}

DUK_INTERNAL void duk_push_c_function_builtin_noconstruct(duk_hthread *thr, duk_c_function func, duk_int_t nargs) {
    duk_uint_t flags;

    DUK_ASSERT_API_ENTRY(thr);

    flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
            DUK_HOBJECT_FLAG_CALLABLE |
            DUK_HOBJECT_FLAG_FASTREFS |
            DUK_HOBJECT_FLAG_NATFUNC |
            DUK_HOBJECT_FLAG_NEWENV |
            DUK_HOBJECT_FLAG_STRICT |
            DUK_HOBJECT_FLAG_NOTAIL |
            DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION);

    /* Must use Function.prototype for standard built-in functions. */
    (void) duk__push_c_function_raw(thr, func, nargs, flags, DUK_BIDX_FUNCTION_PROTOTYPE);
}

DUK_EXTERNAL duk_idx_t duk_push_c_lightfunc(duk_hthread *thr, duk_c_function func, duk_idx_t nargs, duk_idx_t length, duk_int_t magic) {
    duk_small_uint_t lf_flags;
    duk_tval *tv_slot;

    DUK_ASSERT_API_ENTRY(thr);

    DUK__CHECK_SPACE();

    if (nargs >= DUK_LFUNC_NARGS_MIN && nargs <= DUK_LFUNC_NARGS_MAX) {
        /* as is */
    } else if (nargs == DUK_VARARGS) {
        nargs = DUK_LFUNC_NARGS_VARARGS;
    } else {
        goto api_error;
    }
    if (DUK_UNLIKELY(!(length >= DUK_LFUNC_LENGTH_MIN && length <= DUK_LFUNC_LENGTH_MAX))) {
        goto api_error;
    }
    if (DUK_UNLIKELY(!(magic >= DUK_LFUNC_MAGIC_MIN && magic <= DUK_LFUNC_MAGIC_MAX))) {
        goto api_error;
    }

    lf_flags = DUK_LFUNC_FLAGS_PACK((duk_small_int_t) magic, (duk_small_uint_t) length, (duk_small_uint_t) nargs);
    tv_slot = thr->valstack_top++;
    DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv_slot));
    DUK_TVAL_SET_LIGHTFUNC(tv_slot, func, lf_flags);
    DUK_ASSERT(tv_slot >= thr->valstack_bottom);
    return (duk_idx_t) (tv_slot - thr->valstack_bottom);

 api_error:
    DUK_ERROR_TYPE_INVALID_ARGS(thr);
    DUK_WO_NORETURN(return 0;);
}

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL duk_hbufobj *duk_push_bufobj_raw(duk_hthread *thr, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx) {
    duk_hbufobj *obj;
    duk_tval *tv_slot;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(prototype_bidx >= 0);

    DUK__CHECK_SPACE();

    obj = duk_hbufobj_alloc(thr, hobject_flags_and_class);
    DUK_ASSERT(obj != NULL);

    DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) obj, thr->builtins[prototype_bidx]);
    DUK_HBUFOBJ_ASSERT_VALID(obj);

    tv_slot = thr->valstack_top;
    DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj);
    DUK_HOBJECT_INCREF(thr, obj);
    thr->valstack_top++;

    return obj;
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/* XXX: There's quite a bit of overlap with buffer creation handling in
 * duk_bi_buffer.c.  Look for overlap and refactor.
 */
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
#define DUK__PACK_ARGS(classnum,protobidx,elemtype,elemshift,istypedarray) \
    (((classnum) << 24) | ((protobidx) << 16) | ((elemtype) << 8) | ((elemshift) << 4) | (istypedarray))

static const duk_uint32_t duk__bufobj_flags_lookup[] = {
    /* Node.js Buffers are Uint8Array instances which inherit from Buffer.prototype. */
    DUK__PACK_ARGS(DUK_HOBJECT_CLASS_ARRAYBUFFER,       DUK_BIDX_ARRAYBUFFER_PROTOTYPE,       DUK_HBUFOBJ_ELEM_UINT8,        0, 0),  /* DUK_BUFOBJ_ARRAYBUFFER */
    DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT8ARRAY,        DUK_BIDX_NODEJS_BUFFER_PROTOTYPE,     DUK_HBUFOBJ_ELEM_UINT8,        0, 1),  /* DUK_BUFOBJ_NODEJS_BUFFER */
    DUK__PACK_ARGS(DUK_HOBJECT_CLASS_DATAVIEW,          DUK_BIDX_DATAVIEW_PROTOTYPE,          DUK_HBUFOBJ_ELEM_UINT8,        0, 0),  /* DUK_BUFOBJ_DATAVIEW */
    DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT8ARRAY,         DUK_BIDX_INT8ARRAY_PROTOTYPE,         DUK_HBUFOBJ_ELEM_INT8,         0, 1),  /* DUK_BUFOBJ_INT8ARRAY */
    DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT8ARRAY,        DUK_BIDX_UINT8ARRAY_PROTOTYPE,        DUK_HBUFOBJ_ELEM_UINT8,        0, 1),  /* DUK_BUFOBJ_UINT8ARRAY */
    DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT8CLAMPEDARRAY, DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE, DUK_HBUFOBJ_ELEM_UINT8CLAMPED, 0, 1),  /* DUK_BUFOBJ_UINT8CLAMPEDARRAY */
    DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT16ARRAY,        DUK_BIDX_INT16ARRAY_PROTOTYPE,        DUK_HBUFOBJ_ELEM_INT16,        1, 1),  /* DUK_BUFOBJ_INT16ARRAY */
    DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT16ARRAY,       DUK_BIDX_UINT16ARRAY_PROTOTYPE,       DUK_HBUFOBJ_ELEM_UINT16,       1, 1),  /* DUK_BUFOBJ_UINT16ARRAY */
    DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT32ARRAY,        DUK_BIDX_INT32ARRAY_PROTOTYPE,        DUK_HBUFOBJ_ELEM_INT32,        2, 1),  /* DUK_BUFOBJ_INT32ARRAY */
    DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT32ARRAY,       DUK_BIDX_UINT32ARRAY_PROTOTYPE,       DUK_HBUFOBJ_ELEM_UINT32,       2, 1),  /* DUK_BUFOBJ_UINT32ARRAY */
    DUK__PACK_ARGS(DUK_HOBJECT_CLASS_FLOAT32ARRAY,      DUK_BIDX_FLOAT32ARRAY_PROTOTYPE,      DUK_HBUFOBJ_ELEM_FLOAT32,      2, 1),  /* DUK_BUFOBJ_FLOAT32ARRAY */
    DUK__PACK_ARGS(DUK_HOBJECT_CLASS_FLOAT64ARRAY,      DUK_BIDX_FLOAT64ARRAY_PROTOTYPE,      DUK_HBUFOBJ_ELEM_FLOAT64,      3, 1)   /* DUK_BUFOBJ_FLOAT64ARRAY */
};
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_EXTERNAL void duk_push_buffer_object(duk_hthread *thr, duk_idx_t idx_buffer, duk_size_t byte_offset, duk_size_t byte_length, duk_uint_t flags) {
    duk_hbufobj *h_bufobj;
    duk_hbuffer *h_val;
    duk_hobject *h_arraybuf;
    duk_uint32_t tmp;
    duk_uint_t classnum;
    duk_uint_t protobidx;
    duk_uint_t lookupidx;
    duk_uint_t uint_offset, uint_length, uint_added;

    DUK_ASSERT_API_ENTRY(thr);

    /* The underlying types for offset/length in duk_hbufobj is
     * duk_uint_t; make sure argument values fit.
     */
    uint_offset = (duk_uint_t) byte_offset;
    uint_length = (duk_uint_t) byte_length;
    if (sizeof(duk_size_t) != sizeof(duk_uint_t)) {
        if (DUK_UNLIKELY((duk_size_t) uint_offset != byte_offset || (duk_size_t) uint_length != byte_length)) {
            goto range_error;
        }
    }

    DUK_ASSERT_DISABLE(flags >= 0);  /* flags is unsigned */
    lookupidx = flags;
    if (DUK_UNLIKELY(lookupidx >= sizeof(duk__bufobj_flags_lookup) / sizeof(duk_uint32_t))) {
        goto arg_error;
    }
    tmp = duk__bufobj_flags_lookup[lookupidx];
    classnum = tmp >> 24;
    protobidx = (tmp >> 16) & 0xff;

    h_arraybuf = duk_get_hobject(thr, idx_buffer);
    if (h_arraybuf != NULL &&  /* argument is an object */
        flags != DUK_BUFOBJ_ARRAYBUFFER &&  /* creating a view */
        DUK_HOBJECT_GET_CLASS_NUMBER(h_arraybuf) == DUK_HOBJECT_CLASS_ARRAYBUFFER  /* argument is ArrayBuffer */) {
        duk_uint_t tmp_offset;

        DUK_HBUFOBJ_ASSERT_VALID((duk_hbufobj *) h_arraybuf);
        h_val = ((duk_hbufobj *) h_arraybuf)->buf;
        if (DUK_UNLIKELY(h_val == NULL)) {
            goto arg_error;
        }

        tmp_offset = uint_offset + ((duk_hbufobj *) h_arraybuf)->offset;
        if (DUK_UNLIKELY(tmp_offset < uint_offset)) {
            goto range_error;
        }
        uint_offset = tmp_offset;

        /* Note intentional difference to new TypedArray(): we allow
         * caller to create an uncovered typed array (which is memory
         * safe); new TypedArray() rejects it.
         */
    } else {
        /* Handle unexpected object arguments here too, for nice error
         * messages.
         */
        h_arraybuf = NULL;
        h_val = duk_require_hbuffer(thr, idx_buffer);
    }

    /* Wrap check for offset+length. */
    uint_added = uint_offset + uint_length;
    if (DUK_UNLIKELY(uint_added < uint_offset)) {
        goto range_error;
    }
    DUK_ASSERT(uint_added >= uint_offset && uint_added >= uint_length);

    DUK_ASSERT(h_val != NULL);

    h_bufobj = duk_push_bufobj_raw(thr,
                                   DUK_HOBJECT_FLAG_EXTENSIBLE |
                                   DUK_HOBJECT_FLAG_BUFOBJ |
                                   DUK_HOBJECT_CLASS_AS_FLAGS(classnum),
                                   (duk_small_int_t) protobidx);
    DUK_ASSERT(h_bufobj != NULL);

    h_bufobj->buf = h_val;
    DUK_HBUFFER_INCREF(thr, h_val);
    h_bufobj->buf_prop = h_arraybuf;
    DUK_HOBJECT_INCREF_ALLOWNULL(thr, h_arraybuf);
    h_bufobj->offset = uint_offset;
    h_bufobj->length = uint_length;
    h_bufobj->shift = (tmp >> 4) & 0x0f;
    h_bufobj->elem_type = (tmp >> 8) & 0xff;
    h_bufobj->is_typedarray = tmp & 0x0f;
    DUK_HBUFOBJ_ASSERT_VALID(h_bufobj);

    /* TypedArray views need an automatic ArrayBuffer which must be
     * provided as .buffer property of the view.  The ArrayBuffer is
     * referenced via duk_hbufobj->buf_prop and an inherited .buffer
     * accessor returns it.  The ArrayBuffer is created lazily on first
     * access if necessary so we don't need to do anything more here.
     */
    return;

 range_error:
    DUK_ERROR_RANGE(thr, DUK_STR_INVALID_ARGS);
    DUK_WO_NORETURN(return;);

 arg_error:
    DUK_ERROR_TYPE(thr, DUK_STR_INVALID_ARGS);
    DUK_WO_NORETURN(return;);
}
#else  /* DUK_USE_BUFFEROBJECT_SUPPORT */
DUK_EXTERNAL void duk_push_buffer_object(duk_hthread *thr, duk_idx_t idx_buffer, duk_size_t byte_offset, duk_size_t byte_length, duk_uint_t flags) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_UNREF(idx_buffer);
    DUK_UNREF(byte_offset);
    DUK_UNREF(byte_length);
    DUK_UNREF(flags);
    DUK_ERROR_UNSUPPORTED(thr);
    DUK_WO_NORETURN(return;);
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

DUK_EXTERNAL duk_idx_t duk_push_error_object_va_raw(duk_hthread *thr, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, va_list ap) {
    duk_hobject *proto;
#if defined(DUK_USE_AUGMENT_ERROR_CREATE)
    duk_small_uint_t augment_flags;
#endif

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(thr != NULL);
    DUK_UNREF(filename);
    DUK_UNREF(line);

    /* Error code also packs a tracedata related flag. */
#if defined(DUK_USE_AUGMENT_ERROR_CREATE)
    augment_flags = 0;
    if (err_code & DUK_ERRCODE_FLAG_NOBLAME_FILELINE) {
        augment_flags = DUK_AUGMENT_FLAG_NOBLAME_FILELINE;
    }
#endif
    err_code = err_code & (~DUK_ERRCODE_FLAG_NOBLAME_FILELINE);

    /* error gets its 'name' from the prototype */
    proto = duk_error_prototype_from_code(thr, err_code);
    (void) duk_push_object_helper_proto(thr,
                                        DUK_HOBJECT_FLAG_EXTENSIBLE |
                                        DUK_HOBJECT_FLAG_FASTREFS |
                                        DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ERROR),
                                        proto);

    /* ... and its 'message' from an instance property */
    if (fmt) {
        duk_push_vsprintf(thr, fmt, ap);
        duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC);
    } else {
        /* If no explicit message given, put error code into message field
         * (as a number).  This is not fully in keeping with the ECMAScript
         * error model because messages are supposed to be strings (Error
         * constructors use ToString() on their argument).  However, it's
         * probably more useful than having a separate 'code' property.
         */
        duk_push_int(thr, err_code);
        duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC);
    }

    /* XXX: .code = err_code disabled, not sure if useful */

    /* Creation time error augmentation */
#if defined(DUK_USE_AUGMENT_ERROR_CREATE)
    /* filename may be NULL in which case file/line is not recorded */
    duk_err_augment_error_create(thr, thr, filename, line, augment_flags);  /* may throw an error */
#endif

    return duk_get_top_index_unsafe(thr);
}

DUK_EXTERNAL duk_idx_t duk_push_error_object_raw(duk_hthread *thr, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, ...) {
    va_list ap;
    duk_idx_t ret;

    DUK_ASSERT_API_ENTRY(thr);

    va_start(ap, fmt);
    ret = duk_push_error_object_va_raw(thr, err_code, filename, line, fmt, ap);
    va_end(ap);
    return ret;
}

#if !defined(DUK_USE_VARIADIC_MACROS)
DUK_EXTERNAL duk_idx_t duk_push_error_object_stash(duk_hthread *thr, duk_errcode_t err_code, const char *fmt, ...) {
    const char *filename = duk_api_global_filename;
    duk_int_t line = duk_api_global_line;
    va_list ap;
    duk_idx_t ret;

    DUK_ASSERT_API_ENTRY(thr);

    duk_api_global_filename = NULL;
    duk_api_global_line = 0;
    va_start(ap, fmt);
    ret = duk_push_error_object_va_raw(thr, err_code, filename, line, fmt, ap);
    va_end(ap);
    return ret;
}
#endif  /* DUK_USE_VARIADIC_MACROS */

DUK_EXTERNAL void *duk_push_buffer_raw(duk_hthread *thr, duk_size_t size, duk_small_uint_t flags) {
    duk_tval *tv_slot;
    duk_hbuffer *h;
    void *buf_data;

    DUK_ASSERT_API_ENTRY(thr);

    DUK__CHECK_SPACE();

    /* Check for maximum buffer length. */
    if (DUK_UNLIKELY(size > DUK_HBUFFER_MAX_BYTELEN)) {
        DUK_ERROR_RANGE(thr, DUK_STR_BUFFER_TOO_LONG);
        DUK_WO_NORETURN(return NULL;);
    }

    h = duk_hbuffer_alloc(thr->heap, size, flags, &buf_data);
    if (DUK_UNLIKELY(h == NULL)) {
        DUK_ERROR_ALLOC_FAILED(thr);
        DUK_WO_NORETURN(return NULL;);
    }

    tv_slot = thr->valstack_top;
    DUK_TVAL_SET_BUFFER(tv_slot, h);
    DUK_HBUFFER_INCREF(thr, h);
    thr->valstack_top++;

    return (void *) buf_data;
}

DUK_INTERNAL void *duk_push_fixed_buffer_nozero(duk_hthread *thr, duk_size_t len) {
    DUK_ASSERT_API_ENTRY(thr);
    return duk_push_buffer_raw(thr, len, DUK_BUF_FLAG_NOZERO);
}

DUK_INTERNAL void *duk_push_fixed_buffer_zero(duk_hthread *thr, duk_size_t len) {
    void *ptr;

    DUK_ASSERT_API_ENTRY(thr);

    ptr = duk_push_buffer_raw(thr, len, 0);
    DUK_ASSERT(ptr != NULL);
#if !defined(DUK_USE_ZERO_BUFFER_DATA)
    /* ES2015 requires zeroing even when DUK_USE_ZERO_BUFFER_DATA
     * is not set.
     */
    duk_memzero((void *) ptr, (size_t) len);
#endif
    return ptr;
}

#if defined(DUK_USE_ES6_PROXY)
DUK_EXTERNAL duk_idx_t duk_push_proxy(duk_hthread *thr, duk_uint_t proxy_flags) {
    duk_hobject *h_target;
    duk_hobject *h_handler;
    duk_hproxy *h_proxy;
    duk_tval *tv_slot;
    duk_uint_t flags;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_UNREF(proxy_flags);

    /* DUK__CHECK_SPACE() unnecessary because the Proxy is written to
     * value stack in-place.
     */
#if 0
    DUK__CHECK_SPACE();
#endif

    /* Reject a proxy object as the target because it would need
     * special handling in property lookups.  (ES2015 has no such
     * restriction.)
     */
    h_target = duk_require_hobject_promote_mask(thr, -2, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
    DUK_ASSERT(h_target != NULL);
    if (DUK_HOBJECT_IS_PROXY(h_target)) {
        goto fail_args;
    }

    /* Reject a proxy object as the handler because it would cause
     * potentially unbounded recursion.  (ES2015 has no such
     * restriction.)
     *
     * There's little practical reason to use a lightfunc or a plain
     * buffer as the handler table: one could only provide traps via
     * their prototype objects (Function.prototype and ArrayBuffer.prototype).
     * Even so, as lightfuncs and plain buffers mimic their object
     * counterparts, they're promoted and accepted here.
     */
    h_handler = duk_require_hobject_promote_mask(thr, -1, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
    DUK_ASSERT(h_handler != NULL);
    if (DUK_HOBJECT_IS_PROXY(h_handler)) {
        goto fail_args;
    }

    /* XXX: Proxy object currently has no prototype, so ToPrimitive()
     * coercion fails which is a bit confusing.
     */

    /* CALLABLE and CONSTRUCTABLE flags are copied from the (initial)
     * target, see ES2015 Sections 9.5.15 and 9.5.13.
     */
    flags = DUK_HEAPHDR_GET_FLAGS((duk_heaphdr *) h_target) &
            (DUK_HOBJECT_FLAG_CALLABLE | DUK_HOBJECT_FLAG_CONSTRUCTABLE);
    flags |= DUK_HOBJECT_FLAG_EXTENSIBLE |
             DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ;
    if (flags & DUK_HOBJECT_FLAG_CALLABLE) {
        flags |= DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION) |
                 DUK_HOBJECT_FLAG_SPECIAL_CALL;
    } else {
        flags |= DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT);
    }

    h_proxy = duk_hproxy_alloc(thr, flags);
    DUK_ASSERT(h_proxy != NULL);
    DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_proxy) == NULL);

    /* Initialize Proxy target and handler references; avoid INCREF
     * by stealing the value stack refcounts via direct value stack
     * manipulation.  INCREF is needed for the Proxy itself however.
     */
    DUK_ASSERT(h_target != NULL);
    h_proxy->target = h_target;
    DUK_ASSERT(h_handler != NULL);
    h_proxy->handler = h_handler;
    DUK_HPROXY_ASSERT_VALID(h_proxy);

    DUK_ASSERT(duk_get_hobject(thr, -2) == h_target);
    DUK_ASSERT(duk_get_hobject(thr, -1) == h_handler);
    tv_slot = thr->valstack_top - 2;
    DUK_ASSERT(tv_slot >= thr->valstack_bottom);
    DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) h_proxy);
    DUK_HOBJECT_INCREF(thr, (duk_hobject *) h_proxy);
    tv_slot++;
    DUK_TVAL_SET_UNDEFINED(tv_slot);  /* [ ... target handler ] -> [ ... proxy undefined ] */
    thr->valstack_top = tv_slot;      /* -> [ ... proxy ] */

    DUK_DD(DUK_DDPRINT("created Proxy: %!iT", duk_get_tval(thr, -1)));

    return (duk_idx_t) (thr->valstack_top - thr->valstack_bottom - 1);

 fail_args:
    DUK_ERROR_TYPE_INVALID_ARGS(thr);
    DUK_WO_NORETURN(return 0;);
}
#else  /* DUK_USE_ES6_PROXY */
DUK_EXTERNAL duk_idx_t duk_push_proxy(duk_hthread *thr, duk_uint_t proxy_flags) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_UNREF(proxy_flags);
    DUK_ERROR_UNSUPPORTED(thr);
    DUK_WO_NORETURN(return 0;);
}
#endif  /* DUK_USE_ES6_PROXY */

#if defined(DUK_USE_ASSERTIONS)
DUK_LOCAL void duk__validate_push_heapptr(duk_hthread *thr, void *ptr) {
    duk_heaphdr *h;
    duk_heaphdr *curr;
    duk_bool_t found = 0;

    h = (duk_heaphdr *) ptr;
    if (h == NULL) {
        /* Allowed. */
        return;
    }
    DUK_ASSERT(h != NULL);
    DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h));

    /* One particular problem case is where an object has been
     * queued for finalization but the finalizer hasn't yet been
     * executed.
     *
     * Corner case: we're running in a finalizer for object X, and
     * user code calls duk_push_heapptr() for X itself.  In this
     * case X will be in finalize_list, and we can detect the case
     * by seeing that X's FINALIZED flag is set (which is done before
     * the finalizer starts executing).
     */
#if defined(DUK_USE_FINALIZER_SUPPORT)
    for (curr = thr->heap->finalize_list;
         curr != NULL;
         curr = DUK_HEAPHDR_GET_NEXT(thr->heap, curr)) {
        /* FINALIZABLE is set for all objects on finalize_list
         * except for an object being finalized right now.  So
         * can't assert here.
         */
#if 0
        DUK_ASSERT(DUK_HEAPHDR_HAS_FINALIZABLE(curr));
#endif

        if (curr == h) {
            if (DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) h)) {
                /* Object is currently being finalized. */
                DUK_ASSERT(found == 0);  /* Would indicate corrupted lists. */
                found = 1;
            } else {
                /* Not being finalized but on finalize_list,
                 * allowed since Duktape 2.1.
                 */
                DUK_ASSERT(found == 0);  /* Would indicate corrupted lists. */
                found = 1;
            }
        }
    }
#endif  /* DUK_USE_FINALIZER_SUPPORT */

#if defined(DUK_USE_REFERENCE_COUNTING)
    /* Because refzero_list is now processed to completion inline with
     * no side effects, it's always empty here.
     */
    DUK_ASSERT(thr->heap->refzero_list == NULL);
#endif

    /* If not present in finalize_list (or refzero_list), it
     * must be either in heap_allocated or the string table.
     */
    if (DUK_HEAPHDR_IS_STRING(h)) {
        duk_uint32_t i;
        duk_hstring *str;
        duk_heap *heap = thr->heap;

        DUK_ASSERT(found == 0);
        for (i = 0; i < heap->st_size; i++) {
#if defined(DUK_USE_STRTAB_PTRCOMP)
            str = DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, heap->strtable16[i]);
#else
            str = heap->strtable[i];
#endif
            while (str != NULL) {
                if (str == (duk_hstring *) h) {
                    DUK_ASSERT(found == 0);  /* Would indicate corrupted lists. */
                    found = 1;
                    break;
                }
                str = str->hdr.h_next;
            }
        }
        DUK_ASSERT(found != 0);
    } else {
        for (curr = thr->heap->heap_allocated;
             curr != NULL;
             curr = DUK_HEAPHDR_GET_NEXT(thr->heap, curr)) {
            if (curr == h) {
                DUK_ASSERT(found == 0);  /* Would indicate corrupted lists. */
                found = 1;
            }
        }
        DUK_ASSERT(found != 0);
    }
}
#endif  /* DUK_USE_ASSERTIONS */

DUK_EXTERNAL duk_idx_t duk_push_heapptr(duk_hthread *thr, void *ptr) {
    duk_idx_t ret;
    duk_tval *tv;

    DUK_ASSERT_API_ENTRY(thr);

    /* Reviving an object using a heap pointer is a dangerous API
     * operation: if the application doesn't guarantee that the
     * pointer target is always reachable, difficult-to-diagnose
     * problems may ensue.  Try to validate the 'ptr' argument to
     * the extent possible.
     */

#if defined(DUK_USE_ASSERTIONS)
    duk__validate_push_heapptr(thr, ptr);
#endif

    DUK__CHECK_SPACE();

    ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
    tv = thr->valstack_top++;

    if (ptr == NULL) {
        DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv));
        return ret;
    }

    DUK_HEAPHDR_ASSERT_VALID((duk_heaphdr *) ptr);

    /* If the argument is on finalize_list it has technically been
     * unreachable before duk_push_heapptr() but it's still safe to
     * push it.  Starting from Duktape 2.1 allow application code to
     * do so.  There are two main cases:
     *
     *   (1) The object is on the finalize_list and we're called by
     *       the finalizer for the object being finalized.  In this
     *       case do nothing: finalize_list handling will deal with
     *       the object queueing.  This is detected by the object not
     *       having a FINALIZABLE flag despite being on the finalize_list;
     *       the flag is cleared for the object being finalized only.
     *
     *   (2) The object is on the finalize_list but is not currently
     *       being processed.  In this case the object can be queued
     *       back to heap_allocated with a few flags cleared, in effect
     *       cancelling the finalizer.
     */
    if (DUK_UNLIKELY(DUK_HEAPHDR_HAS_FINALIZABLE((duk_heaphdr *) ptr))) {
        duk_heaphdr *curr;

        DUK_D(DUK_DPRINT("duk_push_heapptr() with a pointer on finalize_list, autorescue"));

        curr = (duk_heaphdr *) ptr;
        DUK_HEAPHDR_CLEAR_FINALIZABLE(curr);

        /* Because FINALIZED is set prior to finalizer call, it will
         * be set for the object being currently finalized, but not
         * for other objects on finalize_list.
         */
        DUK_HEAPHDR_CLEAR_FINALIZED(curr);

        /* Dequeue object from finalize_list and queue it back to
         * heap_allocated.
         */
#if defined(DUK_USE_REFERENCE_COUNTING)
        DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(curr) >= 1);  /* Preincremented on finalize_list insert. */
        DUK_HEAPHDR_PREDEC_REFCOUNT(curr);
#endif
        DUK_HEAP_REMOVE_FROM_FINALIZE_LIST(thr->heap, curr);
        DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(thr->heap, curr);

        /* Continue with the rest. */
    }

    switch (DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) ptr)) {
    case DUK_HTYPE_STRING:
        DUK_TVAL_SET_STRING(tv, (duk_hstring *) ptr);
        break;
    case DUK_HTYPE_OBJECT:
        DUK_TVAL_SET_OBJECT(tv, (duk_hobject *) ptr);
        break;
    default:
        DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) ptr) == DUK_HTYPE_BUFFER);
        DUK_TVAL_SET_BUFFER(tv, (duk_hbuffer *) ptr);
        break;
    }

    DUK_HEAPHDR_INCREF(thr, (duk_heaphdr *) ptr);

    return ret;
}

/* Push object with no prototype, i.e. a "bare" object. */
DUK_EXTERNAL duk_idx_t duk_push_bare_object(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);

    (void) duk_push_object_helper(thr,
                                  DUK_HOBJECT_FLAG_EXTENSIBLE |
                                  DUK_HOBJECT_FLAG_FASTREFS |
                                  DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
                                  -1);  /* no prototype */
    return duk_get_top_index_unsafe(thr);
}

DUK_INTERNAL void duk_push_hstring(duk_hthread *thr, duk_hstring *h) {
    duk_tval tv;

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

    DUK_TVAL_SET_STRING(&tv, h);
    duk_push_tval(thr, &tv);
}

DUK_INTERNAL void duk_push_hstring_stridx(duk_hthread *thr, duk_small_uint_t stridx) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT_STRIDX_VALID(stridx);
    duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx));
}

DUK_INTERNAL void duk_push_hstring_empty(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, DUK_STRIDX_EMPTY_STRING));
}

DUK_INTERNAL void duk_push_hobject(duk_hthread *thr, duk_hobject *h) {
    duk_tval tv;

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

    DUK_TVAL_SET_OBJECT(&tv, h);
    duk_push_tval(thr, &tv);
}

DUK_INTERNAL void duk_push_hbuffer(duk_hthread *thr, duk_hbuffer *h) {
    duk_tval tv;

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

    DUK_TVAL_SET_BUFFER(&tv, h);
    duk_push_tval(thr, &tv);
}

DUK_INTERNAL void duk_push_hobject_bidx(duk_hthread *thr, duk_small_int_t builtin_idx) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(builtin_idx >= 0 && builtin_idx < DUK_NUM_BUILTINS);
    DUK_ASSERT(thr->builtins[builtin_idx] != NULL);

    duk_push_hobject(thr, thr->builtins[builtin_idx]);
}

/*
 *  Poppers
 */

DUK_LOCAL DUK_ALWAYS_INLINE void duk__pop_n_unsafe_raw(duk_hthread *thr, duk_idx_t count) {
    duk_tval *tv;
#if defined(DUK_USE_REFERENCE_COUNTING)
    duk_tval *tv_end;
#endif

    DUK_CTX_ASSERT_VALID(thr);
    DUK_ASSERT(count >= 0);
    DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) count);

#if defined(DUK_USE_REFERENCE_COUNTING)
    tv = thr->valstack_top;
    tv_end = tv - count;
    while (tv != tv_end) {
        tv--;
        DUK_ASSERT(tv >= thr->valstack_bottom);
        DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ(thr, tv);
    }
    thr->valstack_top = tv;
    DUK_REFZERO_CHECK_FAST(thr);
#else
    tv = thr->valstack_top;
    while (count > 0) {
        count--;
        tv--;
        DUK_ASSERT(tv >= thr->valstack_bottom);
        DUK_TVAL_SET_UNDEFINED(tv);
    }
    thr->valstack_top = tv;
#endif

    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
}

DUK_EXTERNAL void duk_pop_n(duk_hthread *thr, duk_idx_t count) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);

    if (DUK_UNLIKELY((duk_uidx_t) (thr->valstack_top - thr->valstack_bottom) < (duk_uidx_t) count)) {
        DUK_ERROR_RANGE_INVALID_COUNT(thr);
        DUK_WO_NORETURN(return;);
    }
    DUK_ASSERT(count >= 0);

    duk__pop_n_unsafe_raw(thr, count);
}

#if defined(DUK_USE_PREFER_SIZE)
DUK_INTERNAL void duk_pop_n_unsafe(duk_hthread *thr, duk_idx_t count) {
    DUK_ASSERT_API_ENTRY(thr);
    duk_pop_n(thr, count);
}
#else  /* DUK_USE_PREFER_SIZE */
DUK_INTERNAL void duk_pop_n_unsafe(duk_hthread *thr, duk_idx_t count) {
    DUK_ASSERT_API_ENTRY(thr);
    duk__pop_n_unsafe_raw(thr, count);
}
#endif  /* DUK_USE_PREFER_SIZE */

/* Pop N elements without DECREF (in effect "stealing" any actual refcounts). */
#if defined(DUK_USE_REFERENCE_COUNTING)
DUK_INTERNAL void duk_pop_n_nodecref_unsafe(duk_hthread *thr, duk_idx_t count) {
    duk_tval *tv;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(count >= 0);
    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
    DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) count);

    tv = thr->valstack_top;
    while (count > 0) {
        count--;
        tv--;
        DUK_ASSERT(tv >= thr->valstack_bottom);
        DUK_TVAL_SET_UNDEFINED(tv);
    }
    thr->valstack_top = tv;

    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
}
#else  /* DUK_USE_REFERENCE_COUNTING */
DUK_INTERNAL void duk_pop_n_nodecref_unsafe(duk_hthread *thr, duk_idx_t count) {
    DUK_ASSERT_API_ENTRY(thr);
    duk_pop_n_unsafe(thr, count);
}
#endif  /* DUK_USE_REFERENCE_COUNTING */

/* Popping one element is called so often that when footprint is not an issue,
 * compile a specialized function for it.
 */
#if defined(DUK_USE_PREFER_SIZE)
DUK_EXTERNAL void duk_pop(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    duk_pop_n(thr, 1);
}
DUK_INTERNAL void duk_pop_unsafe(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    duk_pop_n_unsafe(thr, 1);
}
DUK_INTERNAL void duk_pop_nodecref_unsafe(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    duk_pop_n_nodecref_unsafe(thr, 1);
}
#else  /* DUK_USE_PREFER_SIZE */
DUK_LOCAL DUK_ALWAYS_INLINE void duk__pop_unsafe_raw(duk_hthread *thr) {
    duk_tval *tv;

    DUK_CTX_ASSERT_VALID(thr);
    DUK_ASSERT(thr->valstack_top != thr->valstack_bottom);
    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
    DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) 1);

    tv = --thr->valstack_top;
    DUK_ASSERT(tv >= thr->valstack_bottom);
#if defined(DUK_USE_REFERENCE_COUNTING)
    DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv);  /* side effects */
#else
    DUK_TVAL_SET_UNDEFINED(tv);
#endif

    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
}
DUK_EXTERNAL void duk_pop(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);

    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
    if (DUK_UNLIKELY(thr->valstack_top == thr->valstack_bottom)) {
        DUK_ERROR_RANGE_INVALID_COUNT(thr);
        DUK_WO_NORETURN(return;);
    }

    duk__pop_unsafe_raw(thr);
}
DUK_INTERNAL void duk_pop_unsafe(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    duk__pop_unsafe_raw(thr);
}
DUK_INTERNAL void duk_pop_nodecref_unsafe(duk_hthread *thr) {
    duk_tval *tv;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(thr->valstack_top != thr->valstack_bottom);
    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
    DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) 1);

    tv = --thr->valstack_top;
    DUK_ASSERT(tv >= thr->valstack_bottom);
    DUK_TVAL_SET_UNDEFINED(tv);

    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
}
#endif  /* !DUK_USE_PREFER_SIZE */

#if defined(DUK_USE_PREFER_SIZE)
DUK_INTERNAL void duk_pop_undefined(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    duk_pop_nodecref_unsafe(thr);
}
#else  /* DUK_USE_PREFER_SIZE */
DUK_INTERNAL void duk_pop_undefined(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(thr->valstack_top != thr->valstack_bottom);
    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
    DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) 1);

    DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top - 1));
    thr->valstack_top--;

    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
}
#endif  /* !DUK_USE_PREFER_SIZE */

#if defined(DUK_USE_PREFER_SIZE)
DUK_EXTERNAL void duk_pop_2(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    duk_pop_n(thr, 2);
}
DUK_INTERNAL void duk_pop_2_unsafe(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    duk_pop_n_unsafe(thr, 2);
}
DUK_INTERNAL void duk_pop_2_nodecref_unsafe(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    duk_pop_n_nodecref_unsafe(thr, 2);
}
#else
DUK_LOCAL DUK_ALWAYS_INLINE void duk__pop_2_unsafe_raw(duk_hthread *thr) {
    duk_tval *tv;

    DUK_CTX_ASSERT_VALID(thr);
    DUK_ASSERT(thr->valstack_top != thr->valstack_bottom);
    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
    DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) 2);

    tv = --thr->valstack_top;
    DUK_ASSERT(tv >= thr->valstack_bottom);
#if defined(DUK_USE_REFERENCE_COUNTING)
    DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv);  /* side effects */
#else
    DUK_TVAL_SET_UNDEFINED(tv);
#endif
    tv = --thr->valstack_top;
    DUK_ASSERT(tv >= thr->valstack_bottom);
#if defined(DUK_USE_REFERENCE_COUNTING)
    DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv);  /* side effects */
#else
    DUK_TVAL_SET_UNDEFINED(tv);
#endif

    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
}
DUK_EXTERNAL void duk_pop_2(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);

    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
    if (DUK_UNLIKELY(thr->valstack_top - 2 < thr->valstack_bottom)) {
        DUK_ERROR_RANGE_INVALID_COUNT(thr);
        DUK_WO_NORETURN(return;);
    }

    duk__pop_2_unsafe_raw(thr);
}
DUK_INTERNAL void duk_pop_2_unsafe(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    duk__pop_2_unsafe_raw(thr);
}
DUK_INTERNAL void duk_pop_2_nodecref_unsafe(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(thr->valstack_top != thr->valstack_bottom);
    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
    DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) 2);

    DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top - 1));
    DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top - 2));
    thr->valstack_top -= 2;

    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
}
#endif  /* !DUK_USE_PREFER_SIZE */

DUK_EXTERNAL void duk_pop_3(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    duk_pop_n(thr, 3);
}

DUK_INTERNAL void duk_pop_3_unsafe(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    duk_pop_n_unsafe(thr, 3);
}

DUK_INTERNAL void duk_pop_3_nodecref_unsafe(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    duk_pop_n_nodecref_unsafe(thr, 3);
}

/*
 *  Pack and unpack (pack value stack entries into an array and vice versa)
 */

/* XXX: pack index range? array index offset? */
/* XXX: need ability to pack into a bare array? */
DUK_INTERNAL void duk_pack(duk_hthread *thr, duk_idx_t count) {
    duk_tval *tv_src;
    duk_tval *tv_dst;
    duk_tval *tv_curr;
    duk_tval *tv_limit;
    duk_idx_t top;

    DUK_ASSERT_API_ENTRY(thr);

    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
    top = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
    DUK_ASSERT(top >= 0);
    if (DUK_UNLIKELY((duk_uidx_t) count > (duk_uidx_t) top)) {
        /* Also handles negative count. */
        DUK_ERROR_RANGE_INVALID_COUNT(thr);
        DUK_WO_NORETURN(return;);
    }
    DUK_ASSERT(count >= 0);

    /* Wrapping is controlled by the check above: value stack top can be
     * at most DUK_USE_VALSTACK_LIMIT which is low enough so that
     * multiplying with sizeof(duk_tval) won't wrap.
     */
    DUK_ASSERT(count >= 0 && count <= (duk_idx_t) DUK_USE_VALSTACK_LIMIT);
    DUK_ASSERT((duk_size_t) count <= DUK_SIZE_MAX / sizeof(duk_tval));  /* no wrapping */

    tv_dst = duk_push_harray_with_size_outptr(thr, (duk_uint32_t) count);  /* XXX: uninitialized would be OK */
    DUK_ASSERT(count == 0 || tv_dst != NULL);
    DUK_ASSERT(!duk_is_bare_object(thr, -1));

    /* Copy value stack values directly to the array part without
     * any refcount updates: net refcount changes are zero.
     */
    tv_src = thr->valstack_top - count - 1;
    duk_memcpy_unsafe((void *) tv_dst, (const void *) tv_src, (size_t) count * sizeof(duk_tval));

    /* Overwrite result array to final value stack location and wipe
     * the rest; no refcount operations needed.
     */

    tv_dst = tv_src;  /* when count == 0, same as tv_src (OK) */
    tv_src = thr->valstack_top - 1;
    DUK_TVAL_SET_TVAL(tv_dst, tv_src);

    /* XXX: internal helper to wipe a value stack segment? */
    tv_curr = tv_dst + 1;
    tv_limit = thr->valstack_top;
    while (tv_curr != tv_limit) {
        /* Wipe policy: keep as 'undefined'. */
        DUK_TVAL_SET_UNDEFINED(tv_curr);
        tv_curr++;
    }
    thr->valstack_top = tv_dst + 1;
}

DUK_INTERNAL duk_idx_t duk_unpack_array_like(duk_hthread *thr, duk_idx_t idx) {
    duk_tval *tv;

    DUK_ASSERT_API_ENTRY(thr);

    tv = duk_require_tval(thr, idx);
    if (DUK_LIKELY(DUK_TVAL_IS_OBJECT(tv))) {
        duk_hobject *h;
        duk_uint32_t len;
        duk_uint32_t i;

        h = DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(h != NULL);
        DUK_UNREF(h);

#if defined(DUK_USE_ARRAY_FASTPATH)  /* close enough */
        if (DUK_LIKELY(DUK_HOBJECT_IS_ARRAY(h) &&
                       ((duk_harray *) h)->length <= DUK_HOBJECT_GET_ASIZE(h))) {
            duk_harray *h_arr;
            duk_tval *tv_src;
            duk_tval *tv_dst;

            h_arr = (duk_harray *) h;
            len = h_arr->length;
            if (DUK_UNLIKELY(len >= 0x80000000UL)) {
                goto fail_over_2g;
            }
            duk_require_stack(thr, (duk_idx_t) len);

            /* The potential allocation in duk_require_stack() may
             * run a finalizer which modifies the argArray so that
             * e.g. becomes sparse.  So, we need to recheck that the
             * array didn't change size and that there's still a
             * valid backing array part.
             *
             * XXX: alternatively, could prevent finalizers for the
             * duration.
             */
            if (DUK_UNLIKELY(len != h_arr->length ||
                             h_arr->length > DUK_HOBJECT_GET_ASIZE((duk_hobject *) h_arr))) {
                goto skip_fast;
            }

            /* Main fast path: arguments array is almost always
             * an actual array (though it might also be an arguments
             * object).
             */

            DUK_DDD(DUK_DDDPRINT("fast path for %ld elements", (long) h_arr->length));
            tv_src = DUK_HOBJECT_A_GET_BASE(thr->heap, h);
            tv_dst = thr->valstack_top;
            while (len-- > 0) {
                DUK_ASSERT(tv_dst < thr->valstack_end);
                if (DUK_UNLIKELY(DUK_TVAL_IS_UNUSED(tv_src))) {
                    /* Gaps are very unlikely.  Skip over them,
                     * without an ancestor lookup (technically
                     * not compliant).
                     */
                    DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv_dst));  /* valstack policy */
                } else {
                    DUK_TVAL_SET_TVAL(tv_dst, tv_src);
                    DUK_TVAL_INCREF(thr, tv_dst);
                }
                tv_src++;
                tv_dst++;
            }
            DUK_ASSERT(tv_dst <= thr->valstack_end);
            thr->valstack_top = tv_dst;
            return (duk_idx_t) h_arr->length;
        }
     skip_fast:
#endif  /* DUK_USE_ARRAY_FASTPATH */

        /* Slow path: actual lookups.  The initial 'length' lookup
         * decides the output length, regardless of side effects that
         * may resize or change the argArray while we read the
         * indices.
         */
        idx = duk_normalize_index(thr, idx);
        duk_get_prop_stridx(thr, idx, DUK_STRIDX_LENGTH);
        len = duk_to_uint32(thr, -1);  /* ToUint32() coercion required */
        if (DUK_UNLIKELY(len >= 0x80000000UL)) {
            goto fail_over_2g;
        }
        duk_pop_unsafe(thr);
        DUK_DDD(DUK_DDDPRINT("slow path for %ld elements", (long) len));

        duk_require_stack(thr, (duk_idx_t) len);
        for (i = 0; i < len; i++) {
            duk_get_prop_index(thr, idx, (duk_uarridx_t) i);
        }
        return (duk_idx_t) len;
    } else if (DUK_TVAL_IS_UNDEFINED(tv) || DUK_TVAL_IS_NULL(tv)) {
        return 0;
    }

    DUK_ERROR_TYPE_INVALID_ARGS(thr);
    DUK_WO_NORETURN(return 0;);

 fail_over_2g:
    DUK_ERROR_RANGE_INVALID_LENGTH(thr);
    DUK_WO_NORETURN(return 0;);
}

/*
 *  Error throwing
 */

#if defined(DUK_USE_GCC_PRAGMAS)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsuggest-attribute=noreturn"
#elif defined(DUK_USE_CLANG_PRAGMAS)
#pragma clang diagnostic push
#endif

DUK_EXTERNAL void duk_throw_raw(duk_hthread *thr) {
    duk_tval *tv_val;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
    DUK_ASSERT(thr->valstack_end >= thr->valstack_top);

    if (DUK_UNLIKELY(thr->valstack_top == thr->valstack_bottom)) {
        DUK_ERROR_TYPE_INVALID_ARGS(thr);
        DUK_WO_NORETURN(return;);
    }

    /* Errors are augmented when they are created, not when they are
     * thrown or re-thrown.  The current error handler, however, runs
     * just before an error is thrown.
     */

    /* Sync so that augmentation sees up-to-date activations, NULL
     * thr->ptr_curr_pc so that it's not used if side effects occur
     * in augmentation or longjmp handling.
     */
    duk_hthread_sync_and_null_currpc(thr);

#if defined(DUK_USE_AUGMENT_ERROR_THROW)
    DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (before throw augment)", (duk_tval *) duk_get_tval(thr, -1)));
    duk_err_augment_error_throw(thr);
#endif
    DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (after throw augment)", (duk_tval *) duk_get_tval(thr, -1)));

    tv_val = DUK_GET_TVAL_NEGIDX(thr, -1);
    duk_err_setup_ljstate1(thr, DUK_LJ_TYPE_THROW, tv_val);
#if defined(DUK_USE_DEBUGGER_SUPPORT)
    duk_err_check_debugger_integration(thr);
#endif

    /* thr->heap->lj.jmpbuf_ptr is checked by duk_err_longjmp() so we don't
     * need to check that here.  If the value is NULL, a fatal error occurs
     * because we can't return.
     */

    duk_err_longjmp(thr);
    DUK_UNREACHABLE();
}

DUK_EXTERNAL void duk_fatal_raw(duk_hthread *thr, const char *err_msg) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT(thr->heap->fatal_func != NULL);

    DUK_D(DUK_DPRINT("fatal error occurred: %s", err_msg ? err_msg : "NULL"));

    /* fatal_func should be noreturn, but noreturn declarations on function
     * pointers has a very spotty support apparently so it's not currently
     * done.
     */
    thr->heap->fatal_func(thr->heap->heap_udata, err_msg);

    /* If the fatal handler returns, all bets are off.  It'd be nice to
     * print something here but since we don't want to depend on stdio,
     * there's no way to do so portably.
     */
    DUK_D(DUK_DPRINT("fatal error handler returned, all bets are off!"));
    for (;;) {
        /* loop forever, don't return (function marked noreturn) */
    }
}

DUK_EXTERNAL void duk_error_va_raw(duk_hthread *thr, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, va_list ap) {
    DUK_ASSERT_API_ENTRY(thr);

    duk_push_error_object_va_raw(thr, err_code, filename, line, fmt, ap);
    (void) duk_throw(thr);
    DUK_WO_NORETURN(return;);
}

DUK_EXTERNAL void duk_error_raw(duk_hthread *thr, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, ...) {
    va_list ap;

    DUK_ASSERT_API_ENTRY(thr);

    va_start(ap, fmt);
    duk_push_error_object_va_raw(thr, err_code, filename, line, fmt, ap);
    va_end(ap);
    (void) duk_throw(thr);
    DUK_WO_NORETURN(return;);
}

#if defined(DUK_USE_GCC_PRAGMAS)
#pragma GCC diagnostic pop
#elif defined(DUK_USE_CLANG_PRAGMAS)
#pragma clang diagnostic pop
#endif

#if !defined(DUK_USE_VARIADIC_MACROS)
DUK_NORETURN(DUK_LOCAL_DECL void duk__throw_error_from_stash(duk_hthread *thr, duk_errcode_t err_code, const char *fmt, va_list ap));

DUK_LOCAL void duk__throw_error_from_stash(duk_hthread *thr, duk_errcode_t err_code, const char *fmt, va_list ap) {
    const char *filename;
    duk_int_t line;

    DUK_CTX_ASSERT_VALID(thr);

    filename = duk_api_global_filename;
    line = duk_api_global_line;
    duk_api_global_filename = NULL;
    duk_api_global_line = 0;

    duk_push_error_object_va_raw(thr, err_code, filename, line, fmt, ap);
    (void) duk_throw(thr);
    DUK_WO_NORETURN(return;);
}

#define DUK__ERROR_STASH_SHARED(code) do { \
        va_list ap; \
        va_start(ap, fmt); \
        duk__throw_error_from_stash(thr, (code), fmt, ap); \
        va_end(ap); \
        DUK_WO_NORETURN(return 0;); \
    } while (0)

DUK_EXTERNAL duk_ret_t duk_error_stash(duk_hthread *thr, duk_errcode_t err_code, const char *fmt, ...) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK__ERROR_STASH_SHARED(err_code);
}
DUK_EXTERNAL duk_ret_t duk_generic_error_stash(duk_hthread *thr, const char *fmt, ...) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK__ERROR_STASH_SHARED(DUK_ERR_ERROR);
}
DUK_EXTERNAL duk_ret_t duk_eval_error_stash(duk_hthread *thr, const char *fmt, ...) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK__ERROR_STASH_SHARED(DUK_ERR_EVAL_ERROR);
}
DUK_EXTERNAL duk_ret_t duk_range_error_stash(duk_hthread *thr, const char *fmt, ...) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK__ERROR_STASH_SHARED(DUK_ERR_RANGE_ERROR);
}
DUK_EXTERNAL duk_ret_t duk_reference_error_stash(duk_hthread *thr, const char *fmt, ...) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK__ERROR_STASH_SHARED(DUK_ERR_REFERENCE_ERROR);
}
DUK_EXTERNAL duk_ret_t duk_syntax_error_stash(duk_hthread *thr, const char *fmt, ...) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK__ERROR_STASH_SHARED(DUK_ERR_SYNTAX_ERROR);
}
DUK_EXTERNAL duk_ret_t duk_type_error_stash(duk_hthread *thr, const char *fmt, ...) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK__ERROR_STASH_SHARED(DUK_ERR_TYPE_ERROR);
}
DUK_EXTERNAL duk_ret_t duk_uri_error_stash(duk_hthread *thr, const char *fmt, ...) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK__ERROR_STASH_SHARED(DUK_ERR_URI_ERROR);
}
#endif  /* DUK_USE_VARIADIC_MACROS */

/*
 *  Comparison
 */

DUK_EXTERNAL duk_bool_t duk_equals(duk_hthread *thr, duk_idx_t idx1, duk_idx_t idx2) {
    duk_tval *tv1, *tv2;

    DUK_ASSERT_API_ENTRY(thr);

    tv1 = duk_get_tval(thr, idx1);
    tv2 = duk_get_tval(thr, idx2);
    if ((tv1 == NULL) || (tv2 == NULL)) {
        return 0;
    }

    /* Coercion may be needed, the helper handles that by pushing the
     * tagged values to the stack.
     */
    return duk_js_equals(thr, tv1, tv2);
}

DUK_EXTERNAL duk_bool_t duk_strict_equals(duk_hthread *thr, duk_idx_t idx1, duk_idx_t idx2) {
    duk_tval *tv1, *tv2;

    DUK_ASSERT_API_ENTRY(thr);

    tv1 = duk_get_tval(thr, idx1);
    tv2 = duk_get_tval(thr, idx2);
    if ((tv1 == NULL) || (tv2 == NULL)) {
        return 0;
    }

    /* No coercions or other side effects, so safe */
    return duk_js_strict_equals(tv1, tv2);
}

DUK_EXTERNAL duk_bool_t duk_samevalue(duk_hthread *thr, duk_idx_t idx1, duk_idx_t idx2) {
    duk_tval *tv1, *tv2;

    DUK_ASSERT_API_ENTRY(thr);

    tv1 = duk_get_tval(thr, idx1);
    tv2 = duk_get_tval(thr, idx2);
    if ((tv1 == NULL) || (tv2 == NULL)) {
        return 0;
    }

    /* No coercions or other side effects, so safe */
    return duk_js_samevalue(tv1, tv2);
}

/*
 *  instanceof
 */

DUK_EXTERNAL duk_bool_t duk_instanceof(duk_hthread *thr, duk_idx_t idx1, duk_idx_t idx2) {
    duk_tval *tv1, *tv2;

    DUK_ASSERT_API_ENTRY(thr);

    /* Index validation is strict, which differs from duk_equals().
     * The strict behavior mimics how instanceof itself works, e.g.
     * it is a TypeError if rval is not a -callable- object.  It would
     * be somewhat inconsistent if rval would be allowed to be
     * non-existent without a TypeError.
     */
    tv1 = duk_require_tval(thr, idx1);
    DUK_ASSERT(tv1 != NULL);
    tv2 = duk_require_tval(thr, idx2);
    DUK_ASSERT(tv2 != NULL);

    return duk_js_instanceof(thr, tv1, tv2);
}

/*
 *  Lightfunc
 */

DUK_INTERNAL void duk_push_lightfunc_name_raw(duk_hthread *thr, duk_c_function func, duk_small_uint_t lf_flags) {
    /* Lightfunc name, includes Duktape/C native function pointer, which
     * can often be used to locate the function from a symbol table.
     * The name also includes the 16-bit duk_tval flags field because it
     * includes the magic value.  Because a single native function often
     * provides different functionality depending on the magic value, it
     * seems reasonably to include it in the name.
     *
     * On the other hand, a complicated name increases string table
     * pressure in low memory environments (but only when function name
     * is accessed).
     */

    DUK_ASSERT_API_ENTRY(thr);

    duk_push_literal(thr, "light_");
    duk_push_string_funcptr(thr, (duk_uint8_t *) &func, sizeof(func));
    duk_push_sprintf(thr, "_%04x", (unsigned int) lf_flags);
    duk_concat(thr, 3);
}

DUK_INTERNAL void duk_push_lightfunc_name(duk_hthread *thr, duk_tval *tv) {
    duk_c_function func;
    duk_small_uint_t lf_flags;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv));

    DUK_TVAL_GET_LIGHTFUNC(tv, func, lf_flags);
    duk_push_lightfunc_name_raw(thr, func, lf_flags);
}

DUK_INTERNAL void duk_push_lightfunc_tostring(duk_hthread *thr, duk_tval *tv) {
    duk_c_function func;
    duk_small_uint_t lf_flags;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv));

    DUK_TVAL_GET_LIGHTFUNC(tv, func, lf_flags);  /* read before 'tv' potentially invalidated */
    duk_push_literal(thr, "function ");
    duk_push_lightfunc_name_raw(thr, func, lf_flags);
    duk_push_literal(thr, "() { [lightfunc code] }");
    duk_concat(thr, 3);
}

/*
 *  Function pointers
 *
 *  Printing function pointers is non-portable, so we do that by hex printing
 *  bytes from memory.
 */

DUK_INTERNAL void duk_push_string_funcptr(duk_hthread *thr, duk_uint8_t *ptr, duk_size_t sz) {
    duk_uint8_t buf[32 * 2];
    duk_uint8_t *p, *q;
    duk_small_uint_t i;
    duk_small_uint_t t;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(sz <= 32);  /* sanity limit for function pointer size */

    p = buf;
#if defined(DUK_USE_INTEGER_LE)
    q = ptr + sz;
#else
    q = ptr;
#endif
    for (i = 0; i < sz; i++) {
#if defined(DUK_USE_INTEGER_LE)
        t = *(--q);
#else
        t = *(q++);
#endif
        *p++ = duk_lc_digits[t >> 4];
        *p++ = duk_lc_digits[t & 0x0f];
    }

    duk_push_lstring(thr, (const char *) buf, sz * 2);
}

/*
 *  Push readable string summarizing duk_tval.  The operation is side effect
 *  free and will only throw from internal errors (e.g. out of memory).
 *  This is used by e.g. property access code to summarize a key/base safely,
 *  and is not intended to be fast (but small and safe).
 */

/* String limits for summary strings. */
#define DUK__READABLE_SUMMARY_MAXCHARS 96  /* maximum supported by helper */
#define DUK__READABLE_STRING_MAXCHARS  32  /* for strings/symbols */
#define DUK__READABLE_ERRMSG_MAXCHARS  96  /* for error messages */

/* String sanitizer which escapes ASCII control characters and a few other
 * ASCII characters, passes Unicode as is, and replaces invalid UTF-8 with
 * question marks.  No errors are thrown for any input string, except in out
 * of memory situations.
 */
DUK_LOCAL void duk__push_hstring_readable_unicode(duk_hthread *thr, duk_hstring *h_input, duk_small_uint_t maxchars) {
    const duk_uint8_t *p, *p_start, *p_end;
    duk_uint8_t buf[DUK_UNICODE_MAX_XUTF8_LENGTH * DUK__READABLE_SUMMARY_MAXCHARS +
                    2 /*quotes*/ + 3 /*periods*/];
    duk_uint8_t *q;
    duk_ucodepoint_t cp;
    duk_small_uint_t nchars;

    DUK_CTX_ASSERT_VALID(thr);
    DUK_ASSERT(h_input != NULL);
    DUK_ASSERT(maxchars <= DUK__READABLE_SUMMARY_MAXCHARS);

    p_start = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_input);
    p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_input);
    p = p_start;
    q = buf;

    nchars = 0;
    *q++ = (duk_uint8_t) DUK_ASC_SINGLEQUOTE;
    for (;;) {
        if (p >= p_end) {
            break;
        }
        if (nchars == maxchars) {
            *q++ = (duk_uint8_t) DUK_ASC_PERIOD;
            *q++ = (duk_uint8_t) DUK_ASC_PERIOD;
            *q++ = (duk_uint8_t) DUK_ASC_PERIOD;
            break;
        }
        if (duk_unicode_decode_xutf8(thr, &p, p_start, p_end, &cp)) {
            if (cp < 0x20 || cp == 0x7f || cp == DUK_ASC_SINGLEQUOTE || cp == DUK_ASC_BACKSLASH) {
                DUK_ASSERT(DUK_UNICODE_MAX_XUTF8_LENGTH >= 4);  /* estimate is valid */
                DUK_ASSERT((cp >> 4) <= 0x0f);
                *q++ = (duk_uint8_t) DUK_ASC_BACKSLASH;
                *q++ = (duk_uint8_t) DUK_ASC_LC_X;
                *q++ = (duk_uint8_t) duk_lc_digits[cp >> 4];
                *q++ = (duk_uint8_t) duk_lc_digits[cp & 0x0f];
            } else {
                q += duk_unicode_encode_xutf8(cp, q);
            }
        } else {
            p++;  /* advance manually */
            *q++ = (duk_uint8_t) DUK_ASC_QUESTION;
        }
        nchars++;
    }
    *q++ = (duk_uint8_t) DUK_ASC_SINGLEQUOTE;

    duk_push_lstring(thr, (const char *) buf, (duk_size_t) (q - buf));
}

DUK_LOCAL const char *duk__push_string_tval_readable(duk_hthread *thr, duk_tval *tv, duk_bool_t error_aware) {
    DUK_CTX_ASSERT_VALID(thr);
    /* 'tv' may be NULL */

    if (tv == NULL) {
        duk_push_literal(thr, "none");
    } else {
        switch (DUK_TVAL_GET_TAG(tv)) {
        case DUK_TAG_STRING: {
            duk_hstring *h = DUK_TVAL_GET_STRING(tv);
            if (DUK_HSTRING_HAS_SYMBOL(h)) {
                /* XXX: string summary produces question marks
                 * so this is not very ideal.
                 */
                duk_push_literal(thr, "[Symbol ");
                duk_push_string(thr, duk__get_symbol_type_string(h));
                duk_push_literal(thr, " ");
                duk__push_hstring_readable_unicode(thr, h, DUK__READABLE_STRING_MAXCHARS);
                duk_push_literal(thr, "]");
                duk_concat(thr, 5);
                break;
            }
            duk__push_hstring_readable_unicode(thr, h, DUK__READABLE_STRING_MAXCHARS);
            break;
        }
        case DUK_TAG_OBJECT: {
            duk_hobject *h = DUK_TVAL_GET_OBJECT(tv);
            DUK_ASSERT(h != NULL);

            if (error_aware &&
                duk_hobject_prototype_chain_contains(thr, h, thr->builtins[DUK_BIDX_ERROR_PROTOTYPE], 1 /*ignore_loop*/)) {
                /* Get error message in a side effect free way if
                 * possible; if not, summarize as a generic object.
                 * Error message currently gets quoted.
                 */
                /* XXX: better internal getprop call; get without side effects
                 * but traverse inheritance chain.
                 */
                duk_tval *tv_msg;
                tv_msg = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, h, DUK_STRIDX_MESSAGE);
                if (tv_msg != NULL && DUK_TVAL_IS_STRING(tv_msg)) {
                    /* It's critical to avoid recursion so
                     * only summarize a string .message.
                     */
                    duk__push_hstring_readable_unicode(thr, DUK_TVAL_GET_STRING(tv_msg), DUK__READABLE_ERRMSG_MAXCHARS);
                    break;
                }
            }
            duk_push_class_string_tval(thr, tv, 1 /*avoid_side_effects*/);
            break;
        }
        case DUK_TAG_BUFFER: {
            /* While plain buffers mimic Uint8Arrays, they summarize differently.
             * This is useful so that the summarized string accurately reflects the
             * internal type which may matter for figuring out bugs etc.
             */
            /* XXX: Hex encoded, length limited buffer summary here? */
            duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
            DUK_ASSERT(h != NULL);
            duk_push_sprintf(thr, "[buffer:%ld]", (long) DUK_HBUFFER_GET_SIZE(h));
            break;
        }
        case DUK_TAG_POINTER: {
            /* Surround with parentheses like in JX, ensures NULL pointer
             * is distinguishable from null value ("(null)" vs "null").
             */
            duk_push_tval(thr, tv);
            duk_push_sprintf(thr, "(%s)", duk_to_string(thr, -1));
            duk_remove_m2(thr);
            break;
        }
        default: {
            duk_push_tval(thr, tv);
            break;
        }
        }
    }

    return duk_to_string(thr, -1);
}
DUK_INTERNAL const char *duk_push_string_tval_readable(duk_hthread *thr, duk_tval *tv) {
    DUK_ASSERT_API_ENTRY(thr);
    return duk__push_string_tval_readable(thr, tv, 0 /*error_aware*/);
}

DUK_INTERNAL const char *duk_push_string_readable(duk_hthread *thr, duk_idx_t idx) {
    DUK_ASSERT_API_ENTRY(thr);
    return duk_push_string_tval_readable(thr, duk_get_tval(thr, idx));
}

DUK_INTERNAL const char *duk_push_string_tval_readable_error(duk_hthread *thr, duk_tval *tv) {
    DUK_ASSERT_API_ENTRY(thr);
    return duk__push_string_tval_readable(thr, tv, 1 /*error_aware*/);
}

DUK_INTERNAL void duk_push_symbol_descriptive_string(duk_hthread *thr, duk_hstring *h) {
    const duk_uint8_t *p;
    const duk_uint8_t *p_end;
    const duk_uint8_t *q;

    DUK_ASSERT_API_ENTRY(thr);

    /* .toString() */
    duk_push_literal(thr, "Symbol(");
    p = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h);
    p_end = p + DUK_HSTRING_GET_BYTELEN(h);
    DUK_ASSERT(p[0] == 0xff || (p[0] & 0xc0) == 0x80);
    p++;
    for (q = p; q < p_end; q++) {
        if (*q == 0xffU) {
            /* Terminate either at end-of-string (but NUL MUST
             * be accepted without terminating description) or
             * 0xFF, which is used to mark start of unique trailer
             * (and cannot occur in CESU-8 / extended UTF-8).
             */
            break;
        }
    }
    duk_push_lstring(thr, (const char *) p, (duk_size_t) (q - p));
    duk_push_literal(thr, ")");
    duk_concat(thr, 3);
}

/*
 *  Functions
 */

#if 0  /* not used yet */
DUK_INTERNAL void duk_push_hnatfunc_name(duk_hthread *thr, duk_hnatfunc *h) {
    duk_c_function func;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(h != NULL);
    DUK_ASSERT(DUK_HOBJECT_IS_NATFUNC((duk_hobject *) h));

    duk_push_sprintf(thr, "native_");
    func = h->func;
    duk_push_string_funcptr(thr, (duk_uint8_t *) &func, sizeof(func));
    duk_push_sprintf(thr, "_%04x_%04x",
                     (unsigned int) (duk_uint16_t) h->nargs,
                     (unsigned int) (duk_uint16_t) h->magic);
    duk_concat(thr, 3);
}
#endif

/*
 *  duk_tval slice copy
 */

DUK_INTERNAL void duk_copy_tvals_incref(duk_hthread *thr, duk_tval *tv_dst, duk_tval *tv_src, duk_size_t count) {
    duk_tval *tv;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_UNREF(thr);
    DUK_ASSERT(count * sizeof(duk_tval) >= count);  /* no wrap */

    duk_memcpy_unsafe((void *) tv_dst, (const void *) tv_src, count * sizeof(duk_tval));

    tv = tv_dst;
    while (count-- > 0) {
        DUK_TVAL_INCREF(thr, tv);
        tv++;
    }
}

/* automatic undefs */
#undef DUK__ASSERT_SPACE
#undef DUK__CHECK_SPACE
#undef DUK__ERROR_STASH_SHARED
#undef DUK__PACK_ARGS
#undef DUK__READABLE_ERRMSG_MAXCHARS
#undef DUK__READABLE_STRING_MAXCHARS
#undef DUK__READABLE_SUMMARY_MAXCHARS
//#line 1 "duk_api_string.c"
/*
 *  String manipulation
 */

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

DUK_LOCAL void duk__concat_and_join_helper(duk_hthread *thr, duk_idx_t count_in, duk_bool_t is_join) {
    duk_uint_t count;
    duk_uint_t i;
    duk_size_t idx;
    duk_size_t len;
    duk_hstring *h;
    duk_uint8_t *buf;

    DUK_CTX_ASSERT_VALID(thr);

    if (DUK_UNLIKELY(count_in <= 0)) {
        if (count_in < 0) {
            DUK_ERROR_RANGE_INVALID_COUNT(thr);
            DUK_WO_NORETURN(return;);
        }
        DUK_ASSERT(count_in == 0);
        duk_push_hstring_empty(thr);
        return;
    }
    count = (duk_uint_t) count_in;

    if (is_join) {
        duk_size_t t1, t2, limit;
        h = duk_to_hstring(thr, -((duk_idx_t) count) - 1);
        DUK_ASSERT(h != NULL);

        /* A bit tricky overflow test, see doc/code-issues.rst. */
        t1 = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h);
        t2 = (duk_size_t) (count - 1);
        limit = (duk_size_t) DUK_HSTRING_MAX_BYTELEN;
        if (DUK_UNLIKELY(t2 != 0 && t1 > limit / t2)) {
            /* Combined size of separators already overflows. */
            goto error_overflow;
        }
        len = (duk_size_t) (t1 * t2);
    } else {
        len = (duk_size_t) 0;
    }

    for (i = count; i >= 1; i--) {
        duk_size_t new_len;
        h = duk_to_hstring(thr, -((duk_idx_t) i));
        new_len = len + (duk_size_t) DUK_HSTRING_GET_BYTELEN(h);

        /* Impose a string maximum length, need to handle overflow
         * correctly.
         */
        if (new_len < len ||  /* wrapped */
            new_len > (duk_size_t) DUK_HSTRING_MAX_BYTELEN) {
            goto error_overflow;
        }
        len = new_len;
    }

    DUK_DDD(DUK_DDDPRINT("join/concat %lu strings, total length %lu bytes",
                         (unsigned long) count, (unsigned long) len));

    /* Use stack allocated buffer to ensure reachability in errors
     * (e.g. intern error).
     */
    buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, len);
    DUK_ASSERT(buf != NULL);

    /* [ ... (sep) str1 str2 ... strN buf ] */

    idx = 0;
    for (i = count; i >= 1; i--) {
        if (is_join && i != count) {
            h = duk_require_hstring(thr, -((duk_idx_t) count) - 2);  /* extra -1 for buffer */
            duk_memcpy(buf + idx, DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h));
            idx += DUK_HSTRING_GET_BYTELEN(h);
        }
        h = duk_require_hstring(thr, -((duk_idx_t) i) - 1);  /* extra -1 for buffer */
        duk_memcpy(buf + idx, DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h));
        idx += DUK_HSTRING_GET_BYTELEN(h);
    }

    DUK_ASSERT(idx == len);

    /* [ ... (sep) str1 str2 ... strN buf ] */

    /* Get rid of the strings early to minimize memory use before intern. */

    if (is_join) {
        duk_replace(thr, -((duk_idx_t) count) - 2);  /* overwrite sep */
        duk_pop_n(thr, (duk_idx_t) count);
    } else {
        duk_replace(thr, -((duk_idx_t) count) - 1);  /* overwrite str1 */
        duk_pop_n(thr, (duk_idx_t) (count - 1));
    }

    /* [ ... buf ] */

    (void) duk_buffer_to_string(thr, -1);  /* Safe if inputs are safe. */

    /* [ ... res ] */
    return;

 error_overflow:
    DUK_ERROR_RANGE(thr, DUK_STR_RESULT_TOO_LONG);
    DUK_WO_NORETURN(return;);
}

DUK_EXTERNAL void duk_concat(duk_hthread *thr, duk_idx_t count) {
    DUK_ASSERT_API_ENTRY(thr);

    duk__concat_and_join_helper(thr, count, 0 /*is_join*/);
}

#if defined(DUK_USE_PREFER_SIZE)
DUK_INTERNAL void duk_concat_2(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    duk_concat(thr, 2);
}
#else  /* DUK_USE_PREFER_SIZE */
DUK_INTERNAL void duk_concat_2(duk_hthread *thr) {
    duk_hstring *h1;
    duk_hstring *h2;
    duk_uint8_t *buf;
    duk_size_t len1;
    duk_size_t len2;
    duk_size_t len;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(duk_get_top(thr) >= 2);  /* Trusted caller. */

    h1 = duk_to_hstring(thr, -2);
    h2 = duk_to_hstring(thr, -1);
    len1 = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h1);
    len2 = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h2);
    len = len1 + len2;
    if (DUK_UNLIKELY(len < len1 ||  /* wrapped */
                     len > (duk_size_t) DUK_HSTRING_MAX_BYTELEN)) {
        goto error_overflow;
    }
    buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, len);
    DUK_ASSERT(buf != NULL);

    duk_memcpy((void *) buf, (const void *) DUK_HSTRING_GET_DATA(h1), (size_t) len1);
    duk_memcpy((void *) (buf + len1), (const void *) DUK_HSTRING_GET_DATA(h2), (size_t) len2);
    (void) duk_buffer_to_string(thr, -1);  /* Safe if inputs are safe. */

    /* [ ... str1 str2 buf ] */

    duk_replace(thr, -3);
    duk_pop_unsafe(thr);
    return;

 error_overflow:
    DUK_ERROR_RANGE(thr, DUK_STR_RESULT_TOO_LONG);
    DUK_WO_NORETURN(return;);
}
#endif  /* DUK_USE_PREFER_SIZE */

DUK_EXTERNAL void duk_join(duk_hthread *thr, duk_idx_t count) {
    DUK_ASSERT_API_ENTRY(thr);

    duk__concat_and_join_helper(thr, count, 1 /*is_join*/);
}

/* XXX: could map/decode be unified with duk_unicode_support.c code?
 * Case conversion needs also the character surroundings though.
 */

DUK_EXTERNAL void duk_decode_string(duk_hthread *thr, duk_idx_t idx, duk_decode_char_function callback, void *udata) {
    duk_hstring *h_input;
    const duk_uint8_t *p, *p_start, *p_end;
    duk_codepoint_t cp;

    DUK_ASSERT_API_ENTRY(thr);

    h_input = duk_require_hstring(thr, idx);  /* Accept symbols. */
    DUK_ASSERT(h_input != NULL);

    p_start = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_input);
    p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_input);
    p = p_start;

    for (;;) {
        if (p >= p_end) {
            break;
        }
        cp = (duk_codepoint_t) duk_unicode_decode_xutf8_checked(thr, &p, p_start, p_end);
        callback(udata, cp);
    }
}

DUK_EXTERNAL void duk_map_string(duk_hthread *thr, duk_idx_t idx, duk_map_char_function callback, void *udata) {
    duk_hstring *h_input;
    duk_bufwriter_ctx bw_alloc;
    duk_bufwriter_ctx *bw;
    const duk_uint8_t *p, *p_start, *p_end;
    duk_codepoint_t cp;

    DUK_ASSERT_API_ENTRY(thr);

    idx = duk_normalize_index(thr, idx);

    h_input = duk_require_hstring(thr, idx);  /* Accept symbols. */
    DUK_ASSERT(h_input != NULL);

    bw = &bw_alloc;
    DUK_BW_INIT_PUSHBUF(thr, bw, DUK_HSTRING_GET_BYTELEN(h_input));  /* Reasonable output estimate. */

    p_start = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_input);
    p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_input);
    p = p_start;

    for (;;) {
        /* XXX: could write output in chunks with fewer ensure calls,
         * but relative benefit would be small here.
         */

        if (p >= p_end) {
            break;
        }
        cp = (duk_codepoint_t) duk_unicode_decode_xutf8_checked(thr, &p, p_start, p_end);
        cp = callback(udata, cp);

        DUK_BW_WRITE_ENSURE_XUTF8(thr, bw, cp);
    }

    DUK_BW_COMPACT(thr, bw);
    (void) duk_buffer_to_string(thr, -1);  /* Safe, extended UTF-8 encoded. */
    duk_replace(thr, idx);
}

DUK_EXTERNAL void duk_substring(duk_hthread *thr, duk_idx_t idx, duk_size_t start_offset, duk_size_t end_offset) {
    duk_hstring *h;
    duk_hstring *res;
    duk_size_t start_byte_offset;
    duk_size_t end_byte_offset;
    duk_size_t charlen;

    DUK_ASSERT_API_ENTRY(thr);

    idx = duk_require_normalize_index(thr, idx);  /* Accept symbols. */
    h = duk_require_hstring(thr, idx);
    DUK_ASSERT(h != NULL);

    charlen = DUK_HSTRING_GET_CHARLEN(h);
    if (end_offset >= charlen) {
        end_offset = charlen;
    }
    if (start_offset > end_offset) {
        start_offset = end_offset;
    }

    DUK_ASSERT_DISABLE(start_offset >= 0);
    DUK_ASSERT(start_offset <= end_offset && start_offset <= DUK_HSTRING_GET_CHARLEN(h));
    DUK_ASSERT_DISABLE(end_offset >= 0);
    DUK_ASSERT(end_offset >= start_offset && end_offset <= DUK_HSTRING_GET_CHARLEN(h));

    /* Guaranteed by string limits. */
    DUK_ASSERT(start_offset <= DUK_UINT32_MAX);
    DUK_ASSERT(end_offset <= DUK_UINT32_MAX);

    start_byte_offset = (duk_size_t) duk_heap_strcache_offset_char2byte(thr, h, (duk_uint_fast32_t) start_offset);
    end_byte_offset = (duk_size_t) duk_heap_strcache_offset_char2byte(thr, h, (duk_uint_fast32_t) end_offset);

    DUK_ASSERT(end_byte_offset >= start_byte_offset);
    DUK_ASSERT(end_byte_offset - start_byte_offset <= DUK_UINT32_MAX);  /* Guaranteed by string limits. */

    /* No size check is necessary. */
    res = duk_heap_strtable_intern_checked(thr,
                                           DUK_HSTRING_GET_DATA(h) + start_byte_offset,
                                           (duk_uint32_t) (end_byte_offset - start_byte_offset));

    duk_push_hstring(thr, res);
    duk_replace(thr, idx);
}

/* XXX: this is quite clunky.  Add Unicode helpers to scan backwards and
 * forwards with a callback to process codepoints?
 */
DUK_EXTERNAL void duk_trim(duk_hthread *thr, duk_idx_t idx) {
    duk_hstring *h;
    const duk_uint8_t *p, *p_start, *p_end, *p_tmp1, *p_tmp2;  /* pointers for scanning */
    const duk_uint8_t *q_start, *q_end;  /* start (incl) and end (excl) of trimmed part */
    duk_codepoint_t cp;

    DUK_ASSERT_API_ENTRY(thr);

    idx = duk_require_normalize_index(thr, idx);  /* Accept symbols. */
    h = duk_require_hstring(thr, idx);
    DUK_ASSERT(h != NULL);

    p_start = DUK_HSTRING_GET_DATA(h);
    p_end = p_start + DUK_HSTRING_GET_BYTELEN(h);

    p = p_start;
    while (p < p_end) {
        p_tmp1 = p;
        cp = (duk_codepoint_t) duk_unicode_decode_xutf8_checked(thr, &p_tmp1, p_start, p_end);
        if (!(duk_unicode_is_whitespace(cp) || duk_unicode_is_line_terminator(cp))) {
            break;
        }
        p = p_tmp1;
    }
    q_start = p;
    if (p == p_end) {
        /* Entire string is whitespace. */
        q_end = p;
        goto scan_done;
    }

    p = p_end;
    while (p > p_start) {
        p_tmp1 = p;
        while (p > p_start) {
            p--;
            if (((*p) & 0xc0) != 0x80) {
                break;
            }
        }
        p_tmp2 = p;

        cp = (duk_codepoint_t) duk_unicode_decode_xutf8_checked(thr, &p_tmp2, p_start, p_end);
        if (!(duk_unicode_is_whitespace(cp) || duk_unicode_is_line_terminator(cp))) {
            p = p_tmp1;
            break;
        }
    }
    q_end = p;

 scan_done:
    /* This may happen when forward and backward scanning disagree
     * (possible for non-extended-UTF-8 strings).
     */
    if (q_end < q_start) {
        q_end = q_start;
    }

    DUK_ASSERT(q_start >= p_start && q_start <= p_end);
    DUK_ASSERT(q_end >= p_start && q_end <= p_end);
    DUK_ASSERT(q_end >= q_start);

    DUK_DDD(DUK_DDDPRINT("trim: p_start=%p, p_end=%p, q_start=%p, q_end=%p",
                         (const void *) p_start, (const void *) p_end,
                         (const void *) q_start, (const void *) q_end));

    if (q_start == p_start && q_end == p_end) {
        DUK_DDD(DUK_DDDPRINT("nothing was trimmed: avoid interning (hashing etc)"));
        return;
    }

    duk_push_lstring(thr, (const char *) q_start, (duk_size_t) (q_end - q_start));
    duk_replace(thr, idx);
}

DUK_EXTERNAL duk_codepoint_t duk_char_code_at(duk_hthread *thr, duk_idx_t idx, duk_size_t char_offset) {
    duk_hstring *h;
    duk_ucodepoint_t cp;

    DUK_ASSERT_API_ENTRY(thr);

    /* XXX: Share code with String.prototype.charCodeAt?  Main difference
     * is handling of clamped offsets.
     */

    h = duk_require_hstring(thr, idx);  /* Accept symbols. */
    DUK_ASSERT(h != NULL);

    DUK_ASSERT_DISABLE(char_offset >= 0);  /* Always true, arg is unsigned. */
    if (char_offset >= DUK_HSTRING_GET_CHARLEN(h)) {
        return 0;
    }

    DUK_ASSERT(char_offset <= DUK_UINT_MAX);  /* Guaranteed by string limits. */
    cp = duk_hstring_char_code_at_raw(thr, h, (duk_uint_t) char_offset, 0 /*surrogate_aware*/);
    return (duk_codepoint_t) cp;
}
//#line 1 "duk_api_time.c"
/*
 *  Date/time.
 */

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

DUK_INTERNAL duk_double_t duk_time_get_ecmascript_time(duk_hthread *thr) {
    /* ECMAScript time, with millisecond fractions.  Exposed via
     * duk_get_now() for example.
     */
    DUK_UNREF(thr);
    return (duk_double_t) DUK_USE_DATE_GET_NOW(thr);
}

DUK_INTERNAL duk_double_t duk_time_get_ecmascript_time_nofrac(duk_hthread *thr) {
    /* ECMAScript time without millisecond fractions.  Exposed via
     * the Date built-in which doesn't allow fractions.
     */
    DUK_UNREF(thr);
    return (duk_double_t) DUK_FLOOR(DUK_USE_DATE_GET_NOW(thr));
}

DUK_INTERNAL duk_double_t duk_time_get_monotonic_time(duk_hthread *thr) {
    DUK_UNREF(thr);
#if defined(DUK_USE_GET_MONOTONIC_TIME)
    return (duk_double_t) DUK_USE_GET_MONOTONIC_TIME(thr);
#else
    return (duk_double_t) DUK_USE_DATE_GET_NOW(thr);
#endif
}

DUK_EXTERNAL duk_double_t duk_get_now(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_UNREF(thr);

    /* This API intentionally allows millisecond fractions. */
    return duk_time_get_ecmascript_time(thr);
}

#if 0  /* XXX: worth exposing? */
DUK_EXTERNAL duk_double_t duk_get_monotonic_time(duk_hthread *thr) {
    DUK_ASSERT_API_ENTRY(thr);
    DUK_UNREF(thr);

    return duk_time_get_monotonic_time(thr);
}
#endif

DUK_EXTERNAL void duk_time_to_components(duk_hthread *thr, duk_double_t timeval, duk_time_components *comp) {
    duk_int_t parts[DUK_DATE_IDX_NUM_PARTS];
    duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS];
    duk_uint_t flags;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(comp != NULL);  /* XXX: or check? */
    DUK_UNREF(thr);

    /* Convert as one-based, but change month to zero-based to match the
     * ECMAScript Date built-in behavior 1:1.
     */
    flags = DUK_DATE_FLAG_ONEBASED | DUK_DATE_FLAG_NAN_TO_ZERO;

    duk_bi_date_timeval_to_parts(timeval, parts, dparts, flags);

    /* XXX: sub-millisecond accuracy for the API */

    DUK_ASSERT(dparts[DUK_DATE_IDX_MONTH] >= 1.0 && dparts[DUK_DATE_IDX_MONTH] <= 12.0);
    comp->year = dparts[DUK_DATE_IDX_YEAR];
    comp->month = dparts[DUK_DATE_IDX_MONTH] - 1.0;
    comp->day = dparts[DUK_DATE_IDX_DAY];
    comp->hours = dparts[DUK_DATE_IDX_HOUR];
    comp->minutes = dparts[DUK_DATE_IDX_MINUTE];
    comp->seconds = dparts[DUK_DATE_IDX_SECOND];
    comp->milliseconds = dparts[DUK_DATE_IDX_MILLISECOND];
    comp->weekday = dparts[DUK_DATE_IDX_WEEKDAY];
}

DUK_EXTERNAL duk_double_t duk_components_to_time(duk_hthread *thr, duk_time_components *comp) {
    duk_double_t d;
    duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS];
    duk_uint_t flags;

    DUK_ASSERT_API_ENTRY(thr);
    DUK_ASSERT(comp != NULL);  /* XXX: or check? */
    DUK_UNREF(thr);

    /* Match Date constructor behavior (with UTC time).  Month is given
     * as zero-based.  Day-of-month is given as one-based so normalize
     * it to zero-based as the internal conversion helpers expects all
     * components to be zero-based.
     */
    flags = 0;

    /* XXX: expensive conversion; use array format in API instead, or unify
     * time provider and time API to use same struct?
     */

    dparts[DUK_DATE_IDX_YEAR] = comp->year;
    dparts[DUK_DATE_IDX_MONTH] = comp->month;
    dparts[DUK_DATE_IDX_DAY] = comp->day - 1.0;
    dparts[DUK_DATE_IDX_HOUR] = comp->hours;
    dparts[DUK_DATE_IDX_MINUTE] = comp->minutes;
    dparts[DUK_DATE_IDX_SECOND] = comp->seconds;
    dparts[DUK_DATE_IDX_MILLISECOND] = comp->milliseconds;
    dparts[DUK_DATE_IDX_WEEKDAY] = 0;  /* ignored */

    d = duk_bi_date_get_timeval_from_dparts(dparts, flags);

    return d;
}
//#line 1 "duk_bi_array.c"
/*
 *  Array built-ins
 *
 *  Most Array built-ins are intentionally generic in ECMAScript, and are
 *  intended to work even when the 'this' binding is not an Array instance.
 *  This ECMAScript feature is also used by much real world code.  For this
 *  reason the implementations here don't assume exotic Array behavior or
 *  e.g. presence of a .length property.  However, some algorithms have a
 *  fast path for duk_harray backed actual Array instances, enabled when
 *  footprint is not a concern.
 *
 *  XXX: the "Throw" flag should be set for (almost?) all [[Put]] and
 *  [[Delete]] operations, but it's currently false throughout.  Go through
 *  all put/delete cases and check throw flag use.  Need a new API primitive
 *  which allows throws flag to be specified.
 *
 *  XXX: array lengths above 2G won't work reliably.  There are many places
 *  where one needs a full signed 32-bit range ([-0xffffffff, 0xffffffff],
 *  i.e. -33- bits).  Although array 'length' cannot be written to be outside
 *  the unsigned 32-bit range (E5.1 Section 15.4.5.1 throws a RangeError if so)
 *  some intermediate values may be above 0xffffffff and this may not be always
 *  correctly handled now (duk_uint32_t is not enough for all algorithms).
 *  For instance, push() can legitimately write entries beyond length 0xffffffff
 *  and cause a RangeError only at the end.  To do this properly, the current
 *  push() implementation tracks the array index using a 'double' instead of a
 *  duk_uint32_t (which is somewhat awkward).  See test-bi-array-push-maxlen.js.
 *
 *  On using "put" vs. "def" prop
 *  =============================
 *
 *  Code below must be careful to use the appropriate primitive as it matters
 *  for compliance.  When using "put" there may be inherited properties in
 *  Array.prototype which cause side effects when values are written.  When
 *  using "define" there are no such side effects, and many test262 test cases
 *  check for this (for real world code, such side effects are very rare).
 *  Both "put" and "define" are used in the E5.1 specification; as a rule,
 *  "put" is used when modifying an existing array (or a non-array 'this'
 *  binding) and "define" for setting values into a fresh result array.
 */

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

/* Perform an intermediate join when this many elements have been pushed
 * on the value stack.
 */
#define  DUK__ARRAY_MID_JOIN_LIMIT  4096

#if defined(DUK_USE_ARRAY_BUILTIN)

/*
 *  Shared helpers.
 */

/* Shared entry code for many Array built-ins: the 'this' binding is pushed
 * on the value stack and object coerced, and the current .length is returned.
 * Note that length is left on stack (it could be popped, but that's not
 * usually necessary because call handling will clean it up automatically).
 */
DUK_LOCAL duk_uint32_t duk__push_this_obj_len_u32(duk_hthread *thr) {
    duk_uint32_t len;

    /* XXX: push more directly? */
    (void) duk_push_this_coercible_to_object(thr);
    DUK_HOBJECT_ASSERT_VALID(duk_get_hobject(thr, -1));
    duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_LENGTH);
    len = duk_to_uint32(thr, -1);

    /* -> [ ... ToObject(this) ToUint32(length) ] */
    return len;
}

DUK_LOCAL duk_uint32_t duk__push_this_obj_len_u32_limited(duk_hthread *thr) {
    /* Range limited to [0, 0x7fffffff] range, i.e. range that can be
     * represented with duk_int32_t.  Use this when the method doesn't
     * handle the full 32-bit unsigned range correctly.
     */
    duk_uint32_t ret = duk__push_this_obj_len_u32(thr);
    if (DUK_UNLIKELY(ret >= 0x80000000UL)) {
        DUK_ERROR_RANGE_INVALID_LENGTH(thr);
        DUK_WO_NORETURN(return 0U;);
    }
    return ret;
}

#if defined(DUK_USE_ARRAY_FASTPATH)
/* Check if 'this' binding is an Array instance (duk_harray) which satisfies
 * a few other guarantees for fast path operation.  The fast path doesn't
 * need to handle all operations, even for duk_harrays, but must handle a
 * significant fraction to improve performance.  Return a non-NULL duk_harray
 * pointer when all fast path criteria are met, NULL otherwise.
 */
DUK_LOCAL duk_harray *duk__arraypart_fastpath_this(duk_hthread *thr) {
    duk_tval *tv;
    duk_hobject *h;
    duk_uint_t flags_mask, flags_bits, flags_value;

    DUK_ASSERT(thr->valstack_bottom > thr->valstack);  /* because call in progress */
    tv = DUK_GET_THIS_TVAL_PTR(thr);

    /* Fast path requires that 'this' is a duk_harray.  Read only arrays
     * (ROM backed) are also rejected for simplicity.
     */
    if (!DUK_TVAL_IS_OBJECT(tv)) {
        DUK_DD(DUK_DDPRINT("reject array fast path: not an object"));
        return NULL;
    }
    h = DUK_TVAL_GET_OBJECT(tv);
    DUK_ASSERT(h != NULL);
    flags_mask = DUK_HOBJECT_FLAG_ARRAY_PART | \
                 DUK_HOBJECT_FLAG_EXOTIC_ARRAY | \
                 DUK_HEAPHDR_FLAG_READONLY;
    flags_bits = DUK_HOBJECT_FLAG_ARRAY_PART | \
                 DUK_HOBJECT_FLAG_EXOTIC_ARRAY;
    flags_value = DUK_HEAPHDR_GET_FLAGS_RAW((duk_heaphdr *) h);
    if ((flags_value & flags_mask) != flags_bits) {
        DUK_DD(DUK_DDPRINT("reject array fast path: object flag check failed"));
        return NULL;
    }

    /* In some cases a duk_harray's 'length' may be larger than the
     * current array part allocation.  Avoid the fast path in these
     * cases, so that all fast path code can safely assume that all
     * items in the range [0,length[ are backed by the current array
     * part allocation.
     */
    if (((duk_harray *) h)->length > DUK_HOBJECT_GET_ASIZE(h)) {
        DUK_DD(DUK_DDPRINT("reject array fast path: length > array part size"));
        return NULL;
    }

    /* Guarantees for fast path. */
    DUK_ASSERT(h != NULL);
    DUK_ASSERT(DUK_HOBJECT_GET_ASIZE(h) == 0 || DUK_HOBJECT_A_GET_BASE(thr->heap, h) != NULL);
    DUK_ASSERT(((duk_harray *) h)->length <= DUK_HOBJECT_GET_ASIZE(h));

    DUK_DD(DUK_DDPRINT("array fast path allowed for: %!O", (duk_heaphdr *) h));
    return (duk_harray *) h;
}
#endif  /* DUK_USE_ARRAY_FASTPATH */

/*
 *  Constructor
 */

DUK_INTERNAL duk_ret_t duk_bi_array_constructor(duk_hthread *thr) {
    duk_idx_t nargs;
    duk_harray *a;
    duk_double_t d;
    duk_uint32_t len;
    duk_uint32_t len_prealloc;

    nargs = duk_get_top(thr);

    if (nargs == 1 && duk_is_number(thr, 0)) {
        /* XXX: expensive check (also shared elsewhere - so add a shared internal API call?) */
        d = duk_get_number(thr, 0);
        len = duk_to_uint32(thr, 0);
        if (!duk_double_equals((duk_double_t) len, d)) {
            DUK_DCERROR_RANGE_INVALID_LENGTH(thr);
        }

        /* For small lengths create a dense preallocated array.
         * For large arrays preallocate an initial part.
         */
        len_prealloc = len < 64 ? len : 64;
        a = duk_push_harray_with_size(thr, len_prealloc);
        DUK_ASSERT(a != NULL);
        DUK_ASSERT(!duk_is_bare_object(thr, -1));
        a->length = len;
        return 1;
    }

    duk_pack(thr, nargs);
    return 1;
}

/*
 *  isArray()
 */

DUK_INTERNAL duk_ret_t duk_bi_array_constructor_is_array(duk_hthread *thr) {
    DUK_ASSERT_TOP(thr, 1);
    duk_push_boolean(thr, duk_js_isarray(DUK_GET_TVAL_POSIDX(thr, 0)));
    return 1;
}

/*
 *  toString()
 */

DUK_INTERNAL duk_ret_t duk_bi_array_prototype_to_string(duk_hthread *thr) {
    (void) duk_push_this_coercible_to_object(thr);
    duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_JOIN);

    /* [ ... this func ] */
    if (!duk_is_callable(thr, -1)) {
        /* Fall back to the initial (original) Object.toString().  We don't
         * currently have pointers to the built-in functions, only the top
         * level global objects (like "Array") so this is now done in a bit
         * of a hacky manner.  It would be cleaner to push the (original)
         * function and use duk_call_method().
         */

        /* XXX: 'this' will be ToObject() coerced twice, which is incorrect
         * but should have no visible side effects.
         */
        DUK_DDD(DUK_DDDPRINT("this.join is not callable, fall back to (original) Object.toString"));
        duk_set_top(thr, 0);
        return duk_bi_object_prototype_to_string(thr);  /* has access to 'this' binding */
    }

    /* [ ... this func ] */

    duk_insert(thr, -2);

    /* [ ... func this ] */

    DUK_DDD(DUK_DDDPRINT("calling: func=%!iT, this=%!iT",
                         (duk_tval *) duk_get_tval(thr, -2),
                         (duk_tval *) duk_get_tval(thr, -1)));
    duk_call_method(thr, 0);

    return 1;
}

/*
 *  concat()
 */

DUK_INTERNAL duk_ret_t duk_bi_array_prototype_concat(duk_hthread *thr) {
    duk_idx_t i, n;
    duk_uint32_t j, idx, len;
    duk_hobject *h;
    duk_size_t tmp_len;

    /* XXX: In ES2015 Array .length can be up to 2^53-1.  The current
     * implementation is limited to 2^32-1.
     */

    /* XXX: Fast path for array 'this' and array element. */

    /* XXX: The insert here is a bit expensive if there are a lot of items.
     * It could also be special cased in the outermost for loop quite easily
     * (as the element is dup()'d anyway).
     */

    (void) duk_push_this_coercible_to_object(thr);
    duk_insert(thr, 0);
    n = duk_get_top(thr);
    duk_push_array(thr);  /* -> [ ToObject(this) item1 ... itemN arr ] */

    /* NOTE: The Array special behaviors are NOT invoked by duk_xdef_prop_index()
     * (which differs from the official algorithm).  If no error is thrown, this
     * doesn't matter as the length is updated at the end.  However, if an error
     * is thrown, the length will be unset.  That shouldn't matter because the
     * caller won't get a reference to the intermediate value.
     */

    idx = 0;
    for (i = 0; i < n; i++) {
        duk_bool_t spreadable;
        duk_bool_t need_has_check;

        DUK_ASSERT_TOP(thr, n + 1);

        /* [ ToObject(this) item1 ... itemN arr ] */

        h = duk_get_hobject(thr, i);

        if (h == NULL) {
            spreadable = 0;
        } else {
#if defined(DUK_USE_SYMBOL_BUILTIN)
            duk_get_prop_stridx(thr, i, DUK_STRIDX_WELLKNOWN_SYMBOL_IS_CONCAT_SPREADABLE);
            if (duk_is_undefined(thr, -1)) {
                spreadable = duk_js_isarray_hobject(h);
            } else {
                spreadable = duk_to_boolean(thr, -1);
            }
            duk_pop_nodecref_unsafe(thr);
#else
            spreadable = duk_js_isarray_hobject(h);
#endif
        }

        if (!spreadable) {
            duk_dup(thr, i);
            duk_xdef_prop_index_wec(thr, -2, idx);
            idx++;
            if (DUK_UNLIKELY(idx == 0U)) {
                /* Index after update is 0, and index written
                 * was 0xffffffffUL which is no longer a valid
                 * array index.
                 */
                goto fail_wrap;
            }
            continue;
        }

        DUK_ASSERT(duk_is_object(thr, i));
        need_has_check = (DUK_HOBJECT_IS_PROXY(h) != 0);  /* Always 0 w/o Proxy support. */

        /* [ ToObject(this) item1 ... itemN arr ] */

        tmp_len = duk_get_length(thr, i);
        len = (duk_uint32_t) tmp_len;
        if (DUK_UNLIKELY(tmp_len != (duk_size_t) len)) {
            goto fail_wrap;
        }
        if (DUK_UNLIKELY(idx + len < idx)) {
            /* Result length must be at most 0xffffffffUL to be
             * a valid 32-bit array index.
             */
            goto fail_wrap;
        }
        for (j = 0; j < len; j++) {
            /* For a Proxy element, an explicit 'has' check is
             * needed to allow the Proxy to present gaps.
             */
            if (need_has_check) {
                if (duk_has_prop_index(thr, i, j)) {
                    duk_get_prop_index(thr, i, j);
                    duk_xdef_prop_index_wec(thr, -2, idx);
                }
            } else {
                if (duk_get_prop_index(thr, i, j)) {
                    duk_xdef_prop_index_wec(thr, -2, idx);
                } else {
                    duk_pop_undefined(thr);
                }
            }
            idx++;
            DUK_ASSERT(idx != 0U);  /* Wrap check above. */
        }
    }

    /* ES5.1 has a specification "bug" in that nonexistent trailing
     * elements don't affect the result .length.  Test262 and other
     * engines disagree, and the specification bug was fixed in ES2015
     * (see NOTE 1 in https://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.concat).
     */
    duk_push_uarridx(thr, idx);
    duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);

    DUK_ASSERT_TOP(thr, n + 1);
    return 1;

 fail_wrap:
    DUK_ERROR_RANGE_INVALID_LENGTH(thr);
    DUK_WO_NORETURN(return 0;);
}

/*
 *  join(), toLocaleString()
 *
 *  Note: checking valstack is necessary, but only in the per-element loop.
 *
 *  Note: the trivial approach of pushing all the elements on the value stack
 *  and then calling duk_join() fails when the array contains a large number
 *  of elements.  This problem can't be offloaded to duk_join() because the
 *  elements to join must be handled here and have special handling.  Current
 *  approach is to do intermediate joins with very large number of elements.
 *  There is no fancy handling; the prefix gets re-joined multiple times.
 */

DUK_INTERNAL duk_ret_t duk_bi_array_prototype_join_shared(duk_hthread *thr) {
    duk_uint32_t len, count;
    duk_uint32_t idx;
    duk_small_int_t to_locale_string = duk_get_current_magic(thr);
    duk_idx_t valstack_required;

    /* For join(), nargs is 1.  For toLocaleString(), nargs is 0 and
     * setting the top essentially pushes an undefined to the stack,
     * thus defaulting to a comma separator.
     */
    duk_set_top(thr, 1);
    if (duk_is_undefined(thr, 0)) {
        duk_pop_undefined(thr);
        duk_push_hstring_stridx(thr, DUK_STRIDX_COMMA);
    } else {
        duk_to_string(thr, 0);
    }

    len = duk__push_this_obj_len_u32(thr);

    /* [ sep ToObject(this) len ] */

    DUK_DDD(DUK_DDDPRINT("sep=%!T, this=%!T, len=%lu",
                         (duk_tval *) duk_get_tval(thr, 0),
                         (duk_tval *) duk_get_tval(thr, 1),
                         (unsigned long) len));

    /* The extra (+4) is tight. */
    valstack_required = (duk_idx_t) ((len >= DUK__ARRAY_MID_JOIN_LIMIT ?
                                      DUK__ARRAY_MID_JOIN_LIMIT : len) + 4);
    duk_require_stack(thr, valstack_required);

    duk_dup_0(thr);

    /* [ sep ToObject(this) len sep ] */

    count = 0;
    idx = 0;
    for (;;) {
        DUK_DDD(DUK_DDDPRINT("join idx=%ld", (long) idx));
        if (count >= DUK__ARRAY_MID_JOIN_LIMIT ||   /* intermediate join to avoid valstack overflow */
            idx >= len) { /* end of loop (careful with len==0) */
            /* [ sep ToObject(this) len sep str0 ... str(count-1) ] */
            DUK_DDD(DUK_DDDPRINT("mid/final join, count=%ld, idx=%ld, len=%ld",
                                 (long) count, (long) idx, (long) len));
            duk_join(thr, (duk_idx_t) count);  /* -> [ sep ToObject(this) len str ] */
            duk_dup_0(thr);                    /* -> [ sep ToObject(this) len str sep ] */
            duk_insert(thr, -2);               /* -> [ sep ToObject(this) len sep str ] */
            count = 1;
        }
        if (idx >= len) {
            /* if true, the stack already contains the final result */
            break;
        }

        duk_get_prop_index(thr, 1, (duk_uarridx_t) idx);
        if (duk_is_null_or_undefined(thr, -1)) {
            duk_pop_nodecref_unsafe(thr);
            duk_push_hstring_empty(thr);
        } else {
            if (to_locale_string) {
                duk_to_object(thr, -1);
                duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_TO_LOCALE_STRING);
                duk_insert(thr, -2);  /* -> [ ... toLocaleString ToObject(val) ] */
                duk_call_method(thr, 0);
            }
            duk_to_string(thr, -1);
        }

        count++;
        idx++;
    }

    /* [ sep ToObject(this) len sep result ] */

    return 1;
}

/*
 *  pop(), push()
 */

#if defined(DUK_USE_ARRAY_FASTPATH)
DUK_LOCAL duk_ret_t duk__array_pop_fastpath(duk_hthread *thr, duk_harray *h_arr) {
    duk_tval *tv_arraypart;
    duk_tval *tv_val;
    duk_uint32_t len;

    tv_arraypart = DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) h_arr);
    len = h_arr->length;
    if (len <= 0) {
        /* nop, return undefined */
        return 0;
    }

    len--;
    h_arr->length = len;

    /* Fast path doesn't check for an index property inherited from
     * Array.prototype.  This is quite often acceptable; if not,
     * disable fast path.
     */
    DUK_ASSERT_VS_SPACE(thr);
    tv_val = tv_arraypart + len;
    if (DUK_TVAL_IS_UNUSED(tv_val)) {
        /* No net refcount change.  Value stack already has
         * 'undefined' based on value stack init policy.
         */
        DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top));
        DUK_ASSERT(DUK_TVAL_IS_UNUSED(tv_val));
    } else {
        /* No net refcount change. */
        DUK_TVAL_SET_TVAL(thr->valstack_top, tv_val);
        DUK_TVAL_SET_UNUSED(tv_val);
    }
    thr->valstack_top++;

    /* XXX: there's no shrink check in the fast path now */

    return 1;
}
#endif  /* DUK_USE_ARRAY_FASTPATH */

DUK_INTERNAL duk_ret_t duk_bi_array_prototype_pop(duk_hthread *thr) {
    duk_uint32_t len;
    duk_uint32_t idx;
#if defined(DUK_USE_ARRAY_FASTPATH)
    duk_harray *h_arr;
#endif

    DUK_ASSERT_TOP(thr, 0);

#if defined(DUK_USE_ARRAY_FASTPATH)
    h_arr = duk__arraypart_fastpath_this(thr);
    if (h_arr) {
        return duk__array_pop_fastpath(thr, h_arr);
    }
#endif

    /* XXX: Merge fastpath check into a related call (push this, coerce length, etc)? */

    len = duk__push_this_obj_len_u32(thr);
    if (len == 0) {
        duk_push_int(thr, 0);
        duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LENGTH);
        return 0;
    }
    idx = len - 1;

    duk_get_prop_index(thr, 0, (duk_uarridx_t) idx);
    duk_del_prop_index(thr, 0, (duk_uarridx_t) idx);
    duk_push_u32(thr, idx);
    duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LENGTH);
    return 1;
}

#if defined(DUK_USE_ARRAY_FASTPATH)
DUK_LOCAL duk_ret_t duk__array_push_fastpath(duk_hthread *thr, duk_harray *h_arr) {
    duk_tval *tv_arraypart;
    duk_tval *tv_src;
    duk_tval *tv_dst;
    duk_uint32_t len;
    duk_idx_t i, n;

    len = h_arr->length;
    tv_arraypart = DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) h_arr);

    n = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
    DUK_ASSERT(n >= 0);
    DUK_ASSERT((duk_uint32_t) n <= DUK_UINT32_MAX);
    if (DUK_UNLIKELY(len + (duk_uint32_t) n < len)) {
        DUK_D(DUK_DPRINT("Array.prototype.push() would go beyond 32-bit length, throw"));
        DUK_DCERROR_RANGE_INVALID_LENGTH(thr);  /* != 0 return value returned as is by caller */
    }
    if (len + (duk_uint32_t) n > DUK_HOBJECT_GET_ASIZE((duk_hobject *) h_arr)) {
        /* Array part would need to be extended.  Rely on slow path
         * for now.
         *
         * XXX: Rework hobject code a bit and add extend support.
         */
        return 0;
    }

    tv_src = thr->valstack_bottom;
    tv_dst = tv_arraypart + len;
    for (i = 0; i < n; i++) {
        /* No net refcount change; reset value stack values to
         * undefined to satisfy value stack init policy.
         */
        DUK_TVAL_SET_TVAL(tv_dst, tv_src);
        DUK_TVAL_SET_UNDEFINED(tv_src);
        tv_src++;
        tv_dst++;
    }
    thr->valstack_top = thr->valstack_bottom;
    len += (duk_uint32_t) n;
    h_arr->length = len;

    DUK_ASSERT((duk_uint_t) len == len);
    duk_push_uint(thr, (duk_uint_t) len);
    return 1;
}
#endif  /* DUK_USE_ARRAY_FASTPATH */

DUK_INTERNAL duk_ret_t duk_bi_array_prototype_push(duk_hthread *thr) {
    /* Note: 'this' is not necessarily an Array object.  The push()
     * algorithm is supposed to work for other kinds of objects too,
     * so the algorithm has e.g. an explicit update for the 'length'
     * property which is normally "magical" in arrays.
     */

    duk_uint32_t len;
    duk_idx_t i, n;
#if defined(DUK_USE_ARRAY_FASTPATH)
    duk_harray *h_arr;
#endif

#if defined(DUK_USE_ARRAY_FASTPATH)
    h_arr = duk__arraypart_fastpath_this(thr);
    if (h_arr) {
        duk_ret_t rc;
        rc = duk__array_push_fastpath(thr, h_arr);
        if (rc != 0) {
            return rc;
        }
        DUK_DD(DUK_DDPRINT("array push() fast path exited, resize case"));
    }
#endif

    n = duk_get_top(thr);
    len = duk__push_this_obj_len_u32(thr);

    /* [ arg1 ... argN obj length ] */

    /* Technically Array.prototype.push() can create an Array with length
     * longer than 2^32-1, i.e. outside the 32-bit range.  The final length
     * is *not* wrapped to 32 bits in the specification.
     *
     * This implementation tracks length with a uint32 because it's much
     * more practical.
     *
     * See: test-bi-array-push-maxlen.js.
     */

    if (len + (duk_uint32_t) n < len) {
        DUK_D(DUK_DPRINT("Array.prototype.push() would go beyond 32-bit length, throw"));
        DUK_DCERROR_RANGE_INVALID_LENGTH(thr);
    }

    for (i = 0; i < n; i++) {
        duk_dup(thr, i);
        duk_put_prop_index(thr, -3, (duk_uarridx_t) (len + (duk_uint32_t) i));
    }
    len += (duk_uint32_t) n;

    duk_push_u32(thr, len);
    duk_dup_top(thr);
    duk_put_prop_stridx_short(thr, -4, DUK_STRIDX_LENGTH);

    /* [ arg1 ... argN obj length new_length ] */
    return 1;
}

/*
 *  sort()
 *
 *  Currently qsort with random pivot.  This is now really, really slow,
 *  because there is no fast path for array parts.
 *
 *  Signed indices are used because qsort() leaves and degenerate cases
 *  may use a negative offset.
 */

DUK_LOCAL duk_small_int_t duk__array_sort_compare(duk_hthread *thr, duk_int_t idx1, duk_int_t idx2) {
    duk_bool_t have1, have2;
    duk_bool_t undef1, undef2;
    duk_small_int_t ret;
    duk_idx_t idx_obj = 1;  /* fixed offsets in valstack */
    duk_idx_t idx_fn = 0;
    duk_hstring *h1, *h2;

    /* Fast exit if indices are identical.  This is valid for a non-existent property,
     * for an undefined value, and almost always for ToString() coerced comparison of
     * arbitrary values (corner cases where this is not the case include e.g. a an
     * object with varying ToString() coercion).
     *
     * The specification does not prohibit "caching" of values read from the array, so
     * assuming equality for comparing an index with itself falls into the category of
     * "caching".
     *
     * Also, compareFn may be inconsistent, so skipping a call to compareFn here may
     * have an effect on the final result.  The specification does not require any
     * specific behavior for inconsistent compare functions, so again, this fast path
     * is OK.
     */

    if (idx1 == idx2) {
        DUK_DDD(DUK_DDDPRINT("duk__array_sort_compare: idx1=%ld, idx2=%ld -> indices identical, quick exit",
                             (long) idx1, (long) idx2));
        return 0;
    }

    have1 = duk_get_prop_index(thr, idx_obj, (duk_uarridx_t) idx1);
    have2 = duk_get_prop_index(thr, idx_obj, (duk_uarridx_t) idx2);

    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;
    duk_int_t i, n;
    n = (duk_int_t) duk_get_length(thr, 1);
    if (n > 4000) {
        n = 4000;
    }
    *ptr++ = '[';
    for (i = 0; i < n; i++) {
        if (i == pivot) {
            *ptr++ = '|';
        } else if (i == lo) {
            *ptr++ = '<';
        } else if (i == hi) {
            *ptr++ = '>';
        } else if (i >= lo && i <= hi) {
            *ptr++ = '-';
        } else {
            *ptr++ = ' ';
        }
    }
    *ptr++ = ']';
    *ptr++ = '\0';

    DUK_DDD(DUK_DDDPRINT("%s   (lo=%ld, hi=%ld, pivot=%ld)",
                         (const char *) buf, (long) lo, (long) hi, (long) pivot));
}
#endif

DUK_LOCAL void duk__array_qsort(duk_hthread *thr, duk_int_t lo, duk_int_t hi) {
    duk_int_t p, l, r;

    /* The lo/hi indices may be crossed and hi < 0 is possible at entry. */

    DUK_DDD(DUK_DDDPRINT("duk__array_qsort: lo=%ld, hi=%ld, obj=%!T",
                         (long) lo, (long) hi, (duk_tval *) duk_get_tval(thr, 1)));

    DUK_ASSERT_TOP(thr, 3);

    /* In some cases it may be that lo > hi, or hi < 0; these
     * degenerate cases happen e.g. for empty arrays, and in
     * recursion leaves.
     */

    /* trivial cases */
    if (hi - lo < 1) {
        DUK_DDD(DUK_DDDPRINT("degenerate case, return immediately"));
        return;
    }
    DUK_ASSERT(hi > lo);
    DUK_ASSERT(hi - lo + 1 >= 2);

    /* randomized pivot selection */
    p = lo + (duk_int_t) (DUK_UTIL_GET_RANDOM_DOUBLE(thr) * (duk_double_t) (hi - lo + 1));
    DUK_ASSERT(p >= lo && p <= hi);
    DUK_DDD(DUK_DDDPRINT("lo=%ld, hi=%ld, chose pivot p=%ld", (long) lo, (long) hi, (long) p));

    /* move pivot out of the way */
    duk__array_sort_swap(thr, p, lo);
    p = lo;
    DUK_DDD(DUK_DDDPRINT("pivot moved out of the way: %!T", (duk_tval *) duk_get_tval(thr, 1)));

    l = lo + 1;
    r = hi;
    for (;;) {
        /* find elements to swap */
        for (;;) {
            DUK_DDD(DUK_DDDPRINT("left scan: l=%ld, r=%ld, p=%ld",
                                 (long) l, (long) r, (long) p));
            if (l >= hi) {
                break;
            }
            if (duk__array_sort_compare(thr, l, p) >= 0) {  /* !(l < p) */
                break;
            }
            l++;
        }
        for (;;) {
            DUK_DDD(DUK_DDDPRINT("right scan: l=%ld, r=%ld, p=%ld",
                                 (long) l, (long) r, (long) p));
            if (r <= lo) {
                break;
            }
            if (duk__array_sort_compare(thr, p, r) >= 0) {  /* !(p < r) */
                break;
            }
            r--;
        }
        if (l >= r) {
            goto done;
        }
        DUK_ASSERT(l < r);

        DUK_DDD(DUK_DDDPRINT("swap %ld and %ld", (long) l, (long) r));

        duk__array_sort_swap(thr, l, r);

        DUK_DDD(DUK_DDDPRINT("after swap: %!T", (duk_tval *) duk_get_tval(thr, 1)));
        l++;
        r--;
    }
 done:
    /* Note that 'l' and 'r' may cross, i.e. r < l */
    DUK_ASSERT(l >= lo && l <= hi);
    DUK_ASSERT(r >= lo && r <= hi);

    /* XXX: there's no explicit recursion bound here now.  For the average
     * qsort recursion depth O(log n) that's not really necessary: e.g. for
     * 2**32 recursion depth would be about 32 which is OK.  However, qsort
     * worst case recursion depth is O(n) which may be a problem.
     */

    /* move pivot to its final place */
    DUK_DDD(DUK_DDDPRINT("before final pivot swap: %!T", (duk_tval *) duk_get_tval(thr, 1)));
    duk__array_sort_swap(thr, lo, r);

#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)
    duk__debuglog_qsort_state(thr, lo, hi, r);
#endif

    DUK_DDD(DUK_DDDPRINT("recurse: pivot=%ld, obj=%!T", (long) r, (duk_tval *) duk_get_tval(thr, 1)));
    duk__array_qsort(thr, lo, r - 1);
    duk__array_qsort(thr, r + 1, hi);
}

DUK_INTERNAL duk_ret_t duk_bi_array_prototype_sort(duk_hthread *thr) {
    duk_uint32_t len;

    /* XXX: len >= 0x80000000 won't work below because a signed type
     * is needed by qsort.
     */
    len = duk__push_this_obj_len_u32_limited(thr);

    /* stack[0] = compareFn
     * stack[1] = ToObject(this)
     * stack[2] = ToUint32(length)
     */

    if (len > 0) {
        /* avoid degenerate cases, so that (len - 1) won't underflow */
        duk__array_qsort(thr, (duk_int_t) 0, (duk_int_t) (len - 1));
    }

    DUK_ASSERT_TOP(thr, 3);
    duk_pop_nodecref_unsafe(thr);
    return 1;  /* return ToObject(this) */
}

/*
 *  splice()
 */

/* XXX: this compiles to over 500 bytes now, even without special handling
 * for an array part.  Uses signed ints so does not handle full array range correctly.
 */

/* XXX: can shift() / unshift() use the same helper?
 *   shift() is (close to?) <--> splice(0, 1)
 *   unshift is (close to?) <--> splice(0, 0, [items])?
 */

DUK_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_hthread *thr) {
    duk_idx_t nargs;
    duk_uint32_t len_u32;
    duk_int_t len;
    duk_bool_t have_delcount;
    duk_int_t item_count;
    duk_int_t act_start;
    duk_int_t del_count;
    duk_int_t i, n;

    DUK_UNREF(have_delcount);

    nargs = duk_get_top(thr);
    if (nargs < 2) {
        duk_set_top(thr, 2);
        nargs = 2;
        have_delcount = 0;
    } else {
        have_delcount = 1;
    }

    /* XXX: len >= 0x80000000 won't work below because we need to be
     * able to represent -len.
     */
    len_u32 = duk__push_this_obj_len_u32_limited(thr);
    len = (duk_int_t) len_u32;
    DUK_ASSERT(len >= 0);

    act_start = duk_to_int_clamped(thr, 0, -len, len);
    if (act_start < 0) {
        act_start = len + act_start;
    }
    DUK_ASSERT(act_start >= 0 && act_start <= len);

#if defined(DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT)
    if (have_delcount) {
#endif
        del_count = duk_to_int_clamped(thr, 1, 0, len - act_start);
#if defined(DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT)
    } else {
        /* E5.1 standard behavior when deleteCount is not given would be
         * to treat it just like if 'undefined' was given, which coerces
         * ultimately to 0.  Real world behavior is to splice to the end
         * of array, see test-bi-array-proto-splice-no-delcount.js.
         */
        del_count = len - act_start;
    }
#endif

    DUK_ASSERT(nargs >= 2);
    item_count = (duk_int_t) (nargs - 2);

    DUK_ASSERT(del_count >= 0 && del_count <= len - act_start);
    DUK_ASSERT(del_count + act_start <= len);

    /* For now, restrict result array into 32-bit length range. */
    if (((duk_double_t) len) - ((duk_double_t) del_count) + ((duk_double_t) item_count) > (duk_double_t) DUK_UINT32_MAX) {
        DUK_D(DUK_DPRINT("Array.prototype.splice() would go beyond 32-bit length, throw"));
        DUK_DCERROR_RANGE_INVALID_LENGTH(thr);
    }

    duk_push_array(thr);

    /* stack[0] = start
     * stack[1] = deleteCount
     * stack[2...nargs-1] = items
     * stack[nargs] = ToObject(this)               -3
     * stack[nargs+1] = ToUint32(length)           -2
     * stack[nargs+2] = result array               -1
     */

    DUK_ASSERT_TOP(thr, nargs + 3);

    /* Step 9: copy elements-to-be-deleted into the result array */

    for (i = 0; i < del_count; i++) {
        if (duk_get_prop_index(thr, -3, (duk_uarridx_t) (act_start + i))) {
            duk_xdef_prop_index_wec(thr, -2, (duk_uarridx_t) i);  /* throw flag irrelevant (false in std alg) */
        } else {
            duk_pop_undefined(thr);
        }
    }
    duk_push_u32(thr, (duk_uint32_t) del_count);
    duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);

    /* Steps 12 and 13: reorganize elements to make room for itemCount elements */

    if (item_count < del_count) {
        /*    [ A B C D E F G H ]    rel_index = 2, del_count 3, item count 1
         * -> [ A B F G H ]          (conceptual intermediate step)
         * -> [ A B . F G H ]        (placeholder marked)
         *    [ A B C F G H ]        (actual result at this point, C will be replaced)
         */

        DUK_ASSERT_TOP(thr, nargs + 3);

        n = len - del_count;
        for (i = act_start; i < n; i++) {
            if (duk_get_prop_index(thr, -3, (duk_uarridx_t) (i + del_count))) {
                duk_put_prop_index(thr, -4, (duk_uarridx_t) (i + item_count));
            } else {
                duk_pop_undefined(thr);
                duk_del_prop_index(thr, -3, (duk_uarridx_t) (i + item_count));
            }
        }

        DUK_ASSERT_TOP(thr, nargs + 3);

        /* loop iterator init and limit changed from standard algorithm */
        n = len - del_count + item_count;
        for (i = len - 1; i >= n; i--) {
            duk_del_prop_index(thr, -3, (duk_uarridx_t) i);
        }

        DUK_ASSERT_TOP(thr, nargs + 3);
    } else if (item_count > del_count) {
        /*    [ A B C D E F G H ]    rel_index = 2, del_count 3, item count 4
         * -> [ A B F G H ]          (conceptual intermediate step)
         * -> [ A B . . . . F G H ]  (placeholder marked)
         *    [ A B C D E F F G H ]  (actual result at this point)
         */

        DUK_ASSERT_TOP(thr, nargs + 3);

        /* loop iterator init and limit changed from standard algorithm */
        for (i = len - del_count - 1; i >= act_start; i--) {
            if (duk_get_prop_index(thr, -3, (duk_uarridx_t) (i + del_count))) {
                duk_put_prop_index(thr, -4, (duk_uarridx_t) (i + item_count));
            } else {
                duk_pop_undefined(thr);
                duk_del_prop_index(thr, -3, (duk_uarridx_t) (i + item_count));
            }
        }

        DUK_ASSERT_TOP(thr, nargs + 3);
    } else {
        /*    [ A B C D E F G H ]    rel_index = 2, del_count 3, item count 3
         * -> [ A B F G H ]          (conceptual intermediate step)
         * -> [ A B . . . F G H ]    (placeholder marked)
         *    [ A B C D E F G H ]    (actual result at this point)
         */
    }
    DUK_ASSERT_TOP(thr, nargs + 3);

    /* Step 15: insert itemCount elements into the hole made above */

    for (i = 0; i < item_count; i++) {
        duk_dup(thr, i + 2);  /* args start at index 2 */
        duk_put_prop_index(thr, -4, (duk_uarridx_t) (act_start + i));
    }

    /* Step 16: update length; note that the final length may be above 32 bit range
     * (but we checked above that this isn't the case here)
     */

    duk_push_u32(thr, (duk_uint32_t) (len - del_count + item_count));
    duk_put_prop_stridx_short(thr, -4, DUK_STRIDX_LENGTH);

    /* result array is already at the top of stack */
    DUK_ASSERT_TOP(thr, nargs + 3);
    return 1;
}

/*
 *  reverse()
 */

DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reverse(duk_hthread *thr) {
    duk_uint32_t len;
    duk_uint32_t middle;
    duk_uint32_t lower, upper;
    duk_bool_t have_lower, have_upper;

    len = duk__push_this_obj_len_u32(thr);
    middle = len / 2;

    /* If len <= 1, middle will be 0 and for-loop bails out
     * immediately (0 < 0 -> false).
     */

    for (lower = 0; lower < middle; lower++) {
        DUK_ASSERT(len >= 2);
        DUK_ASSERT_TOP(thr, 2);

        DUK_ASSERT(len >= lower + 1);
        upper = len - lower - 1;

        have_lower = duk_get_prop_index(thr, -2, (duk_uarridx_t) lower);
        have_upper = duk_get_prop_index(thr, -3, (duk_uarridx_t) upper);

        /* [ ToObject(this) ToUint32(length) lowerValue upperValue ] */

        if (have_upper) {
            duk_put_prop_index(thr, -4, (duk_uarridx_t) lower);
        } else {
            duk_del_prop_index(thr, -4, (duk_uarridx_t) lower);
            duk_pop_undefined(thr);
        }

        if (have_lower) {
            duk_put_prop_index(thr, -3, (duk_uarridx_t) upper);
        } else {
            duk_del_prop_index(thr, -3, (duk_uarridx_t) upper);
            duk_pop_undefined(thr);
        }

        DUK_ASSERT_TOP(thr, 2);
    }

    DUK_ASSERT_TOP(thr, 2);
    duk_pop_unsafe(thr);  /* -> [ ToObject(this) ] */
    return 1;
}

/*
 *  slice()
 */

DUK_INTERNAL duk_ret_t duk_bi_array_prototype_slice(duk_hthread *thr) {
    duk_uint32_t len_u32;
    duk_int_t len;
    duk_int_t start, end;
    duk_int_t i;
    duk_uarridx_t idx;
    duk_uint32_t res_length = 0;

    /* XXX: len >= 0x80000000 won't work below because we need to be
     * able to represent -len.
     */
    len_u32 = duk__push_this_obj_len_u32_limited(thr);
    len = (duk_int_t) len_u32;
    DUK_ASSERT(len >= 0);

    duk_push_array(thr);

    /* stack[0] = start
     * stack[1] = end
     * stack[2] = ToObject(this)
     * stack[3] = ToUint32(length)
     * stack[4] = result array
     */

    start = duk_to_int_clamped(thr, 0, -len, len);
    if (start < 0) {
        start = len + start;
    }
    /* XXX: could duk_is_undefined() provide defaulting undefined to 'len'
     * (the upper limit)?
     */
    if (duk_is_undefined(thr, 1)) {
        end = len;
    } else {
        end = duk_to_int_clamped(thr, 1, -len, len);
        if (end < 0) {
            end = len + end;
        }
    }
    DUK_ASSERT(start >= 0 && start <= len);
    DUK_ASSERT(end >= 0 && end <= len);

    idx = 0;
    for (i = start; i < end; i++) {
        DUK_ASSERT_TOP(thr, 5);
        if (duk_get_prop_index(thr, 2, (duk_uarridx_t) i)) {
            duk_xdef_prop_index_wec(thr, 4, idx);
            res_length = idx + 1;
        } else {
            duk_pop_undefined(thr);
        }
        idx++;
        DUK_ASSERT_TOP(thr, 5);
    }

    duk_push_u32(thr, res_length);
    duk_xdef_prop_stridx_short(thr, 4, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);

    DUK_ASSERT_TOP(thr, 5);
    return 1;
}

/*
 *  shift()
 */

DUK_INTERNAL duk_ret_t duk_bi_array_prototype_shift(duk_hthread *thr) {
    duk_uint32_t len;
    duk_uint32_t i;

    len = duk__push_this_obj_len_u32(thr);
    if (len == 0) {
        duk_push_int(thr, 0);
        duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LENGTH);
        return 0;
    }

    duk_get_prop_index(thr, 0, 0);

    /* stack[0] = object (this)
     * stack[1] = ToUint32(length)
     * stack[2] = elem at index 0 (retval)
     */

    for (i = 1; i < len; i++) {
        DUK_ASSERT_TOP(thr, 3);
        if (duk_get_prop_index(thr, 0, (duk_uarridx_t) i)) {
            /* fromPresent = true */
            duk_put_prop_index(thr, 0, (duk_uarridx_t) (i - 1));
        } else {
            /* fromPresent = false */
            duk_del_prop_index(thr, 0, (duk_uarridx_t) (i - 1));
            duk_pop_undefined(thr);
        }
    }
    duk_del_prop_index(thr, 0, (duk_uarridx_t) (len - 1));

    duk_push_u32(thr, (duk_uint32_t) (len - 1));
    duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LENGTH);

    DUK_ASSERT_TOP(thr, 3);
    return 1;
}

/*
 *  unshift()
 */

DUK_INTERNAL duk_ret_t duk_bi_array_prototype_unshift(duk_hthread *thr) {
    duk_idx_t nargs;
    duk_uint32_t len;
    duk_uint32_t i;

    nargs = duk_get_top(thr);
    len = duk__push_this_obj_len_u32(thr);

    /* stack[0...nargs-1] = unshift args (vararg)
     * stack[nargs] = ToObject(this)
     * stack[nargs+1] = ToUint32(length)
     */

    DUK_ASSERT_TOP(thr, nargs + 2);

    /* Note: unshift() may operate on indices above unsigned 32-bit range
     * and the final length may be >= 2**32.  However, we restrict the
     * final result to 32-bit range for practicality.
     */

    if (len + (duk_uint32_t) nargs < len) {
        DUK_D(DUK_DPRINT("Array.prototype.unshift() would go beyond 32-bit length, throw"));
        DUK_DCERROR_RANGE_INVALID_LENGTH(thr);
    }

    i = len;
    while (i > 0) {
        DUK_ASSERT_TOP(thr, nargs + 2);
        i--;
        /* k+argCount-1; note that may be above 32-bit range */

        if (duk_get_prop_index(thr, -2, (duk_uarridx_t) i)) {
            /* fromPresent = true */
            /* [ ... ToObject(this) ToUint32(length) val ] */
            duk_put_prop_index(thr, -3, (duk_uarridx_t) (i + (duk_uint32_t) nargs));  /* -> [ ... ToObject(this) ToUint32(length) ] */
        } else {
            /* fromPresent = false */
            /* [ ... ToObject(this) ToUint32(length) val ] */
            duk_pop_undefined(thr);
            duk_del_prop_index(thr, -2, (duk_uarridx_t) (i + (duk_uint32_t) nargs));  /* -> [ ... ToObject(this) ToUint32(length) ] */
        }
        DUK_ASSERT_TOP(thr, nargs + 2);
    }

    for (i = 0; i < (duk_uint32_t) nargs; i++) {
        DUK_ASSERT_TOP(thr, nargs + 2);
        duk_dup(thr, (duk_idx_t) i);  /* -> [ ... ToObject(this) ToUint32(length) arg[i] ] */
        duk_put_prop_index(thr, -3, (duk_uarridx_t) i);
        DUK_ASSERT_TOP(thr, nargs + 2);
    }

    DUK_ASSERT_TOP(thr, nargs + 2);
    duk_push_u32(thr, len + (duk_uint32_t) nargs);
    duk_dup_top(thr);  /* -> [ ... ToObject(this) ToUint32(length) final_len final_len ] */
    duk_put_prop_stridx_short(thr, -4, DUK_STRIDX_LENGTH);
    return 1;
}

/*
 *  indexOf(), lastIndexOf()
 */

DUK_INTERNAL duk_ret_t duk_bi_array_prototype_indexof_shared(duk_hthread *thr) {
    duk_idx_t nargs;
    duk_int_t i, len;
    duk_int_t from_idx;
    duk_small_int_t idx_step = duk_get_current_magic(thr);  /* idx_step is +1 for indexOf, -1 for lastIndexOf */

    /* lastIndexOf() needs to be a vararg function because we must distinguish
     * between an undefined fromIndex and a "not given" fromIndex; indexOf() is
     * made vararg for symmetry although it doesn't strictly need to be.
     */

    nargs = duk_get_top(thr);
    duk_set_top(thr, 2);

    /* XXX: must be able to represent -len */
    len = (duk_int_t) duk__push_this_obj_len_u32_limited(thr);
    if (len == 0) {
        goto not_found;
    }

    /* Index clamping is a bit tricky, we must ensure that we'll only iterate
     * through elements that exist and that the specific requirements from E5.1
     * Sections 15.4.4.14 and 15.4.4.15 are fulfilled; especially:
     *
     *   - indexOf: clamp to [-len,len], negative handling -> [0,len],
     *     if clamped result is len, for-loop bails out immediately
     *
     *   - lastIndexOf: clamp to [-len-1, len-1], negative handling -> [-1, len-1],
     *     if clamped result is -1, for-loop bails out immediately
     *
     * If fromIndex is not given, ToInteger(undefined) = 0, which is correct
     * for indexOf() but incorrect for lastIndexOf().  Hence special handling,
     * and why lastIndexOf() needs to be a vararg function.
     */

    if (nargs >= 2) {
        /* indexOf: clamp fromIndex to [-len, len]
         * (if fromIndex == len, for-loop terminates directly)
         *
         * lastIndexOf: clamp fromIndex to [-len - 1, len - 1]
         * (if clamped to -len-1 -> fromIndex becomes -1, terminates for-loop directly)
         */
        from_idx = duk_to_int_clamped(thr,
                                      1,
                                      (idx_step > 0 ? -len : -len - 1),
                                      (idx_step > 0 ? len : len - 1));
        if (from_idx < 0) {
            /* for lastIndexOf, result may be -1 (mark immediate termination) */
            from_idx = len + from_idx;
        }
    } else {
        /* for indexOf, ToInteger(undefined) would be 0, i.e. correct, but
         * handle both indexOf and lastIndexOf specially here.
         */
        if (idx_step > 0) {
            from_idx = 0;
        } else {
            from_idx = len - 1;
        }
    }

    /* stack[0] = searchElement
     * stack[1] = fromIndex
     * stack[2] = object
     * stack[3] = length (not needed, but not popped above)
     */

    for (i = from_idx; i >= 0 && i < len; i += idx_step) {
        DUK_ASSERT_TOP(thr, 4);

        if (duk_get_prop_index(thr, 2, (duk_uarridx_t) i)) {
            DUK_ASSERT_TOP(thr, 5);
            if (duk_strict_equals(thr, 0, 4)) {
                duk_push_int(thr, i);
                return 1;
            }
        }

        duk_pop_unsafe(thr);
    }

 not_found:
    duk_push_int(thr, -1);
    return 1;
}

/*
 *  every(), some(), forEach(), map(), filter()
 */

#define DUK__ITER_EVERY    0
#define DUK__ITER_SOME     1
#define DUK__ITER_FOREACH  2
#define DUK__ITER_MAP      3
#define DUK__ITER_FILTER   4

/* XXX: This helper is a bit awkward because the handling for the different iteration
 * callers is quite different.  This now compiles to a bit less than 500 bytes, so with
 * 5 callers the net result is about 100 bytes / caller.
 */

DUK_INTERNAL duk_ret_t duk_bi_array_prototype_iter_shared(duk_hthread *thr) {
    duk_uint32_t len;
    duk_uint32_t i;
    duk_uarridx_t k;
    duk_bool_t bval;
    duk_small_int_t iter_type = duk_get_current_magic(thr);
    duk_uint32_t res_length = 0;

    /* each call this helper serves has nargs==2 */
    DUK_ASSERT_TOP(thr, 2);

    len = duk__push_this_obj_len_u32(thr);
    duk_require_callable(thr, 0);
    /* if thisArg not supplied, behave as if undefined was supplied */

    if (iter_type == DUK__ITER_MAP || iter_type == DUK__ITER_FILTER) {
        duk_push_array(thr);
    } else {
        duk_push_undefined(thr);
    }

    /* stack[0] = callback
     * stack[1] = thisArg
     * stack[2] = object
     * stack[3] = ToUint32(length)  (unused, but avoid unnecessary pop)
     * stack[4] = result array (or undefined)
     */

    k = 0;  /* result index for filter() */
    for (i = 0; i < len; i++) {
        DUK_ASSERT_TOP(thr, 5);

        if (!duk_get_prop_index(thr, 2, (duk_uarridx_t) i)) {
            /* For 'map' trailing missing elements don't invoke the
             * callback but count towards the result length.
             */
            if (iter_type == DUK__ITER_MAP) {
                res_length = i + 1;
            }
            duk_pop_undefined(thr);
            continue;
        }

        /* The original value needs to be preserved for filter(), hence
         * this funny order.  We can't re-get the value because of side
         * effects.
         */

        duk_dup_0(thr);
        duk_dup_1(thr);
        duk_dup_m3(thr);
        duk_push_u32(thr, i);
        duk_dup_2(thr);  /* [ ... val callback thisArg val i obj ] */
        duk_call_method(thr, 3); /* -> [ ... val retval ] */

        switch (iter_type) {
        case DUK__ITER_EVERY:
            bval = duk_to_boolean(thr, -1);
            if (!bval) {
                /* stack top contains 'false' */
                return 1;
            }
            break;
        case DUK__ITER_SOME:
            bval = duk_to_boolean(thr, -1);
            if (bval) {
                /* stack top contains 'true' */
                return 1;
            }
            break;
        case DUK__ITER_FOREACH:
            /* nop */
            break;
        case DUK__ITER_MAP:
            duk_dup_top(thr);
            duk_xdef_prop_index_wec(thr, 4, (duk_uarridx_t) i);  /* retval to result[i] */
            res_length = i + 1;
            break;
        case DUK__ITER_FILTER:
            bval = duk_to_boolean(thr, -1);
            if (bval) {
                duk_dup_m2(thr);  /* orig value */
                duk_xdef_prop_index_wec(thr, 4, (duk_uarridx_t) k);
                k++;
                res_length = k;
            }
            break;
        default:
            DUK_UNREACHABLE();
            break;
        }
        duk_pop_2_unsafe(thr);

        DUK_ASSERT_TOP(thr, 5);
    }

    switch (iter_type) {
    case DUK__ITER_EVERY:
        duk_push_true(thr);
        break;
    case DUK__ITER_SOME:
        duk_push_false(thr);
        break;
    case DUK__ITER_FOREACH:
        duk_push_undefined(thr);
        break;
    case DUK__ITER_MAP:
    case DUK__ITER_FILTER:
        DUK_ASSERT_TOP(thr, 5);
        DUK_ASSERT(duk_is_array(thr, -1));  /* topmost element is the result array already */
        duk_push_u32(thr, res_length);
        duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);
        break;
    default:
        DUK_UNREACHABLE();
        break;
    }

    return 1;
}

/*
 *  reduce(), reduceRight()
 */

DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reduce_shared(duk_hthread *thr) {
    duk_idx_t nargs;
    duk_bool_t have_acc;
    duk_uint32_t i, len;
    duk_small_int_t idx_step = duk_get_current_magic(thr);  /* idx_step is +1 for reduce, -1 for reduceRight */

    /* We're a varargs function because we need to detect whether
     * initialValue was given or not.
     */
    nargs = duk_get_top(thr);
    DUK_DDD(DUK_DDDPRINT("nargs=%ld", (long) nargs));

    duk_set_top(thr, 2);
    len = duk__push_this_obj_len_u32(thr);
    duk_require_callable(thr, 0);

    /* stack[0] = callback fn
     * stack[1] = initialValue
     * stack[2] = object (coerced this)
     * stack[3] = length (not needed, but not popped above)
     * stack[4] = accumulator
     */

    have_acc = 0;
    if (nargs >= 2) {
        duk_dup_1(thr);
        have_acc = 1;
    }
    DUK_DDD(DUK_DDDPRINT("have_acc=%ld, acc=%!T",
                         (long) have_acc, (duk_tval *) duk_get_tval(thr, 3)));

    /* For len == 0, i is initialized to len - 1 which underflows.
     * The condition (i < len) will then exit the for-loop on the
     * first round which is correct.  Similarly, loop termination
     * happens by i underflowing.
     */

    for (i = (idx_step >= 0 ? 0 : len - 1);
         i < len;  /* i >= 0 would always be true */
         i += (duk_uint32_t) idx_step) {
        DUK_DDD(DUK_DDDPRINT("i=%ld, len=%ld, have_acc=%ld, top=%ld, acc=%!T",
                             (long) i, (long) len, (long) have_acc,
                             (long) duk_get_top(thr),
                             (duk_tval *) duk_get_tval(thr, 4)));

        DUK_ASSERT((have_acc && duk_get_top(thr) == 5) ||
                   (!have_acc && duk_get_top(thr) == 4));

        if (!duk_has_prop_index(thr, 2, (duk_uarridx_t) i)) {
            continue;
        }

        if (!have_acc) {
            DUK_ASSERT_TOP(thr, 4);
            duk_get_prop_index(thr, 2, (duk_uarridx_t) i);
            have_acc = 1;
            DUK_ASSERT_TOP(thr, 5);
        } else {
            DUK_ASSERT_TOP(thr, 5);
            duk_dup_0(thr);
            duk_dup(thr, 4);
            duk_get_prop_index(thr, 2, (duk_uarridx_t) i);
            duk_push_u32(thr, i);
            duk_dup_2(thr);
            DUK_DDD(DUK_DDDPRINT("calling reduce function: func=%!T, prev=%!T, curr=%!T, idx=%!T, obj=%!T",
                                 (duk_tval *) duk_get_tval(thr, -5), (duk_tval *) duk_get_tval(thr, -4),
                                 (duk_tval *) duk_get_tval(thr, -3), (duk_tval *) duk_get_tval(thr, -2),
                                 (duk_tval *) duk_get_tval(thr, -1)));
            duk_call(thr, 4);
            DUK_DDD(DUK_DDDPRINT("-> result: %!T", (duk_tval *) duk_get_tval(thr, -1)));
            duk_replace(thr, 4);
            DUK_ASSERT_TOP(thr, 5);
        }
    }

    if (!have_acc) {
        DUK_DCERROR_TYPE_INVALID_ARGS(thr);
    }

    DUK_ASSERT_TOP(thr, 5);
    return 1;
}

#endif  /* DUK_USE_ARRAY_BUILTIN */

/* automatic undefs */
#undef DUK__ARRAY_MID_JOIN_LIMIT
#undef DUK__ITER_EVERY
#undef DUK__ITER_FILTER
#undef DUK__ITER_FOREACH
#undef DUK__ITER_MAP
#undef DUK__ITER_SOME
//#line 1 "duk_bi_boolean.c"
/*
 *  Boolean built-ins
 */

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

#if defined(DUK_USE_BOOLEAN_BUILTIN)

/* Shared helper to provide toString() and valueOf().  Checks 'this', gets
 * the primitive value to stack top, and optionally coerces with ToString().
 */
DUK_INTERNAL duk_ret_t duk_bi_boolean_prototype_tostring_shared(duk_hthread *thr) {
    duk_tval *tv;
    duk_hobject *h;
    duk_small_int_t coerce_tostring = duk_get_current_magic(thr);

    /* XXX: there is room to use a shared helper here, many built-ins
     * check the 'this' type, and if it's an object, check its class,
     * then get its internal value, etc.
     */

    duk_push_this(thr);
    tv = duk_get_tval(thr, -1);
    DUK_ASSERT(tv != NULL);

    if (DUK_TVAL_IS_BOOLEAN(tv)) {
        goto type_ok;
    } else if (DUK_TVAL_IS_OBJECT(tv)) {
        h = DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(h != NULL);

        if (DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_BOOLEAN) {
            duk_xget_owndataprop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE);
            DUK_ASSERT(duk_is_boolean(thr, -1));
            goto type_ok;
        }
    }

    DUK_DCERROR_TYPE_INVALID_ARGS(thr);
    /* never here */

 type_ok:
    if (coerce_tostring) {
        duk_to_string(thr, -1);
    }
    return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_boolean_constructor(duk_hthread *thr) {
    duk_hobject *h_this;

    duk_to_boolean(thr, 0);

    if (duk_is_constructor_call(thr)) {
        /* XXX: helper; rely on Boolean.prototype as being non-writable, non-configurable */
        duk_push_this(thr);
        h_this = duk_known_hobject(thr, -1);
        DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_this) == thr->builtins[DUK_BIDX_BOOLEAN_PROTOTYPE]);

        DUK_HOBJECT_SET_CLASS_NUMBER(h_this, DUK_HOBJECT_CLASS_BOOLEAN);

        duk_dup_0(thr);  /* -> [ val obj val ] */
        duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE);  /* XXX: proper flags? */
    }  /* unbalanced stack */

    return 1;
}

#endif  /* DUK_USE_BOOLEAN_BUILTIN */
//#line 1 "duk_bi_buffer.c"
/*
 *  ES2015 TypedArray and Node.js Buffer built-ins
 */

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

/*
 *  Helpers for buffer handling, enabled with DUK_USE_BUFFEROBJECT_SUPPORT.
 */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
/* Map class number (minus DUK_HOBJECT_CLASS_BUFOBJ_MIN) to a bidx for the
 * default internal prototype.
 */
static const duk_uint8_t duk__buffer_proto_from_classnum[] = {
    DUK_BIDX_ARRAYBUFFER_PROTOTYPE,
    DUK_BIDX_DATAVIEW_PROTOTYPE,
    DUK_BIDX_INT8ARRAY_PROTOTYPE,
    DUK_BIDX_UINT8ARRAY_PROTOTYPE,
    DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE,
    DUK_BIDX_INT16ARRAY_PROTOTYPE,
    DUK_BIDX_UINT16ARRAY_PROTOTYPE,
    DUK_BIDX_INT32ARRAY_PROTOTYPE,
    DUK_BIDX_UINT32ARRAY_PROTOTYPE,
    DUK_BIDX_FLOAT32ARRAY_PROTOTYPE,
    DUK_BIDX_FLOAT64ARRAY_PROTOTYPE
};

/* Map DUK_HBUFOBJ_ELEM_xxx to duk_hobject class number.
 * Sync with duk_hbufobj.h and duk_hobject.h.
 */
static const duk_uint8_t duk__buffer_class_from_elemtype[9] = {
    DUK_HOBJECT_CLASS_UINT8ARRAY,
    DUK_HOBJECT_CLASS_UINT8CLAMPEDARRAY,
    DUK_HOBJECT_CLASS_INT8ARRAY,
    DUK_HOBJECT_CLASS_UINT16ARRAY,
    DUK_HOBJECT_CLASS_INT16ARRAY,
    DUK_HOBJECT_CLASS_UINT32ARRAY,
    DUK_HOBJECT_CLASS_INT32ARRAY,
    DUK_HOBJECT_CLASS_FLOAT32ARRAY,
    DUK_HOBJECT_CLASS_FLOAT64ARRAY
};

/* Map DUK_HBUFOBJ_ELEM_xxx to prototype object built-in index.
 * Sync with duk_hbufobj.h.
 */
static const duk_uint8_t duk__buffer_proto_from_elemtype[9] = {
    DUK_BIDX_UINT8ARRAY_PROTOTYPE,
    DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE,
    DUK_BIDX_INT8ARRAY_PROTOTYPE,
    DUK_BIDX_UINT16ARRAY_PROTOTYPE,
    DUK_BIDX_INT16ARRAY_PROTOTYPE,
    DUK_BIDX_UINT32ARRAY_PROTOTYPE,
    DUK_BIDX_INT32ARRAY_PROTOTYPE,
    DUK_BIDX_FLOAT32ARRAY_PROTOTYPE,
    DUK_BIDX_FLOAT64ARRAY_PROTOTYPE
};

/* Map DUK__FLD_xxx to byte size. */
static const duk_uint8_t duk__buffer_nbytes_from_fldtype[6] = {
    1,  /* DUK__FLD_8BIT */
    2,  /* DUK__FLD_16BIT */
    4,  /* DUK__FLD_32BIT */
    4,  /* DUK__FLD_FLOAT */
    8,  /* DUK__FLD_DOUBLE */
    0   /* DUK__FLD_VARINT; not relevant here */
};

/* Bitfield for each DUK_HBUFOBJ_ELEM_xxx indicating which element types
 * are compatible with a blind byte copy for the TypedArray set() method (also
 * used for TypedArray constructor).  Array index is target buffer elem type,
 * bitfield indicates compatible source types.  The types must have same byte
 * size and they must be coercion compatible.
 */
#if !defined(DUK_USE_PREFER_SIZE)
static duk_uint16_t duk__buffer_elemtype_copy_compatible[9] = {
    /* xxx -> DUK_HBUFOBJ_ELEM_UINT8 */
    (1U << DUK_HBUFOBJ_ELEM_UINT8) |
        (1U << DUK_HBUFOBJ_ELEM_UINT8CLAMPED) |
        (1U << DUK_HBUFOBJ_ELEM_INT8),

    /* xxx -> DUK_HBUFOBJ_ELEM_UINT8CLAMPED
     * Note: INT8 is -not- copy compatible, e.g. -1 would coerce to 0x00.
     */
    (1U << DUK_HBUFOBJ_ELEM_UINT8) |
        (1U << DUK_HBUFOBJ_ELEM_UINT8CLAMPED),

    /* xxx -> DUK_HBUFOBJ_ELEM_INT8 */
    (1U << DUK_HBUFOBJ_ELEM_UINT8) |
        (1U << DUK_HBUFOBJ_ELEM_UINT8CLAMPED) |
        (1U << DUK_HBUFOBJ_ELEM_INT8),

    /* xxx -> DUK_HBUFOBJ_ELEM_UINT16 */
    (1U << DUK_HBUFOBJ_ELEM_UINT16) |
        (1U << DUK_HBUFOBJ_ELEM_INT16),

    /* xxx -> DUK_HBUFOBJ_ELEM_INT16 */
    (1U << DUK_HBUFOBJ_ELEM_UINT16) |
        (1U << DUK_HBUFOBJ_ELEM_INT16),

    /* xxx -> DUK_HBUFOBJ_ELEM_UINT32 */
    (1U << DUK_HBUFOBJ_ELEM_UINT32) |
        (1U << DUK_HBUFOBJ_ELEM_INT32),

    /* xxx -> DUK_HBUFOBJ_ELEM_INT32 */
    (1U << DUK_HBUFOBJ_ELEM_UINT32) |
        (1U << DUK_HBUFOBJ_ELEM_INT32),

    /* xxx -> DUK_HBUFOBJ_ELEM_FLOAT32 */
    (1U << DUK_HBUFOBJ_ELEM_FLOAT32),

    /* xxx -> DUK_HBUFOBJ_ELEM_FLOAT64 */
    (1U << DUK_HBUFOBJ_ELEM_FLOAT64)
};
#endif  /* !DUK_USE_PREFER_SIZE */

DUK_LOCAL duk_hbufobj *duk__hbufobj_promote_this(duk_hthread *thr) {
    duk_tval *tv_dst;
    duk_hbufobj *res;

    duk_push_this(thr);
    DUK_ASSERT(duk_is_buffer(thr, -1));
    res = (duk_hbufobj *) duk_to_hobject(thr, -1);
    DUK_HBUFOBJ_ASSERT_VALID(res);
    DUK_DD(DUK_DDPRINT("promoted 'this' automatically to an ArrayBuffer: %!iT", duk_get_tval(thr, -1)));

    tv_dst = duk_get_borrowed_this_tval(thr);
    DUK_TVAL_SET_OBJECT_UPDREF(thr, tv_dst, (duk_hobject *) res);
    duk_pop(thr);

    return res;
}

#define DUK__BUFOBJ_FLAG_THROW    (1 << 0)
#define DUK__BUFOBJ_FLAG_PROMOTE  (1 << 1)

/* Shared helper.  When DUK__BUFOBJ_FLAG_PROMOTE is given, the return value is
 * always a duk_hbufobj *.  Without the flag the return value can also be a
 * plain buffer, and the caller must check for it using DUK_HEAPHDR_IS_BUFFER().
 */
DUK_LOCAL duk_heaphdr *duk__getrequire_bufobj_this(duk_hthread *thr, duk_small_uint_t flags) {
    duk_tval *tv;
    duk_hbufobj *h_this;

    DUK_ASSERT(thr != NULL);

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

    if (DUK_TVAL_IS_OBJECT(tv)) {
        h_this = (duk_hbufobj *) DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(h_this != NULL);
        if (DUK_HOBJECT_IS_BUFOBJ((duk_hobject *) h_this)) {
            DUK_HBUFOBJ_ASSERT_VALID(h_this);
            return (duk_heaphdr *) h_this;
        }
    } else if (DUK_TVAL_IS_BUFFER(tv)) {
        if (flags & DUK__BUFOBJ_FLAG_PROMOTE) {
            /* Promote a plain buffer to a Uint8Array.  This is very
             * inefficient but allows plain buffer to be used wherever an
             * Uint8Array is used with very small cost; hot path functions
             * like index read/write calls should provide direct buffer
             * support to avoid promotion.
             */
            /* XXX: make this conditional to a flag if call sites need it? */
            h_this = duk__hbufobj_promote_this(thr);
            DUK_ASSERT(h_this != NULL);
            DUK_HBUFOBJ_ASSERT_VALID(h_this);
            return (duk_heaphdr *) h_this;
        } else {
            /* XXX: ugly, share return pointer for duk_hbuffer. */
            return (duk_heaphdr *) DUK_TVAL_GET_BUFFER(tv);
        }
    }

    if (flags & DUK__BUFOBJ_FLAG_THROW) {
        DUK_ERROR_TYPE(thr, DUK_STR_NOT_BUFFER);
        DUK_WO_NORETURN(return NULL;);
    }
    return NULL;
}

/* Check that 'this' is a duk_hbufobj and return a pointer to it. */
DUK_LOCAL duk_hbufobj *duk__get_bufobj_this(duk_hthread *thr) {
    return (duk_hbufobj *) duk__getrequire_bufobj_this(thr, DUK__BUFOBJ_FLAG_PROMOTE);
}

/* Check that 'this' is a duk_hbufobj and return a pointer to it
 * (NULL if not).
 */
DUK_LOCAL duk_hbufobj *duk__require_bufobj_this(duk_hthread *thr) {
    return (duk_hbufobj *) duk__getrequire_bufobj_this(thr, DUK__BUFOBJ_FLAG_THROW | DUK__BUFOBJ_FLAG_PROMOTE);
}

/* Check that value is a duk_hbufobj and return a pointer to it. */
DUK_LOCAL duk_hbufobj *duk__require_bufobj_value(duk_hthread *thr, duk_idx_t idx) {
    duk_tval *tv;
    duk_hbufobj *h_obj;

    /* Don't accept relative indices now. */
    DUK_ASSERT(idx >= 0);

    tv = duk_require_tval(thr, idx);
    DUK_ASSERT(tv != NULL);
    if (DUK_TVAL_IS_OBJECT(tv)) {
        h_obj = (duk_hbufobj *) DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(h_obj != NULL);
        if (DUK_HOBJECT_IS_BUFOBJ((duk_hobject *) h_obj)) {
            DUK_HBUFOBJ_ASSERT_VALID(h_obj);
            return h_obj;
        }
    } else if (DUK_TVAL_IS_BUFFER(tv)) {
        h_obj = (duk_hbufobj *) duk_to_hobject(thr, idx);
        DUK_ASSERT(h_obj != NULL);
        DUK_HBUFOBJ_ASSERT_VALID(h_obj);
        return h_obj;
    }

    DUK_ERROR_TYPE(thr, DUK_STR_NOT_BUFFER);
    DUK_WO_NORETURN(return NULL;);
}

DUK_LOCAL void duk__set_bufobj_buffer(duk_hthread *thr, duk_hbufobj *h_bufobj, duk_hbuffer *h_val) {
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(h_bufobj != NULL);
    DUK_ASSERT(h_bufobj->buf == NULL);  /* no need to decref */
    DUK_ASSERT(h_val != NULL);
    DUK_HBUFOBJ_ASSERT_VALID(h_bufobj);
    DUK_UNREF(thr);

    h_bufobj->buf = h_val;
    DUK_HBUFFER_INCREF(thr, h_val);
    h_bufobj->length = (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_val);
    DUK_ASSERT(h_bufobj->shift == 0);
    DUK_ASSERT(h_bufobj->elem_type == DUK_HBUFOBJ_ELEM_UINT8);
    DUK_ASSERT(h_bufobj->is_typedarray == 0);

    DUK_HBUFOBJ_ASSERT_VALID(h_bufobj);
}

/* Shared offset/length coercion helper. */
DUK_LOCAL void duk__resolve_offset_opt_length(duk_hthread *thr,
                                              duk_hbufobj *h_bufarg,
                                              duk_idx_t idx_offset,
                                              duk_idx_t idx_length,
                                              duk_uint_t *out_offset,
                                              duk_uint_t *out_length,
                                              duk_bool_t throw_flag) {
    duk_int_t offset_signed;
    duk_int_t length_signed;
    duk_uint_t offset;
    duk_uint_t length;

    offset_signed = duk_to_int(thr, idx_offset);
    if (offset_signed < 0) {
        goto fail_range;
    }
    offset = (duk_uint_t) offset_signed;
    if (offset > h_bufarg->length) {
        goto fail_range;
    }
    DUK_ASSERT_DISABLE(offset >= 0);  /* unsigned */
    DUK_ASSERT(offset <= h_bufarg->length);

    if (duk_is_undefined(thr, idx_length)) {
        DUK_ASSERT(h_bufarg->length >= offset);
        length = h_bufarg->length - offset;  /* >= 0 */
    } else {
        length_signed = duk_to_int(thr, idx_length);
        if (length_signed < 0) {
            goto fail_range;
        }
        length = (duk_uint_t) length_signed;
        DUK_ASSERT(h_bufarg->length >= offset);
        if (length > h_bufarg->length - offset) {
            /* Unlike for negative arguments, some call sites
             * want length to be clamped if it's positive.
             */
            if (throw_flag) {
                goto fail_range;
            } else {
                length = h_bufarg->length - offset;
            }
        }
    }
    DUK_ASSERT_DISABLE(length >= 0);  /* unsigned */
    DUK_ASSERT(offset + length <= h_bufarg->length);

    *out_offset = offset;
    *out_length = length;
    return;

 fail_range:
    DUK_ERROR_RANGE(thr, DUK_STR_INVALID_ARGS);
    DUK_WO_NORETURN(return;);
}

/* Shared lenient buffer length clamping helper.  No negative indices, no
 * element/byte shifting.
 */
DUK_LOCAL void duk__clamp_startend_nonegidx_noshift(duk_hthread *thr,
                                                    duk_int_t buffer_length,
                                                    duk_idx_t idx_start,
                                                    duk_idx_t idx_end,
                                                    duk_int_t *out_start_offset,
                                                    duk_int_t *out_end_offset) {
    duk_int_t start_offset;
    duk_int_t end_offset;

    DUK_ASSERT(out_start_offset != NULL);
    DUK_ASSERT(out_end_offset != NULL);

    /* undefined coerces to zero which is correct */
    start_offset = duk_to_int_clamped(thr, idx_start, 0, buffer_length);
    if (duk_is_undefined(thr, idx_end)) {
        end_offset = buffer_length;
    } else {
        end_offset = duk_to_int_clamped(thr, idx_end, start_offset, buffer_length);
    }

    DUK_ASSERT(start_offset >= 0);
    DUK_ASSERT(start_offset <= buffer_length);
    DUK_ASSERT(end_offset >= 0);
    DUK_ASSERT(end_offset <= buffer_length);
    DUK_ASSERT(start_offset <= end_offset);

    *out_start_offset = start_offset;
    *out_end_offset = end_offset;
}

/* Shared lenient buffer length clamping helper.  Indices are treated as
 * element indices (though output values are byte offsets) which only
 * really matters for TypedArray views as other buffer object have a zero
 * shift.  Negative indices are counted from end of input slice; crossed
 * indices are clamped to zero length; and final indices are clamped
 * against input slice.  Used for e.g. ArrayBuffer slice().
 */
DUK_LOCAL void duk__clamp_startend_negidx_shifted(duk_hthread *thr,
                                                  duk_int_t buffer_length,
                                                  duk_uint8_t buffer_shift,
                                                  duk_idx_t idx_start,
                                                  duk_idx_t idx_end,
                                                  duk_int_t *out_start_offset,
                                                  duk_int_t *out_end_offset) {
    duk_int_t start_offset;
    duk_int_t end_offset;

    DUK_ASSERT(out_start_offset != NULL);
    DUK_ASSERT(out_end_offset != NULL);

    buffer_length >>= buffer_shift;  /* as (full) elements */

    /* Resolve start/end offset as element indices first; arguments
     * at idx_start/idx_end are element offsets.  Working with element
     * indices first also avoids potential for wrapping.
     */

    start_offset = duk_to_int(thr, idx_start);
    if (start_offset < 0) {
        start_offset = buffer_length + start_offset;
    }
    if (duk_is_undefined(thr, idx_end)) {
        end_offset = buffer_length;
    } else {
        end_offset = duk_to_int(thr, idx_end);
        if (end_offset < 0) {
            end_offset = buffer_length + end_offset;
        }
    }
    /* Note: start_offset/end_offset can still be < 0 here. */

    if (start_offset < 0) {
        start_offset = 0;
    } else if (start_offset > buffer_length) {
        start_offset = buffer_length;
    }
    if (end_offset < start_offset) {
        end_offset = start_offset;
    } else if (end_offset > buffer_length) {
        end_offset = buffer_length;
    }
    DUK_ASSERT(start_offset >= 0);
    DUK_ASSERT(start_offset <= buffer_length);
    DUK_ASSERT(end_offset >= 0);
    DUK_ASSERT(end_offset <= buffer_length);
    DUK_ASSERT(start_offset <= end_offset);

    /* Convert indices to byte offsets. */
    start_offset <<= buffer_shift;
    end_offset <<= buffer_shift;

    *out_start_offset = start_offset;
    *out_end_offset = end_offset;
}

DUK_INTERNAL void duk_hbufobj_promote_plain(duk_hthread *thr, duk_idx_t idx) {
    if (duk_is_buffer(thr, idx)) {
        duk_to_object(thr, idx);
    }
}

DUK_INTERNAL void duk_hbufobj_push_uint8array_from_plain(duk_hthread *thr, duk_hbuffer *h_buf) {
    /* Push Uint8Array which will share the same underlying buffer as
     * the plain buffer argument.  Also create an ArrayBuffer with the
     * same backing for the result .buffer property.
     */

    duk_push_hbuffer(thr, h_buf);
    duk_push_buffer_object(thr, -1, 0, (duk_size_t) DUK_HBUFFER_GET_SIZE(h_buf), DUK_BUFOBJ_UINT8ARRAY);
    duk_remove_m2(thr);

#if 0
    /* More verbose equivalent; maybe useful if e.g. .buffer is omitted. */
    h_bufobj = duk_push_bufobj_raw(thr,
                                   DUK_HOBJECT_FLAG_EXTENSIBLE |
                                   DUK_HOBJECT_FLAG_BUFOBJ |
                                   DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_UINT8ARRAY),
                                   DUK_BIDX_UINT8ARRAY_PROTOTYPE);
    DUK_ASSERT(h_bufobj != NULL);
    duk__set_bufobj_buffer(thr, h_bufobj, h_buf);
    h_bufobj->is_typedarray = 1;
    DUK_HBUFOBJ_ASSERT_VALID(h_bufobj);

    h_arrbuf = duk_push_bufobj_raw(thr,
                                   DUK_HOBJECT_FLAG_EXTENSIBLE |
                                   DUK_HOBJECT_FLAG_BUFOBJ |
                                   DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER),
                                   DUK_BIDX_ARRAYBUFFER_PROTOTYPE);
    DUK_ASSERT(h_arrbuf != NULL);
    duk__set_bufobj_buffer(thr, h_arrbuf, h_buf);
    DUK_ASSERT(h_arrbuf->is_typedarray == 0);
    DUK_HBUFOBJ_ASSERT_VALID(h_arrbuf);

    DUK_ASSERT(h_bufobj->buf_prop == NULL);
    h_bufobj->buf_prop = (duk_hobject *) h_arrbuf;
    DUK_ASSERT(h_arrbuf != NULL);
    DUK_HBUFOBJ_INCREF(thr, h_arrbuf);
    duk_pop(thr);
#endif
}

/* Indexed read helper for buffer objects, also called from outside this file. */
DUK_INTERNAL void duk_hbufobj_push_validated_read(duk_hthread *thr, duk_hbufobj *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size) {
    duk_double_union du;

    DUK_ASSERT(elem_size > 0);
    duk_memcpy((void *) du.uc, (const void *) p, (size_t) elem_size);

    switch (h_bufobj->elem_type) {
    case DUK_HBUFOBJ_ELEM_UINT8:
    case DUK_HBUFOBJ_ELEM_UINT8CLAMPED:
        duk_push_uint(thr, (duk_uint_t) du.uc[0]);
        break;
    case DUK_HBUFOBJ_ELEM_INT8:
        duk_push_int(thr, (duk_int_t) (duk_int8_t) du.uc[0]);
        break;
    case DUK_HBUFOBJ_ELEM_UINT16:
        duk_push_uint(thr, (duk_uint_t) du.us[0]);
        break;
    case DUK_HBUFOBJ_ELEM_INT16:
        duk_push_int(thr, (duk_int_t) (duk_int16_t) du.us[0]);
        break;
    case DUK_HBUFOBJ_ELEM_UINT32:
        duk_push_uint(thr, (duk_uint_t) du.ui[0]);
        break;
    case DUK_HBUFOBJ_ELEM_INT32:
        duk_push_int(thr, (duk_int_t) (duk_int32_t) du.ui[0]);
        break;
    case DUK_HBUFOBJ_ELEM_FLOAT32:
        duk_push_number(thr, (duk_double_t) du.f[0]);
        break;
    case DUK_HBUFOBJ_ELEM_FLOAT64:
        duk_push_number(thr, (duk_double_t) du.d);
        break;
    default:
        DUK_UNREACHABLE();
    }
}

/* Indexed write helper for buffer objects, also called from outside this file. */
DUK_INTERNAL void duk_hbufobj_validated_write(duk_hthread *thr, duk_hbufobj *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size) {
    duk_double_union du;

    /* NOTE! Caller must ensure that any side effects from the
     * coercions below are safe.  If that cannot be guaranteed
     * (which is normally the case), caller must coerce the
     * argument using duk_to_number() before any pointer
     * validations; the result of duk_to_number() always coerces
     * without side effects here.
     */

    switch (h_bufobj->elem_type) {
    case DUK_HBUFOBJ_ELEM_UINT8:
        du.uc[0] = (duk_uint8_t) duk_to_uint32(thr, -1);
        break;
    case DUK_HBUFOBJ_ELEM_UINT8CLAMPED:
        du.uc[0] = (duk_uint8_t) duk_to_uint8clamped(thr, -1);
        break;
    case DUK_HBUFOBJ_ELEM_INT8:
        du.uc[0] = (duk_uint8_t) duk_to_int32(thr, -1);
        break;
    case DUK_HBUFOBJ_ELEM_UINT16:
        du.us[0] = (duk_uint16_t) duk_to_uint32(thr, -1);
        break;
    case DUK_HBUFOBJ_ELEM_INT16:
        du.us[0] = (duk_uint16_t) duk_to_int32(thr, -1);
        break;
    case DUK_HBUFOBJ_ELEM_UINT32:
        du.ui[0] = (duk_uint32_t) duk_to_uint32(thr, -1);
        break;
    case DUK_HBUFOBJ_ELEM_INT32:
        du.ui[0] = (duk_uint32_t) duk_to_int32(thr, -1);
        break;
    case DUK_HBUFOBJ_ELEM_FLOAT32:
        /* A double-to-float cast is undefined behavior in C99 if
         * the cast is out-of-range, so use a helper.  Example:
         * runtime error: value -1e+100 is outside the range of representable values of type 'float'
         */
        du.f[0] = duk_double_to_float_t(duk_to_number_m1(thr));
        break;
    case DUK_HBUFOBJ_ELEM_FLOAT64:
        du.d = (duk_double_t) duk_to_number_m1(thr);
        break;
    default:
        DUK_UNREACHABLE();
    }

    DUK_ASSERT(elem_size > 0);
    duk_memcpy((void *) p, (const void *) du.uc, (size_t) elem_size);
}

/* Helper to create a fixed buffer from argument value at index 0.
 * Node.js and allocPlain() compatible.
 */
DUK_LOCAL duk_hbuffer *duk__hbufobj_fixed_from_argvalue(duk_hthread *thr) {
    duk_int_t len;
    duk_int_t i;
    duk_size_t buf_size;
    duk_uint8_t *buf;

    switch (duk_get_type(thr, 0)) {
    case DUK_TYPE_NUMBER: {
        len = duk_to_int_clamped(thr, 0, 0, DUK_INT_MAX);
        (void) duk_push_fixed_buffer_zero(thr, (duk_size_t) len);
        break;
    }
    case DUK_TYPE_BUFFER: { /* Treat like Uint8Array. */
        goto slow_copy;
    }
    case DUK_TYPE_OBJECT: {
        duk_hobject *h;
        duk_hbufobj *h_bufobj;

        /* For Node.js Buffers "Passing an ArrayBuffer returns a Buffer
         * that shares allocated memory with the given ArrayBuffer."
         * https://nodejs.org/api/buffer.html#buffer_buffer_from_buffer_alloc_and_buffer_allocunsafe
         */

        h = duk_known_hobject(thr, 0);
        if (DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_ARRAYBUFFER) {
            DUK_ASSERT(DUK_HOBJECT_IS_BUFOBJ(h));
            h_bufobj = (duk_hbufobj *) h;
            if (DUK_UNLIKELY(h_bufobj->buf == NULL)) {
                DUK_ERROR_TYPE_INVALID_ARGS(thr);
                DUK_WO_NORETURN(return NULL;);
            }
            if (DUK_UNLIKELY(h_bufobj->offset != 0 || h_bufobj->length != DUK_HBUFFER_GET_SIZE(h_bufobj->buf))) {
                /* No support for ArrayBuffers with slice
                 * offset/length.
                 */
                DUK_ERROR_TYPE_INVALID_ARGS(thr);
                DUK_WO_NORETURN(return NULL;);
            }
            duk_push_hbuffer(thr, h_bufobj->buf);
            return h_bufobj->buf;
        }
        goto slow_copy;
    }
    case DUK_TYPE_STRING: {
        /* ignore encoding for now */
        duk_require_hstring_notsymbol(thr, 0);
        duk_dup_0(thr);
        (void) duk_to_buffer(thr, -1, &buf_size);
        break;
    }
    default:
        DUK_ERROR_TYPE_INVALID_ARGS(thr);
        DUK_WO_NORETURN(return NULL;);
    }

 done:
    DUK_ASSERT(duk_is_buffer(thr, -1));
    return duk_known_hbuffer(thr, -1);

 slow_copy:
    /* XXX: fast path for typed arrays and other buffer objects? */

    (void) duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_LENGTH);
    len = duk_to_int_clamped(thr, -1, 0, DUK_INT_MAX);
    duk_pop(thr);
    buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, (duk_size_t) len);  /* no zeroing, all indices get initialized */
    for (i = 0; i < len; i++) {
        /* XXX: fast path for array or buffer arguments? */
        duk_get_prop_index(thr, 0, (duk_uarridx_t) i);
        buf[i] = (duk_uint8_t) (duk_to_uint32(thr, -1) & 0xffU);
        duk_pop(thr);
    }
    goto done;
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/*
 *  Node.js Buffer constructor
 *
 *  Node.js Buffers are just Uint8Arrays with internal prototype set to
 *  Buffer.prototype so they're handled otherwise the same as Uint8Array.
 *  However, the constructor arguments are very different so a separate
 *  constructor entry point is used.
 */
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_constructor(duk_hthread *thr) {
    duk_hbuffer *h_buf;

    h_buf = duk__hbufobj_fixed_from_argvalue(thr);
    DUK_ASSERT(h_buf != NULL);

    duk_push_buffer_object(thr,
                           -1,
                           0,
                           DUK_HBUFFER_FIXED_GET_SIZE((duk_hbuffer_fixed *) (void *) h_buf),
                           DUK_BUFOBJ_UINT8ARRAY);
    duk_push_hobject_bidx(thr, DUK_BIDX_NODEJS_BUFFER_PROTOTYPE);
    duk_set_prototype(thr, -2);

    /* XXX: a more direct implementation */

    return 1;
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/*
 *  ArrayBuffer, DataView, and TypedArray constructors
 */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_constructor(duk_hthread *thr) {
    duk_hbufobj *h_bufobj;
    duk_hbuffer *h_val;
    duk_int_t len;

    DUK_CTX_ASSERT_VALID(thr);

    duk_require_constructor_call(thr);

    len = duk_to_int(thr, 0);
    if (len < 0) {
        goto fail_length;
    }
    (void) duk_push_fixed_buffer_zero(thr, (duk_size_t) len);
    h_val = (duk_hbuffer *) duk_known_hbuffer(thr, -1);

    h_bufobj = duk_push_bufobj_raw(thr,
                                   DUK_HOBJECT_FLAG_EXTENSIBLE |
                                   DUK_HOBJECT_FLAG_BUFOBJ |
                                   DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER),
                                   DUK_BIDX_ARRAYBUFFER_PROTOTYPE);
    DUK_ASSERT(h_bufobj != NULL);

    duk__set_bufobj_buffer(thr, h_bufobj, h_val);
    DUK_HBUFOBJ_ASSERT_VALID(h_bufobj);

    return 1;

 fail_length:
    DUK_DCERROR_RANGE_INVALID_LENGTH(thr);
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */


/* Format of magic, bits:
 *   0...1: elem size shift (0-3)
 *   2...5: elem type (DUK_HBUFOBJ_ELEM_xxx)
 *
 * XXX: add prototype bidx explicitly to magic instead of using a mapping?
 */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_hthread *thr) {
    duk_tval *tv;
    duk_hobject *h_obj;
    duk_hbufobj *h_bufobj = NULL;
    duk_hbufobj *h_bufarg = NULL;
    duk_hbuffer *h_val;
    duk_small_uint_t magic;
    duk_small_uint_t shift;
    duk_small_uint_t elem_type;
    duk_small_uint_t elem_size;
    duk_small_uint_t class_num;
    duk_small_uint_t proto_bidx;
    duk_uint_t align_mask;
    duk_uint_t elem_length;
    duk_int_t elem_length_signed;
    duk_uint_t byte_length;
    duk_small_uint_t copy_mode;

    /* XXX: The same copy helpers could be shared with at least some
     * buffer functions.
     */

    duk_require_constructor_call(thr);

    /* We could fit built-in index into magic but that'd make the magic
     * number dependent on built-in numbering (genbuiltins.py doesn't
     * handle that yet).  So map both class and prototype from the
     * element type.
     */
    magic = (duk_small_uint_t) duk_get_current_magic(thr);
    shift = magic & 0x03U;               /* bits 0...1: shift */
    elem_type = (magic >> 2) & 0x0fU;    /* bits 2...5: type */
    elem_size = 1U << shift;
    align_mask = elem_size - 1;
    DUK_ASSERT(elem_type < sizeof(duk__buffer_proto_from_elemtype) / sizeof(duk_uint8_t));
    proto_bidx = duk__buffer_proto_from_elemtype[elem_type];
    DUK_ASSERT(proto_bidx < DUK_NUM_BUILTINS);
    DUK_ASSERT(elem_type < sizeof(duk__buffer_class_from_elemtype) / sizeof(duk_uint8_t));
    class_num = duk__buffer_class_from_elemtype[elem_type];

    DUK_DD(DUK_DDPRINT("typedarray constructor, magic=%d, shift=%d, elem_type=%d, "
                       "elem_size=%d, proto_bidx=%d, class_num=%d",
                       (int) magic, (int) shift, (int) elem_type, (int) elem_size,
                       (int) proto_bidx, (int) class_num));

    /* Argument variants.  When the argument is an ArrayBuffer a view to
     * the same buffer is created; otherwise a new ArrayBuffer is always
     * created.
     */

    /* XXX: initial iteration to treat a plain buffer like an ArrayBuffer:
     * coerce to an ArrayBuffer object and use that as .buffer.  The underlying
     * buffer will be the same but result .buffer !== inputPlainBuffer.
     */
    duk_hbufobj_promote_plain(thr, 0);

    tv = duk_get_tval(thr, 0);
    DUK_ASSERT(tv != NULL);  /* arg count */
    if (DUK_TVAL_IS_OBJECT(tv)) {
        h_obj = DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(h_obj != NULL);

        if (DUK_HOBJECT_GET_CLASS_NUMBER(h_obj) == DUK_HOBJECT_CLASS_ARRAYBUFFER) {
            /* ArrayBuffer: unlike any other argument variant, create
             * a view into the existing buffer.
             */

            duk_int_t byte_offset_signed;
            duk_uint_t byte_offset;

            h_bufarg = (duk_hbufobj *) h_obj;

            byte_offset_signed = duk_to_int(thr, 1);
            if (byte_offset_signed < 0) {
                goto fail_arguments;
            }
            byte_offset = (duk_uint_t) byte_offset_signed;
            if (byte_offset > h_bufarg->length ||
                (byte_offset & align_mask) != 0) {
                /* Must be >= 0 and multiple of element size. */
                goto fail_arguments;
            }
            if (duk_is_undefined(thr, 2)) {
                DUK_ASSERT(h_bufarg->length >= byte_offset);
                byte_length = h_bufarg->length - byte_offset;
                if ((byte_length & align_mask) != 0) {
                    /* Must be element size multiple from
                     * start offset to end of buffer.
                     */
                    goto fail_arguments;
                }
                elem_length = (byte_length >> shift);
            } else {
                elem_length_signed = duk_to_int(thr, 2);
                if (elem_length_signed < 0) {
                    goto fail_arguments;
                }
                elem_length = (duk_uint_t) elem_length_signed;
                byte_length = elem_length << shift;
                if ((byte_length >> shift) != elem_length) {
                    /* Byte length would overflow. */
                    /* XXX: easier check with less code? */
                    goto fail_arguments;
                }
                DUK_ASSERT(h_bufarg->length >= byte_offset);
                if (byte_length > h_bufarg->length - byte_offset) {
                    /* Not enough data. */
                    goto fail_arguments;
                }
            }
            DUK_UNREF(elem_length);
            DUK_ASSERT_DISABLE(byte_offset >= 0);
            DUK_ASSERT(byte_offset <= h_bufarg->length);
            DUK_ASSERT_DISABLE(byte_length >= 0);
            DUK_ASSERT(byte_offset + byte_length <= h_bufarg->length);
            DUK_ASSERT((elem_length << shift) == byte_length);

            h_bufobj = duk_push_bufobj_raw(thr,
                                           DUK_HOBJECT_FLAG_EXTENSIBLE |
                                           DUK_HOBJECT_FLAG_BUFOBJ |
                                           DUK_HOBJECT_CLASS_AS_FLAGS(class_num),
                                           (duk_small_int_t) proto_bidx);
            h_val = h_bufarg->buf;
            if (h_val == NULL) {
                DUK_DCERROR_TYPE_INVALID_ARGS(thr);
            }
            h_bufobj->buf = h_val;
            DUK_HBUFFER_INCREF(thr, h_val);
            h_bufobj->offset = h_bufarg->offset + byte_offset;
            h_bufobj->length = byte_length;
            h_bufobj->shift = (duk_uint8_t) shift;
            h_bufobj->elem_type = (duk_uint8_t) elem_type;
            h_bufobj->is_typedarray = 1;
            DUK_HBUFOBJ_ASSERT_VALID(h_bufobj);

            /* Set .buffer to the argument ArrayBuffer. */
            DUK_ASSERT(h_bufobj->buf_prop == NULL);
            h_bufobj->buf_prop = (duk_hobject *) h_bufarg;
            DUK_ASSERT(h_bufarg != NULL);
            DUK_HBUFOBJ_INCREF(thr, h_bufarg);
            return 1;
        } else if (DUK_HOBJECT_IS_BUFOBJ(h_obj)) {
            /* TypedArray (or other non-ArrayBuffer duk_hbufobj).
             * Conceptually same behavior as for an Array-like argument,
             * with a few fast paths.
             */

            h_bufarg = (duk_hbufobj *) h_obj;
            DUK_HBUFOBJ_ASSERT_VALID(h_bufarg);
            elem_length_signed = (duk_int_t) (h_bufarg->length >> h_bufarg->shift);
            if (h_bufarg->buf == NULL) {
                DUK_DCERROR_TYPE_INVALID_ARGS(thr);
            }

            /* Select copy mode.  Must take into account element
             * compatibility and validity of the underlying source
             * buffer.
             */

            DUK_DDD(DUK_DDDPRINT("selecting copy mode for bufobj arg, "
                                 "src byte_length=%ld, src shift=%d, "
                                 "src/dst elem_length=%ld; "
                                 "dst shift=%d -> dst byte_length=%ld",
                                 (long) h_bufarg->length, (int) h_bufarg->shift,
                                 (long) elem_length_signed, (int) shift,
                                 (long) (elem_length_signed << shift)));

            copy_mode = 2;  /* default is explicit index read/write copy */
#if !defined(DUK_USE_PREFER_SIZE)
            /* With a size optimized build copy_mode 2 is enough.
             * Modes 0 and 1 are faster but conceptually the same.
             */
            DUK_ASSERT(elem_type < sizeof(duk__buffer_elemtype_copy_compatible) / sizeof(duk_uint16_t));
            if (DUK_HBUFOBJ_VALID_SLICE(h_bufarg)) {
                if ((duk__buffer_elemtype_copy_compatible[elem_type] & (1 << h_bufarg->elem_type)) != 0) {
                    DUK_DDD(DUK_DDDPRINT("source/target are copy compatible, memcpy"));
                    DUK_ASSERT(shift == h_bufarg->shift);  /* byte sizes will match */
                    copy_mode = 0;
                } else {
                    DUK_DDD(DUK_DDDPRINT("source/target not copy compatible but valid, fast copy"));
                    copy_mode = 1;
                }
            }
#endif  /* !DUK_USE_PREFER_SIZE */
        } else {
            /* Array or Array-like */
            elem_length_signed = (duk_int_t) duk_get_length(thr, 0);
            copy_mode = 2;
        }
    } else {
        /* Non-object argument is simply int coerced, matches
         * V8 behavior (except for "null", which we coerce to
         * 0 but V8 TypeErrors).
         */
        elem_length_signed = duk_to_int(thr, 0);
        copy_mode = 3;
    }
    if (elem_length_signed < 0) {
        goto fail_arguments;
    }
    elem_length = (duk_uint_t) elem_length_signed;
    byte_length = (duk_uint_t) (elem_length << shift);
    if ((byte_length >> shift) != elem_length) {
        /* Byte length would overflow. */
        /* XXX: easier check with less code? */
        goto fail_arguments;
    }

    DUK_DDD(DUK_DDDPRINT("elem_length=%ld, byte_length=%ld",
                         (long) elem_length, (long) byte_length));

    /* ArrayBuffer argument is handled specially above; the rest of the
     * argument variants are handled by shared code below.
     *
     * ArrayBuffer in h_bufobj->buf_prop is intentionally left unset.
     * It will be automatically created by the .buffer accessor on
     * first access.
     */

    /* Push the resulting view object on top of a plain fixed buffer. */
    (void) duk_push_fixed_buffer(thr, byte_length);
    h_val = duk_known_hbuffer(thr, -1);
    DUK_ASSERT(h_val != NULL);

    h_bufobj = duk_push_bufobj_raw(thr,
                                   DUK_HOBJECT_FLAG_EXTENSIBLE |
                                   DUK_HOBJECT_FLAG_BUFOBJ |
                                   DUK_HOBJECT_CLASS_AS_FLAGS(class_num),
                                   (duk_small_int_t) proto_bidx);

    h_bufobj->buf = h_val;
    DUK_HBUFFER_INCREF(thr, h_val);
    DUK_ASSERT(h_bufobj->offset == 0);
    h_bufobj->length = byte_length;
    h_bufobj->shift = (duk_uint8_t) shift;
    h_bufobj->elem_type = (duk_uint8_t) elem_type;
    h_bufobj->is_typedarray = 1;
    DUK_HBUFOBJ_ASSERT_VALID(h_bufobj);

    /* Copy values, the copy method depends on the arguments.
     *
     * Copy mode decision may depend on the validity of the underlying
     * buffer of the source argument; there must be no harmful side effects
     * from there to here for copy_mode to still be valid.
     */
    DUK_DDD(DUK_DDDPRINT("copy mode: %d", (int) copy_mode));
    switch (copy_mode) {
        /* Copy modes 0 and 1 can be omitted in size optimized build,
         * copy mode 2 handles them (but more slowly).
         */
#if !defined(DUK_USE_PREFER_SIZE)
    case 0: {
        /* Use byte copy. */

        duk_uint8_t *p_src;
        duk_uint8_t *p_dst;

        DUK_ASSERT(h_bufobj != NULL);
        DUK_ASSERT(h_bufobj->buf != NULL);
        DUK_ASSERT(DUK_HBUFOBJ_VALID_SLICE(h_bufobj));
        DUK_ASSERT(h_bufarg != NULL);
        DUK_ASSERT(h_bufarg->buf != NULL);
        DUK_ASSERT(DUK_HBUFOBJ_VALID_SLICE(h_bufarg));

        p_dst = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufobj);
        p_src = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufarg);

        DUK_DDD(DUK_DDDPRINT("using memcpy: p_src=%p, p_dst=%p, byte_length=%ld",
                             (void *) p_src, (void *) p_dst, (long) byte_length));

        duk_memcpy_unsafe((void *) p_dst, (const void *) p_src, (size_t) byte_length);
        break;
    }
    case 1: {
        /* Copy values through direct validated reads and writes. */

        duk_small_uint_t src_elem_size;
        duk_small_uint_t dst_elem_size;
        duk_uint8_t *p_src;
        duk_uint8_t *p_src_end;
        duk_uint8_t *p_dst;

        DUK_ASSERT(h_bufobj != NULL);
        DUK_ASSERT(h_bufobj->buf != NULL);
        DUK_ASSERT(DUK_HBUFOBJ_VALID_SLICE(h_bufobj));
        DUK_ASSERT(h_bufarg != NULL);
        DUK_ASSERT(h_bufarg->buf != NULL);
        DUK_ASSERT(DUK_HBUFOBJ_VALID_SLICE(h_bufarg));

        src_elem_size = (duk_small_uint_t) (1U << h_bufarg->shift);
        dst_elem_size = elem_size;

        p_src = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufarg);
        p_dst = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufobj);
        p_src_end = p_src + h_bufarg->length;

        DUK_DDD(DUK_DDDPRINT("using fast copy: p_src=%p, p_src_end=%p, p_dst=%p, "
                             "src_elem_size=%d, dst_elem_size=%d",
                             (void *) p_src, (void *) p_src_end, (void *) p_dst,
                             (int) src_elem_size, (int) dst_elem_size));

        while (p_src != p_src_end) {
            DUK_DDD(DUK_DDDPRINT("fast path per element copy loop: "
                                 "p_src=%p, p_src_end=%p, p_dst=%p",
                                 (void *) p_src, (void *) p_src_end, (void *) p_dst));
            /* A validated read() is always a number, so it's write coercion
             * is always side effect free an won't invalidate pointers etc.
             */
            duk_hbufobj_push_validated_read(thr, h_bufarg, p_src, src_elem_size);
            duk_hbufobj_validated_write(thr, h_bufobj, p_dst, dst_elem_size);
            duk_pop(thr);
            p_src += src_elem_size;
            p_dst += dst_elem_size;
        }
        break;
    }
#endif  /* !DUK_USE_PREFER_SIZE */
    case 2: {
        /* Copy values by index reads and writes.  Let virtual
         * property handling take care of coercion.
         */
        duk_uint_t i;

        DUK_DDD(DUK_DDDPRINT("using slow copy"));

        for (i = 0; i < elem_length; i++) {
            duk_get_prop_index(thr, 0, (duk_uarridx_t) i);
            duk_put_prop_index(thr, -2, (duk_uarridx_t) i);
        }
        break;
    }
    default:
    case 3: {
        /* No copy, leave zero bytes in the buffer.  There's no
         * ambiguity with Float32/Float64 because zero bytes also
         * represent 0.0.
         */

        DUK_DDD(DUK_DDDPRINT("using no copy"));
        break;
    }
    }

    return 1;

 fail_arguments:
    DUK_DCERROR_RANGE_INVALID_ARGS(thr);
}
#else  /* DUK_USE_BUFFEROBJECT_SUPPORT */
/* When bufferobject support is disabled, new Uint8Array() could still be
 * supported to create a plain fixed buffer.  Disabled for now.
 */
#if 0
DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_hthread *thr) {
    duk_int_t elem_length_signed;
    duk_uint_t byte_length;

    /* XXX: The same copy helpers could be shared with at least some
     * buffer functions.
     */

    duk_require_constructor_call(thr);

    elem_length_signed = duk_require_int(thr, 0);
    if (elem_length_signed < 0) {
        goto fail_arguments;
    }
    byte_length = (duk_uint_t) elem_length_signed;

    (void) duk_push_fixed_buffer_zero(thr, (duk_size_t) byte_length);
    return 1;

 fail_arguments:
    DUK_DCERROR_RANGE_INVALID_ARGS(thr);
}
#endif  /* 0 */
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_dataview_constructor(duk_hthread *thr) {
    duk_hbufobj *h_bufarg;
    duk_hbufobj *h_bufobj;
    duk_hbuffer *h_val;
    duk_uint_t offset;
    duk_uint_t length;

    duk_require_constructor_call(thr);

    h_bufarg = duk__require_bufobj_value(thr, 0);
    DUK_ASSERT(h_bufarg != NULL);
    if (DUK_HOBJECT_GET_CLASS_NUMBER((duk_hobject *) h_bufarg) != DUK_HOBJECT_CLASS_ARRAYBUFFER) {
        DUK_DCERROR_TYPE_INVALID_ARGS(thr);
    }

    duk__resolve_offset_opt_length(thr, h_bufarg, 1, 2, &offset, &length, 1 /*throw_flag*/);
    DUK_ASSERT(offset <= h_bufarg->length);
    DUK_ASSERT(offset + length <= h_bufarg->length);

    h_bufobj = duk_push_bufobj_raw(thr,
                                   DUK_HOBJECT_FLAG_EXTENSIBLE |
                                   DUK_HOBJECT_FLAG_BUFOBJ |
                                   DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DATAVIEW),
                                   DUK_BIDX_DATAVIEW_PROTOTYPE);

    h_val = h_bufarg->buf;
    if (h_val == NULL) {
        DUK_DCERROR_TYPE_INVALID_ARGS(thr);
    }
    h_bufobj->buf = h_val;
    DUK_HBUFFER_INCREF(thr, h_val);
    h_bufobj->offset = h_bufarg->offset + offset;
    h_bufobj->length = length;
    DUK_ASSERT(h_bufobj->shift == 0);
    DUK_ASSERT(h_bufobj->elem_type == DUK_HBUFOBJ_ELEM_UINT8);
    DUK_ASSERT(h_bufobj->is_typedarray == 0);

    DUK_ASSERT(h_bufobj->buf_prop == NULL);
    h_bufobj->buf_prop = (duk_hobject *) h_bufarg;
    DUK_ASSERT(h_bufarg != NULL);
    DUK_HBUFOBJ_INCREF(thr, h_bufarg);

    DUK_HBUFOBJ_ASSERT_VALID(h_bufobj);
    return 1;
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/*
 *  ArrayBuffer.isView()
 */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_isview(duk_hthread *thr) {
    duk_hobject *h_obj;
    duk_bool_t ret = 0;

    if (duk_is_buffer(thr, 0)) {
        ret = 1;
    } else {
        h_obj = duk_get_hobject(thr, 0);
        if (h_obj != NULL && DUK_HOBJECT_IS_BUFOBJ(h_obj)) {
            /* DataView needs special casing: ArrayBuffer.isView() is
             * true, but ->is_typedarray is 0.
             */
            ret = ((duk_hbufobj *) h_obj)->is_typedarray ||
                  (DUK_HOBJECT_GET_CLASS_NUMBER(h_obj) == DUK_HOBJECT_CLASS_DATAVIEW);
        }
    }
    duk_push_boolean(thr, ret);
    return 1;
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/*
 *  Uint8Array.allocPlain()
 */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_uint8array_allocplain(duk_hthread *thr) {
    duk__hbufobj_fixed_from_argvalue(thr);
    return 1;
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/*
 *  Uint8Array.plainOf()
 */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_uint8array_plainof(duk_hthread *thr) {
    duk_hbufobj *h_bufobj;

#if !defined(DUK_USE_PREFER_SIZE)
    /* Avoid churn if argument is already a plain buffer. */
    if (duk_is_buffer(thr, 0)) {
        return 1;
    }
#endif

    /* Promotes plain buffers to ArrayBuffers, so for a plain buffer
     * argument we'll create a pointless temporary (but still work
     * correctly).
     */
    h_bufobj = duk__require_bufobj_value(thr, 0);
    if (h_bufobj->buf == NULL) {
        duk_push_undefined(thr);
    } else {
        duk_push_hbuffer(thr, h_bufobj->buf);
    }
    return 1;
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/*
 *  Node.js Buffer: toString([encoding], [start], [end])
 */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tostring(duk_hthread *thr) {
    duk_hbufobj *h_this;
    duk_int_t start_offset, end_offset;
    duk_uint8_t *buf_slice;
    duk_size_t slice_length;

    h_this = duk__get_bufobj_this(thr);
    if (h_this == NULL) {
        /* XXX: happens e.g. when evaluating: String(Buffer.prototype). */
        duk_push_literal(thr, "[object Object]");
        return 1;
    }
    DUK_HBUFOBJ_ASSERT_VALID(h_this);

    /* Ignore encoding for now. */

    duk__clamp_startend_nonegidx_noshift(thr,
                                         (duk_int_t) h_this->length,
                                         1 /*idx_start*/,
                                         2 /*idx_end*/,
                                         &start_offset,
                                         &end_offset);

    slice_length = (duk_size_t) (end_offset - start_offset);
    buf_slice = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, slice_length);  /* all bytes initialized below */
    DUK_ASSERT(buf_slice != NULL);

    /* Neutered or uncovered, TypeError. */
    if (h_this->buf == NULL ||
        !DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_this, (duk_size_t) start_offset + slice_length)) {
        DUK_DCERROR_TYPE_INVALID_ARGS(thr);
    }

    /* XXX: ideally we wouldn't make a copy but a view into the buffer for the
     * decoding process.  Or the decoding helper could be changed to accept
     * the slice info (a buffer pointer is NOT a good approach because guaranteeing
     * its stability is difficult).
     */

    DUK_ASSERT(DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_this, (duk_size_t) start_offset + slice_length));
    duk_memcpy_unsafe((void *) buf_slice,
                      (const void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + start_offset),
                      (size_t) slice_length);

    /* Use the equivalent of: new TextEncoder().encode(this) to convert the
     * string.  Result will be valid UTF-8; non-CESU-8 inputs are currently
     * interpreted loosely.  Value stack convention is a bit odd for now.
     */
    duk_replace(thr, 0);
    duk_set_top(thr, 1);
    return duk_textdecoder_decode_utf8_nodejs(thr);
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/*
 *  Node.js Buffer.prototype: toJSON()
 */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tojson(duk_hthread *thr) {
    duk_hbufobj *h_this;
    duk_uint8_t *buf;
    duk_uint_t i, n;
    duk_tval *tv;

    h_this = duk__require_bufobj_this(thr);
    DUK_ASSERT(h_this != NULL);

    if (h_this->buf == NULL || !DUK_HBUFOBJ_VALID_SLICE(h_this)) {
        /* Serialize uncovered backing buffer as a null; doesn't
         * really matter as long we're memory safe.
         */
        duk_push_null(thr);
        return 1;
    }

    duk_push_object(thr);
    duk_push_hstring_stridx(thr, DUK_STRIDX_UC_BUFFER);
    duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_TYPE);

    /* XXX: uninitialized would be OK */
    DUK_ASSERT_DISABLE((duk_size_t) h_this->length <= (duk_size_t) DUK_UINT32_MAX);
    tv = duk_push_harray_with_size_outptr(thr, (duk_uint32_t) h_this->length);  /* XXX: needs revision with >4G buffers */
    DUK_ASSERT(!duk_is_bare_object(thr, -1));

    DUK_ASSERT(h_this->buf != NULL);
    buf = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this);
    for (i = 0, n = h_this->length; i < n; i++) {
        DUK_TVAL_SET_U32(tv + i, (duk_uint32_t) buf[i]);  /* no need for decref or incref */
    }
    duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_DATA);

    return 1;
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/*
 *  Node.js Buffer.prototype.equals()
 *  Node.js Buffer.prototype.compare()
 *  Node.js Buffer.compare()
 */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_buffer_compare_shared(duk_hthread *thr) {
    duk_small_uint_t magic;
    duk_hbufobj *h_bufarg1;
    duk_hbufobj *h_bufarg2;
    duk_small_int_t comp_res;

    /* XXX: keep support for plain buffers and non-Node.js buffers? */

    magic = (duk_small_uint_t) duk_get_current_magic(thr);
    if (magic & 0x02U) {
        /* Static call style. */
        h_bufarg1 = duk__require_bufobj_value(thr, 0);
        h_bufarg2 = duk__require_bufobj_value(thr, 1);
    } else {
        h_bufarg1 = duk__require_bufobj_this(thr);
        h_bufarg2 = duk__require_bufobj_value(thr, 0);
    }
    DUK_ASSERT(h_bufarg1 != NULL);
    DUK_ASSERT(h_bufarg2 != NULL);

    /* We want to compare the slice/view areas of the arguments.
     * If either slice/view is invalid (underlying buffer is shorter)
     * ensure equals() is false, but otherwise the only thing that
     * matters is to be memory safe.
     */

    if (DUK_HBUFOBJ_VALID_SLICE(h_bufarg1) &&
        DUK_HBUFOBJ_VALID_SLICE(h_bufarg2)) {
        comp_res = duk_js_data_compare((const duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_bufarg1->buf) + h_bufarg1->offset,
                                       (const duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_bufarg2->buf) + h_bufarg2->offset,
                                       (duk_size_t) h_bufarg1->length,
                                       (duk_size_t) h_bufarg2->length);
    } else {
        comp_res = -1;  /* either nonzero value is ok */
    }

    if (magic & 0x01U) {
        /* compare: similar to string comparison but for buffer data. */
        duk_push_int(thr, comp_res);
    } else {
        /* equals */
        duk_push_boolean(thr, (comp_res == 0));
    }

    return 1;
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/*
 *  Node.js Buffer.prototype.fill()
 */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_fill(duk_hthread *thr) {
    duk_hbufobj *h_this;
    const duk_uint8_t *fill_str_ptr;
    duk_size_t fill_str_len;
    duk_uint8_t fill_value;
    duk_int_t fill_offset;
    duk_int_t fill_end;
    duk_size_t fill_length;
    duk_uint8_t *p;

    h_this = duk__require_bufobj_this(thr);
    DUK_ASSERT(h_this != NULL);
    if (h_this->buf == NULL) {
        DUK_DCERROR_TYPE_INVALID_ARGS(thr);
    }

    /* [ value offset end ] */

    if (duk_is_string_notsymbol(thr, 0)) {
        fill_str_ptr = (const duk_uint8_t *) duk_get_lstring(thr, 0, &fill_str_len);
        DUK_ASSERT(fill_str_ptr != NULL);
    } else {
        /* Symbols get ToNumber() coerced and cause TypeError. */
        fill_value = (duk_uint8_t) duk_to_uint32(thr, 0);
        fill_str_ptr = (const duk_uint8_t *) &fill_value;
        fill_str_len = 1;
    }

    /* Fill offset handling is more lenient than in Node.js. */

    duk__clamp_startend_nonegidx_noshift(thr,
                                         (duk_int_t) h_this->length,
                                         1 /*idx_start*/,
                                         2 /*idx_end*/,
                                         &fill_offset,
                                         &fill_end);

    DUK_DDD(DUK_DDDPRINT("fill: fill_value=%02x, fill_offset=%ld, fill_end=%ld, view length=%ld",
                         (unsigned int) fill_value, (long) fill_offset, (long) fill_end, (long) h_this->length));

    DUK_ASSERT(fill_end - fill_offset >= 0);
    DUK_ASSERT(h_this->buf != NULL);

    p = (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + fill_offset);
    fill_length = (duk_size_t) (fill_end - fill_offset);
    if (fill_str_len == 1) {
        /* Handle single character fills as memset() even when
         * the fill data comes from a one-char argument.
         */
        duk_memset_unsafe((void *) p, (int) fill_str_ptr[0], (size_t) fill_length);
    } else if (fill_str_len > 1) {
        duk_size_t i, n, t;

        for (i = 0, n = (duk_size_t) (fill_end - fill_offset), t = 0; i < n; i++) {
            p[i] = fill_str_ptr[t++];
            if (t >= fill_str_len) {
                t = 0;
            }
        }
    } else {
        DUK_DDD(DUK_DDDPRINT("zero size fill pattern, ignore silently"));
    }

    /* Return the Buffer to allow chaining: b.fill(0x11).fill(0x22, 3, 5).toString() */
    duk_push_this(thr);
    return 1;
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/*
 *  Node.js Buffer.prototype.write(string, [offset], [length], [encoding])
 */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_write(duk_hthread *thr) {
    duk_hbufobj *h_this;
    duk_uint_t offset;
    duk_uint_t length;
    const duk_uint8_t *str_data;
    duk_size_t str_len;

    /* XXX: very inefficient support for plain buffers */
    h_this = duk__require_bufobj_this(thr);
    DUK_ASSERT(h_this != NULL);

    /* Argument must be a string, e.g. a buffer is not allowed. */
    str_data = (const duk_uint8_t *) duk_require_lstring_notsymbol(thr, 0, &str_len);

    duk__resolve_offset_opt_length(thr, h_this, 1, 2, &offset, &length, 0 /*throw_flag*/);
    DUK_ASSERT(offset <= h_this->length);
    DUK_ASSERT(offset + length <= h_this->length);

    /* XXX: encoding is ignored now. */

    if (length > str_len) {
        length = (duk_uint_t) str_len;
    }

    if (DUK_HBUFOBJ_VALID_SLICE(h_this)) {
        /* Cannot overlap. */
        duk_memcpy_unsafe((void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + offset),
                          (const void *) str_data,
                          (size_t) length);
    } else {
        DUK_DDD(DUK_DDDPRINT("write() target buffer is not covered, silent ignore"));
    }

    duk_push_uint(thr, length);
    return 1;
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/*
 *  Node.js Buffer.prototype.copy()
 */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_copy(duk_hthread *thr) {
    duk_hbufobj *h_this;
    duk_hbufobj *h_bufarg;
    duk_int_t source_length;
    duk_int_t target_length;
    duk_int_t target_start, source_start, source_end;
    duk_uint_t target_ustart, source_ustart, source_uend;
    duk_uint_t copy_size = 0;

    /* [ targetBuffer targetStart sourceStart sourceEnd ] */

    h_this = duk__require_bufobj_this(thr);
    h_bufarg = duk__require_bufobj_value(thr, 0);
    DUK_ASSERT(h_this != NULL);
    DUK_ASSERT(h_bufarg != NULL);
    source_length = (duk_int_t) h_this->length;
    target_length = (duk_int_t) h_bufarg->length;

    target_start = duk_to_int(thr, 1);
    source_start = duk_to_int(thr, 2);
    if (duk_is_undefined(thr, 3)) {
        source_end = source_length;
    } else {
        source_end = duk_to_int(thr, 3);
    }

    DUK_DDD(DUK_DDDPRINT("checking copy args: target_start=%ld, target_length=%ld, "
                         "source_start=%ld, source_end=%ld, source_length=%ld",
                         (long) target_start, (long) h_bufarg->length,
                         (long) source_start, (long) source_end, (long) source_length));

    /* This behavior mostly mimics Node.js now. */

    if (source_start < 0 || source_end < 0 || target_start < 0) {
        /* Negative offsets cause a RangeError. */
        goto fail_bounds;
    }
    source_ustart = (duk_uint_t) source_start;
    source_uend = (duk_uint_t) source_end;
    target_ustart = (duk_uint_t) target_start;
    if (source_ustart >= source_uend ||  /* crossed offsets or zero size */
        source_ustart >= (duk_uint_t) source_length ||  /* source out-of-bounds (but positive) */
        target_ustart >= (duk_uint_t) target_length) {  /* target out-of-bounds (but positive) */
        goto silent_ignore;
    }
    if (source_uend >= (duk_uint_t) source_length) {
        /* Source end clamped silently to available length. */
        source_uend = (duk_uint_t) source_length;
    }
    copy_size = source_uend - source_ustart;
    if (target_ustart + copy_size > (duk_uint_t) target_length) {
        /* Clamp to target's end if too long.
         *
         * NOTE: there's no overflow possibility in the comparison;
         * both target_ustart and copy_size are >= 0 and based on
         * values in duk_int_t range.  Adding them as duk_uint_t
         * values is then guaranteed not to overflow.
         */
        DUK_ASSERT(target_ustart + copy_size >= target_ustart);  /* no overflow */
        DUK_ASSERT(target_ustart + copy_size >= copy_size);  /* no overflow */
        copy_size = (duk_uint_t) target_length - target_ustart;
    }

    DUK_DDD(DUK_DDDPRINT("making copy: target_ustart=%lu source_ustart=%lu copy_size=%lu",
                         (unsigned long) target_ustart, (unsigned long) source_ustart,
                         (unsigned long) copy_size));

    DUK_ASSERT(copy_size >= 1);
    DUK_ASSERT(source_ustart <= (duk_uint_t) source_length);
    DUK_ASSERT(source_ustart + copy_size <= (duk_uint_t) source_length);
    DUK_ASSERT(target_ustart <= (duk_uint_t) target_length);
    DUK_ASSERT(target_ustart + copy_size <= (duk_uint_t) target_length);

    /* Ensure copy is covered by underlying buffers. */
    DUK_ASSERT(h_bufarg->buf != NULL);  /* length check */
    DUK_ASSERT(h_this->buf != NULL);    /* length check */
    if (DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_bufarg, target_ustart + copy_size) &&
        DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_this, source_ustart + copy_size)) {
        /* Must use memmove() because copy area may overlap (source and target
         * buffer may be the same, or from different slices.
         */
        duk_memmove_unsafe((void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufarg) + target_ustart),
                           (const void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + source_ustart),
                           (size_t) copy_size);
    } else {
        DUK_DDD(DUK_DDDPRINT("buffer copy not covered by underlying buffer(s), ignoring"));
    }

 silent_ignore:
    /* Return value is like write(), number of bytes written.
     * The return value matters because of code like:
     * "off += buf.copy(...)".
         */
    duk_push_uint(thr, copy_size);
    return 1;

 fail_bounds:
    DUK_DCERROR_RANGE_INVALID_ARGS(thr);
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/*
 *  TypedArray.prototype.set()
 *
 *  TypedArray set() is pretty interesting to implement because:
 *
 *    - The source argument may be a plain array or a typedarray.  If the
 *      source is a TypedArray, values are decoded and re-encoded into the
 *      target (not as a plain byte copy).  This may happen even when the
 *      element byte size is the same, e.g. integer values may be re-encoded
 *      into floats.
 *
 *    - Source and target may refer to the same underlying buffer, so that
 *      the set() operation may overlap.  The specification requires that this
 *      must work as if a copy was made before the operation.  Note that this
 *      is NOT a simple memmove() situation because the source and target
 *      byte sizes may be different -- e.g. a 4-byte source (Int8Array) may
 *      expand to a 16-byte target (Uint32Array) so that the target overlaps
 *      the source both from beginning and the end (unlike in typical memmove).
 *
 *    - Even if 'buf' pointers of the source and target differ, there's no
 *      guarantee that their memory areas don't overlap.  This may be the
 *      case with external buffers.
 *
 *  Even so, it is nice to optimize for the common case:
 *
 *    - Source and target separate buffers or non-overlapping.
 *
 *    - Source and target have a compatible type so that a plain byte copy
 *      is possible.  Note that while e.g. uint8 and int8 are compatible
 *      (coercion one way or another doesn't change the byte representation),
 *      e.g. int8 and uint8clamped are NOT compatible when writing int8
 *      values into uint8clamped typedarray (-1 would clamp to 0 for instance).
 *
 *  See test-bi-typedarray-proto-set.js.
 */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_hthread *thr) {
    duk_hbufobj *h_this;
    duk_hobject *h_obj;
    duk_uarridx_t i, n;
    duk_int_t offset_signed;
    duk_uint_t offset_elems;
    duk_uint_t offset_bytes;

    h_this = duk__require_bufobj_this(thr);
    DUK_ASSERT(h_this != NULL);
    DUK_HBUFOBJ_ASSERT_VALID(h_this);

    if (h_this->buf == NULL) {
        DUK_DDD(DUK_DDDPRINT("source neutered, skip copy"));
        return 0;
    }

    duk_hbufobj_promote_plain(thr, 0);
    h_obj = duk_require_hobject(thr, 0);

    /* XXX: V8 throws a TypeError for negative values.  Would it
     * be more useful to interpret negative offsets here from the
     * end of the buffer too?
     */
    offset_signed = duk_to_int(thr, 1);
    if (offset_signed < 0) {
        /* For some reason this is a TypeError (at least in V8). */
        DUK_DCERROR_TYPE_INVALID_ARGS(thr);
    }
    offset_elems = (duk_uint_t) offset_signed;
    offset_bytes = offset_elems << h_this->shift;
    if ((offset_bytes >> h_this->shift) != offset_elems) {
        /* Byte length would overflow. */
        /* XXX: easier check with less code? */
        goto fail_args;
    }
    if (offset_bytes > h_this->length) {
        /* Equality may be OK but >length not.  Checking
         * this explicitly avoids some overflow cases
         * below.
         */
        goto fail_args;
    }
    DUK_ASSERT(offset_bytes <= h_this->length);

    /* Fast path: source is a TypedArray (or any bufobj). */

    if (DUK_HOBJECT_IS_BUFOBJ(h_obj)) {
        duk_hbufobj *h_bufarg;
#if !defined(DUK_USE_PREFER_SIZE)
        duk_uint16_t comp_mask;
#endif
        duk_small_int_t no_overlap = 0;
        duk_uint_t src_length;
        duk_uint_t dst_length;
        duk_uint_t dst_length_elems;
        duk_uint8_t *p_src_base;
        duk_uint8_t *p_src_end;
        duk_uint8_t *p_src;
        duk_uint8_t *p_dst_base;
        duk_uint8_t *p_dst;
        duk_small_uint_t src_elem_size;
        duk_small_uint_t dst_elem_size;

        h_bufarg = (duk_hbufobj *) h_obj;
        DUK_HBUFOBJ_ASSERT_VALID(h_bufarg);

        if (h_bufarg->buf == NULL) {
            DUK_DDD(DUK_DDDPRINT("target neutered, skip copy"));
            return 0;
        }

        /* Nominal size check. */
        src_length = h_bufarg->length;  /* bytes in source */
        dst_length_elems = (src_length >> h_bufarg->shift);  /* elems in source and dest */
        dst_length = dst_length_elems << h_this->shift;  /* bytes in dest */
        if ((dst_length >> h_this->shift) != dst_length_elems) {
            /* Byte length would overflow. */
            /* XXX: easier check with less code? */
            goto fail_args;
        }
        DUK_DDD(DUK_DDDPRINT("nominal size check: src_length=%ld, dst_length=%ld",
                             (long) src_length, (long) dst_length));
        DUK_ASSERT(offset_bytes <= h_this->length);
        if (dst_length > h_this->length - offset_bytes) {
            /* Overflow not an issue because subtraction is used on the right
             * side and guaranteed to be >= 0.
             */
            DUK_DDD(DUK_DDDPRINT("copy exceeds target buffer nominal length"));
            goto fail_args;
        }
        if (!DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_this, offset_bytes + dst_length)) {
            DUK_DDD(DUK_DDDPRINT("copy not covered by underlying target buffer, ignore"));
            return 0;
        }

        p_src_base = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufarg);
        p_dst_base = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + offset_bytes;

        /* Check actual underlying buffers for validity and that they
         * cover the copy.  No side effects are allowed after the check
         * so that the validity status doesn't change.
         */
        if (!DUK_HBUFOBJ_VALID_SLICE(h_this) ||
            !DUK_HBUFOBJ_VALID_SLICE(h_bufarg)) {
            /* The condition could be more narrow and check for the
             * copy area only, but there's no need for fine grained
             * behavior when the underlying buffer is misconfigured.
             */
            DUK_DDD(DUK_DDDPRINT("source and/or target not covered by underlying buffer, skip copy"));
            return 0;
        }

        /* We want to do a straight memory copy if possible: this is
         * an important operation because .set() is the TypedArray
         * way to copy chunks of memory.  However, because set()
         * conceptually works in terms of elements, not all views are
         * compatible with direct byte copying.
         *
         * If we do manage a direct copy, the "overlap issue" handled
         * below can just be solved using memmove() because the source
         * and destination element sizes are necessarily equal.
         */

#if !defined(DUK_USE_PREFER_SIZE)
        DUK_ASSERT(h_this->elem_type < sizeof(duk__buffer_elemtype_copy_compatible) / sizeof(duk_uint16_t));
        comp_mask = duk__buffer_elemtype_copy_compatible[h_this->elem_type];
        if (comp_mask & (1 << h_bufarg->elem_type)) {
            DUK_ASSERT(src_length == dst_length);

            DUK_DDD(DUK_DDDPRINT("fast path: able to use memmove() because views are compatible"));
            duk_memmove_unsafe((void *) p_dst_base, (const void *) p_src_base, (size_t) dst_length);
            return 0;
        }
        DUK_DDD(DUK_DDDPRINT("fast path: views are not compatible with a byte copy, copy by item"));
#endif  /* !DUK_USE_PREFER_SIZE */

        /* We want to avoid making a copy to process set() but that's
         * not always possible: the source and the target may overlap
         * and because element sizes are different, the overlap cannot
         * always be handled with a memmove() or choosing the copy
         * direction in a certain way.  For example, if source type is
         * uint8 and target type is uint32, the target area may exceed
         * the source area from both ends!
         *
         * Note that because external buffers may point to the same
         * memory areas, we must ultimately make this check using
         * pointers.
         *
         * NOTE: careful with side effects: any side effect may cause
         * a buffer resize (or external buffer pointer/length update)!
         */

        DUK_DDD(DUK_DDDPRINT("overlap check: p_src_base=%p, src_length=%ld, "
                             "p_dst_base=%p, dst_length=%ld",
                             (void *) p_src_base, (long) src_length,
                             (void *) p_dst_base, (long) dst_length));

        if (p_src_base >= p_dst_base + dst_length ||  /* source starts after dest ends */
            p_src_base + src_length <= p_dst_base) {   /* source ends before dest starts */
            no_overlap = 1;
        }

        if (!no_overlap) {
            /* There's overlap: the desired end result is that
             * conceptually a copy is made to avoid "trampling"
             * of source data by destination writes.  We make
             * an actual temporary copy to handle this case.
             */
            duk_uint8_t *p_src_copy;

            DUK_DDD(DUK_DDDPRINT("there is overlap, make a copy of the source"));
            p_src_copy = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, src_length);
            DUK_ASSERT(p_src_copy != NULL);
            duk_memcpy_unsafe((void *) p_src_copy, (const void *) p_src_base, (size_t) src_length);

            p_src_base = p_src_copy;  /* use p_src_base from now on */
        }
        /* Value stack intentionally mixed size here. */

        DUK_DDD(DUK_DDDPRINT("after overlap check: p_src_base=%p, src_length=%ld, "
                             "p_dst_base=%p, dst_length=%ld, valstack top=%ld",
                             (void *) p_src_base, (long) src_length,
                             (void *) p_dst_base, (long) dst_length,
                             (long) duk_get_top(thr)));

        /* Ready to make the copy.  We must proceed element by element
         * and must avoid any side effects that might cause the buffer
         * validity check above to become invalid.
         *
         * Although we work through the value stack here, only plain
         * numbers are handled which should be side effect safe.
         */

        src_elem_size = (duk_small_uint_t) (1U << h_bufarg->shift);
        dst_elem_size = (duk_small_uint_t) (1U << h_this->shift);
        p_src = p_src_base;
        p_dst = p_dst_base;
        p_src_end = p_src_base + src_length;

        while (p_src != p_src_end) {
            DUK_DDD(DUK_DDDPRINT("fast path per element copy loop: "
                                 "p_src=%p, p_src_end=%p, p_dst=%p",
                                 (void *) p_src, (void *) p_src_end, (void *) p_dst));
            /* A validated read() is always a number, so it's write coercion
             * is always side effect free an won't invalidate pointers etc.
             */
            duk_hbufobj_push_validated_read(thr, h_bufarg, p_src, src_elem_size);
            duk_hbufobj_validated_write(thr, h_this, p_dst, dst_elem_size);
            duk_pop(thr);
            p_src += src_elem_size;
            p_dst += dst_elem_size;
        }

        return 0;
    } else {
        /* Slow path: quite slow, but we save space by using the property code
         * to write coerce target values.  We don't need to worry about overlap
         * here because the source is not a TypedArray.
         *
         * We could use the bufobj write coercion helper but since the
         * property read may have arbitrary side effects, full validity checks
         * would be needed for every element anyway.
         */

        n = (duk_uarridx_t) duk_get_length(thr, 0);
        DUK_ASSERT(offset_bytes <= h_this->length);
        if ((n << h_this->shift) > h_this->length - offset_bytes) {
            /* Overflow not an issue because subtraction is used on the right
             * side and guaranteed to be >= 0.
             */
            DUK_DDD(DUK_DDDPRINT("copy exceeds target buffer nominal length"));
            goto fail_args;
        }

        /* There's no need to check for buffer validity status for the
         * target here: the property access code will do that for each
         * element.  Moreover, if we did check the validity here, side
         * effects from reading the source argument might invalidate
         * the results anyway.
         */

        DUK_ASSERT_TOP(thr, 2);
        duk_push_this(thr);

        for (i = 0; i < n; i++) {
            duk_get_prop_index(thr, 0, i);
            duk_put_prop_index(thr, 2, offset_elems + i);
        }
    }

    return 0;

 fail_args:
    DUK_DCERROR_RANGE_INVALID_ARGS(thr);
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/*
 *  Node.js Buffer.prototype.slice([start], [end])
 *  ArrayBuffer.prototype.slice(begin, [end])
 *  TypedArray.prototype.subarray(begin, [end])
 *
 *  The API calls are almost identical; negative indices are counted from end
 *  of buffer, and final indices are clamped (allowing crossed indices).  Main
 *  differences:
 *
 *    - Copy/view behavior; Node.js .slice() and TypedArray .subarray() create
 *      views, ArrayBuffer .slice() creates a copy
 *
 *    - Resulting object has a different class and prototype depending on the
 *      call (or 'this' argument)
 *
 *    - TypedArray .subarray() arguments are element indices, not byte offsets
 *
 *    - Plain buffer argument creates a plain buffer slice
 */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_LOCAL void duk__arraybuffer_plain_slice(duk_hthread *thr, duk_hbuffer *h_val) {
    duk_int_t start_offset, end_offset;
    duk_uint_t slice_length;
    duk_uint8_t *p_copy;
    duk_size_t copy_length;

    duk__clamp_startend_negidx_shifted(thr,
                                       (duk_int_t) DUK_HBUFFER_GET_SIZE(h_val),
                                       0 /*buffer_shift*/,
                                       0 /*idx_start*/,
                                       1 /*idx_end*/,
                                       &start_offset,
                                       &end_offset);
    DUK_ASSERT(end_offset <= (duk_int_t) DUK_HBUFFER_GET_SIZE(h_val));
    DUK_ASSERT(start_offset >= 0);
    DUK_ASSERT(end_offset >= start_offset);
    slice_length = (duk_uint_t) (end_offset - start_offset);

    p_copy = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, (duk_size_t) slice_length);
    DUK_ASSERT(p_copy != NULL);
    copy_length = slice_length;

    duk_memcpy_unsafe((void *) p_copy,
                      (const void *) ((duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_val) + start_offset),
                      copy_length);
}
#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
/* Shared helper for slice/subarray operation.
 * Magic: 0x01=isView, 0x02=copy, 0x04=Node.js Buffer special handling.
 */
DUK_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_hthread *thr) {
    duk_small_int_t magic;
    duk_small_uint_t res_class_num;
    duk_small_int_t res_proto_bidx;
    duk_hbufobj *h_this;
    duk_hbufobj *h_bufobj;
    duk_hbuffer *h_val;
    duk_int_t start_offset, end_offset;
    duk_uint_t slice_length;
    duk_tval *tv;

    /* [ start end ] */

    magic = duk_get_current_magic(thr);

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

    if (DUK_TVAL_IS_BUFFER(tv)) {
        /* For plain buffers return a plain buffer slice. */
        h_val = DUK_TVAL_GET_BUFFER(tv);
        DUK_ASSERT(h_val != NULL);

        if (magic & 0x02) {
            /* Make copy: ArrayBuffer.prototype.slice() uses this. */
            duk__arraybuffer_plain_slice(thr, h_val);
            return 1;
        } else {
            /* View into existing buffer: cannot be done if the
             * result is a plain buffer because there's no slice
             * info.  So return an ArrayBuffer instance; coerce
             * the 'this' binding into an object and behave as if
             * the original call was for an Object-coerced plain
             * buffer (handled automatically by duk__require_bufobj_this()).
             */

            DUK_DDD(DUK_DDDPRINT("slice() doesn't handle view into plain buffer, coerce 'this' to ArrayBuffer object"));
            /* fall through */
        }
    }
    tv = NULL;  /* No longer valid nor needed. */

    h_this = duk__require_bufobj_this(thr);

    /* Slice offsets are element (not byte) offsets, which only matters
     * for TypedArray views, Node.js Buffer and ArrayBuffer have shift
     * zero so byte and element offsets are the same.  Negative indices
     * are counted from end of slice, crossed indices are allowed (and
     * result in zero length result), and final values are clamped
     * against the current slice.  There's intentionally no check
     * against the underlying buffer here.
     */

    duk__clamp_startend_negidx_shifted(thr,
                                       (duk_int_t) h_this->length,
                                       (duk_uint8_t) h_this->shift,
                                       0 /*idx_start*/,
                                       1 /*idx_end*/,
                                       &start_offset,
                                       &end_offset);
    DUK_ASSERT(end_offset >= start_offset);
    DUK_ASSERT(start_offset >= 0);
    DUK_ASSERT(end_offset >= 0);
    slice_length = (duk_uint_t) (end_offset - start_offset);

    /* The resulting buffer object gets the same class and prototype as
     * the buffer in 'this', e.g. if the input is a Uint8Array the
     * result is a Uint8Array; if the input is a Float32Array, the
     * result is a Float32Array.  The result internal prototype should
     * be the default prototype for the class (e.g. initial value of
     * Uint8Array.prototype), not copied from the argument (Duktape 1.x
     * did that).
     *
     * Node.js Buffers have special handling: they're Uint8Arrays as far
     * as the internal class is concerned, so the new Buffer should also
     * be an Uint8Array but inherit from Buffer.prototype.
     */
    res_class_num = DUK_HOBJECT_GET_CLASS_NUMBER((duk_hobject *) h_this);
    DUK_ASSERT(res_class_num >= DUK_HOBJECT_CLASS_BUFOBJ_MIN);  /* type check guarantees */
    DUK_ASSERT(res_class_num <= DUK_HOBJECT_CLASS_BUFOBJ_MAX);
    res_proto_bidx = duk__buffer_proto_from_classnum[res_class_num - DUK_HOBJECT_CLASS_BUFOBJ_MIN];
    if (magic & 0x04) {
        res_proto_bidx = DUK_BIDX_NODEJS_BUFFER_PROTOTYPE;
    }
    h_bufobj = duk_push_bufobj_raw(thr,
                                   DUK_HOBJECT_FLAG_EXTENSIBLE |
                                   DUK_HOBJECT_FLAG_BUFOBJ |
                                   DUK_HOBJECT_CLASS_AS_FLAGS(res_class_num),
                                   res_proto_bidx);
    DUK_ASSERT(h_bufobj != NULL);

    DUK_ASSERT(h_bufobj->length == 0);
    h_bufobj->shift = h_this->shift;  /* inherit */
    h_bufobj->elem_type = h_this->elem_type;  /* inherit */
    h_bufobj->is_typedarray = magic & 0x01;
    DUK_ASSERT(h_bufobj->is_typedarray == 0 || h_bufobj->is_typedarray == 1);

    h_val = h_this->buf;
    if (h_val == NULL) {
        DUK_DCERROR_TYPE_INVALID_ARGS(thr);
    }

    if (magic & 0x02) {
        /* non-zero: make copy */
        duk_uint8_t *p_copy;
        duk_size_t copy_length;

        p_copy = (duk_uint8_t *) duk_push_fixed_buffer_zero(thr, (duk_size_t) slice_length);  /* must be zeroed, not all bytes always copied */
        DUK_ASSERT(p_copy != NULL);

        /* Copy slice, respecting underlying buffer limits; remainder
         * is left as zero.
         */
        copy_length = DUK_HBUFOBJ_CLAMP_BYTELENGTH(h_this, slice_length);
        duk_memcpy_unsafe((void *) p_copy,
                          (const void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + start_offset),
                          copy_length);

        h_val = duk_known_hbuffer(thr, -1);

        h_bufobj->buf = h_val;
        DUK_HBUFFER_INCREF(thr, h_val);
        h_bufobj->length = slice_length;
        DUK_ASSERT(h_bufobj->offset == 0);

        duk_pop(thr);  /* reachable so pop OK */
    } else {
        h_bufobj->buf = h_val;
        DUK_HBUFFER_INCREF(thr, h_val);
        h_bufobj->length = slice_length;
        h_bufobj->offset = h_this->offset + (duk_uint_t) start_offset;

        /* Copy the .buffer property, needed for TypedArray.prototype.subarray().
         *
         * XXX: limit copy only for TypedArray classes specifically?
         */

        DUK_ASSERT(h_bufobj->buf_prop == NULL);
        h_bufobj->buf_prop = h_this->buf_prop;  /* may be NULL */
        DUK_HOBJECT_INCREF_ALLOWNULL(thr, (duk_hobject *) h_bufobj->buf_prop);
    }
    /* unbalanced stack on purpose */

    DUK_HBUFOBJ_ASSERT_VALID(h_bufobj);
    return 1;
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/*
 *  Node.js Buffer.isEncoding()
 */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_encoding(duk_hthread *thr) {
    const char *encoding;

    /* only accept lowercase 'utf8' now. */

    encoding = duk_to_string(thr, 0);
    DUK_ASSERT(duk_is_string(thr, 0));  /* guaranteed by duk_to_string() */
    duk_push_boolean(thr, DUK_STRCMP(encoding, "utf8") == 0);
    return 1;
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/*
 *  Node.js Buffer.isBuffer()
 */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_buffer(duk_hthread *thr) {
    duk_hobject *h;
    duk_hobject *h_proto;
    duk_bool_t ret = 0;

    DUK_ASSERT(duk_get_top(thr) >= 1);  /* nargs */
    h = duk_get_hobject(thr, 0);
    if (h != NULL) {
        h_proto = thr->builtins[DUK_BIDX_NODEJS_BUFFER_PROTOTYPE];
        DUK_ASSERT(h_proto != NULL);

        h = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h);
        if (h != NULL) {
            ret = duk_hobject_prototype_chain_contains(thr, h, h_proto, 0 /*ignore_loop*/);
        }
    }

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

/*
 *  Node.js Buffer.byteLength()
 */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_byte_length(duk_hthread *thr) {
    const char *str;
    duk_size_t len;

    /* At the moment Buffer(<str>) will just use the string bytes as
     * is (ignoring encoding), so we return the string length here
     * unconditionally.
     */

    /* XXX: to be revised; Old Node.js behavior just coerces any buffer
     * values to string:
     * $ node
     * > Buffer.byteLength(new Uint32Array(10))
     * 20
     * > Buffer.byteLength(new Uint32Array(100))
     * 20
     * (The 20 comes from '[object Uint32Array]'.length
     */

    str = duk_to_lstring(thr, 0, &len);
    DUK_UNREF(str);
    duk_push_size_t(thr, len);
    return 1;
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/*
 *  Node.js Buffer.concat()
 */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_concat(duk_hthread *thr) {
    duk_hobject *h_arg;
    duk_uint_t total_length;
    duk_hbufobj *h_bufobj;
    duk_hbufobj *h_bufres;
    duk_hbuffer *h_val;
    duk_uint_t i, n;
    duk_uint8_t *p;
    duk_size_t space_left;
    duk_size_t copy_size;

    /* Node.js accepts only actual Arrays. */
    h_arg = duk_require_hobject(thr, 0);
    if (DUK_HOBJECT_GET_CLASS_NUMBER(h_arg) != DUK_HOBJECT_CLASS_ARRAY) {
        DUK_DCERROR_TYPE_INVALID_ARGS(thr);
    }

    /* Compute result length and validate argument buffers. */
    n = (duk_uint_t) duk_get_length(thr, 0);
    total_length = 0;
    for (i = 0; i < n; i++) {
        /* Neutered checks not necessary here: neutered buffers have
         * zero 'length' so we'll effectively skip them.
         */
        DUK_ASSERT_TOP(thr, 2);  /* [ array totalLength ] */
        duk_get_prop_index(thr, 0, (duk_uarridx_t) i);  /* -> [ array totalLength buf ] */
        h_bufobj = duk__require_bufobj_value(thr, 2);
        DUK_ASSERT(h_bufobj != NULL);
        total_length += h_bufobj->length;
        if (DUK_UNLIKELY(total_length < h_bufobj->length)) {
            DUK_DCERROR_RANGE_INVALID_ARGS(thr);  /* Wrapped. */
        }
        duk_pop(thr);
    }
    /* In Node.js v0.12.1 a 1-element array is special and won't create a
     * copy, this was fixed later so an explicit check no longer needed.
     */

    /* User totalLength overrides a computed length, but we'll check
     * every copy in the copy loop.  Note that duk_to_int() can
     * technically have arbitrary side effects so we need to recheck
     * the buffers in the copy loop.
     */
    if (!duk_is_undefined(thr, 1) && n > 0) {
        /* For n == 0, Node.js ignores totalLength argument and
         * returns a zero length buffer.
         */
        duk_int_t total_length_signed;
        total_length_signed = duk_to_int(thr, 1);
        if (total_length_signed < 0) {
            DUK_DCERROR_RANGE_INVALID_ARGS(thr);
        }
        total_length = (duk_uint_t) total_length_signed;
    }

    h_bufres = duk_push_bufobj_raw(thr,
                                   DUK_HOBJECT_FLAG_EXTENSIBLE |
                                   DUK_HOBJECT_FLAG_BUFOBJ |
                                   DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_UINT8ARRAY),
                                   DUK_BIDX_NODEJS_BUFFER_PROTOTYPE);
    DUK_ASSERT(h_bufres != NULL);

    p = (duk_uint8_t *) duk_push_fixed_buffer_zero(thr, total_length);  /* must be zeroed, all bytes not necessarily written over */
    DUK_ASSERT(p != NULL);
    space_left = (duk_size_t) total_length;

    for (i = 0; i < n; i++) {
        DUK_ASSERT_TOP(thr, 4);  /* [ array totalLength bufres buf ] */

        duk_get_prop_index(thr, 0, (duk_uarridx_t) i);
        h_bufobj = duk__require_bufobj_value(thr, 4);
        DUK_ASSERT(h_bufobj != NULL);

        copy_size = h_bufobj->length;
        if (copy_size > space_left) {
            copy_size = space_left;
        }

        if (h_bufobj->buf != NULL &&
            DUK_HBUFOBJ_VALID_SLICE(h_bufobj)) {
            duk_memcpy_unsafe((void *) p,
                              (const void *) DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufobj),
                              copy_size);
        } else {
            /* Just skip, leaving zeroes in the result. */
            ;
        }
        p += copy_size;
        space_left -= copy_size;

        duk_pop(thr);
    }

    h_val = duk_known_hbuffer(thr, -1);

    duk__set_bufobj_buffer(thr, h_bufres, h_val);
    h_bufres->is_typedarray = 1;
    DUK_HBUFOBJ_ASSERT_VALID(h_bufres);

    duk_pop(thr);  /* pop plain buffer, now reachable through h_bufres */

    return 1;  /* return h_bufres */
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/*
 *  Shared readfield and writefield methods
 *
 *  The readfield/writefield methods need support for endianness and field
 *  types.  All offsets are byte based so no offset shifting is needed.
 */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
/* Format of magic, bits:
 *   0...1: field type; 0=uint8, 1=uint16, 2=uint32, 3=float, 4=double, 5=unused, 6=unused, 7=unused
 *       3: endianness: 0=little, 1=big
 *       4: signed: 1=yes, 0=no
 *       5: typedarray: 1=yes, 0=no
 */
#define  DUK__FLD_8BIT         0
#define  DUK__FLD_16BIT        1
#define  DUK__FLD_32BIT        2
#define  DUK__FLD_FLOAT        3
#define  DUK__FLD_DOUBLE       4
#define  DUK__FLD_VARINT       5
#define  DUK__FLD_BIGENDIAN    (1 << 3)
#define  DUK__FLD_SIGNED       (1 << 4)
#define  DUK__FLD_TYPEDARRAY   (1 << 5)

/* XXX: split into separate functions for each field type? */
DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_hthread *thr) {
    duk_small_uint_t magic = (duk_small_uint_t) duk_get_current_magic(thr);
    duk_small_uint_t magic_ftype;
    duk_small_uint_t magic_bigendian;
    duk_small_uint_t magic_signed;
    duk_small_uint_t magic_typedarray;
    duk_small_uint_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;

    magic_ftype = magic & 0x0007U;
    magic_bigendian = magic & 0x0008U;
    magic_signed = magic & 0x0010U;
    magic_typedarray = magic & 0x0020U;

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

    /* [ offset noAssert                 ], when ftype != DUK__FLD_VARINT */
    /* [ offset fieldByteLength noAssert ], when ftype == DUK__FLD_VARINT */
    /* [ offset 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, 1);  /* 1=little endian */
#else
        endswap = duk_to_boolean(thr, 1);  /* 1=little endian */
#endif
    } else {
        no_assert = duk_to_boolean(thr, (magic_ftype == DUK__FLD_VARINT) ? 2 : 1);
#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.
     * This ensures we can add a small byte length (1-8) to the offset in
     * bound checks and not wrap.
     */
    offset_signed = duk_to_int(thr, 0);
    offset = (duk_uint_t) offset_signed;
    if (offset_signed < 0) {
        goto fail_bounds;
    }

    DUK_DDD(DUK_DDDPRINT("readfield, buffer_length=%ld, offset=%ld, no_assert=%d, "
                         "magic=%04x, magic_fieldtype=%d, magic_bigendian=%d, magic_signed=%d, "
                         "endswap=%u",
                         (long) buffer_length, (long) offset, (int) no_assert,
                         (unsigned int) magic, (int) magic_ftype, (int) (magic_bigendian >> 3),
                         (int) (magic_signed >> 4), (int) endswap));

    /* Update 'buffer_length' to be the effective, safe limit which
     * takes into account the underlying buffer.  This value will be
     * potentially invalidated by any side effect.
     */
    check_length = DUK_HBUFOBJ_CLAMP_BYTELENGTH(h_this, buffer_length);
    DUK_DDD(DUK_DDDPRINT("buffer_length=%ld, check_length=%ld",
                         (long) buffer_length, (long) check_length));

    if (h_this->buf) {
        buf = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this);
    } else {
        /* Neutered.  We could go into the switch-case safely with
         * buf == NULL because check_length == 0.  To avoid scanbuild
         * warnings, fail directly instead.
         */
        DUK_ASSERT(check_length == 0);
        goto fail_neutered;
    }
    DUK_ASSERT(buf != NULL);

    switch (magic_ftype) {
    case DUK__FLD_8BIT: {
        duk_uint8_t tmp;
        if (offset + 1U > check_length) {
            goto fail_bounds;
        }
        tmp = buf[offset];
        if (magic_signed) {
            duk_push_int(thr, (duk_int_t) ((duk_int8_t) tmp));
        } else {
            duk_push_uint(thr, (duk_uint_t) tmp);
        }
        break;
    }
    case DUK__FLD_16BIT: {
        duk_uint16_t tmp;
        if (offset + 2U > check_length) {
            goto fail_bounds;
        }
        duk_memcpy((void *) du.uc, (const void *) (buf + offset), 2);
        tmp = du.us[0];
        if (endswap) {
            tmp = DUK_BSWAP16(tmp);
        }
        if (magic_signed) {
            duk_push_int(thr, (duk_int_t) ((duk_int16_t) tmp));
        } else {
            duk_push_uint(thr, (duk_uint_t) tmp);
        }
        break;
    }
    case DUK__FLD_32BIT: {
        duk_uint32_t tmp;
        if (offset + 4U > check_length) {
            goto fail_bounds;
        }
        duk_memcpy((void *) du.uc, (const void *) (buf + offset), 4);
        tmp = du.ui[0];
        if (endswap) {
            tmp = DUK_BSWAP32(tmp);
        }
        if (magic_signed) {
            duk_push_int(thr, (duk_int_t) ((duk_int32_t) tmp));
        } else {
            duk_push_uint(thr, (duk_uint_t) tmp);
        }
        break;
    }
    case DUK__FLD_FLOAT: {
        duk_uint32_t tmp;
        if (offset + 4U > check_length) {
            goto fail_bounds;
        }
        duk_memcpy((void *) du.uc, (const void *) (buf + offset), 4);
        if (endswap) {
            tmp = du.ui[0];
            tmp = DUK_BSWAP32(tmp);
            du.ui[0] = tmp;
        }
        duk_push_number(thr, (duk_double_t) du.f[0]);
        break;
    }
    case DUK__FLD_DOUBLE: {
        if (offset + 8U > check_length) {
            goto fail_bounds;
        }
        duk_memcpy((void *) du.uc, (const void *) (buf + offset), 8);
        if (endswap) {
            DUK_DBLUNION_BSWAP64(&du);
        }
        duk_push_number(thr, (duk_double_t) du.d);
        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;
        duk_small_uint_t shift_tmp;
#else
        duk_double_t tmp;
        duk_small_int_t highbyte;
#endif
        const duk_uint8_t *p;

        field_bytelen = duk_get_int(thr, 1);  /* avoid side effects! */
        if (field_bytelen < 1 || field_bytelen > 6) {
            goto fail_field_length;
        }
        if (offset + (duk_uint_t) field_bytelen > check_length) {
            goto fail_bounds;
        }
        p = (const duk_uint8_t *) (buf + offset);

        /* Slow gathering of value using either 64-bit arithmetic
         * or IEEE doubles if 64-bit types not available.  Handling
         * of negative numbers is a bit non-obvious in both cases.
         */

        if (magic_bigendian) {
            /* Gather in big endian */
            i = 0;
            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_uint_t magic = (duk_small_uint_t) duk_get_current_magic(thr);
    duk_small_uint_t magic_ftype;
    duk_small_uint_t magic_bigendian;
    duk_small_uint_t magic_signed;
    duk_small_uint_t magic_typedarray;
    duk_small_uint_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 & 0x0007U;
    magic_bigendian = magic & 0x0008U;
    magic_signed = magic & 0x0010U;
    magic_typedarray = magic & 0x0020U;
    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.
     * This ensures we can add a small byte length (1-8) to the offset in
     * bound checks and not wrap.
     */
    offset_signed = duk_to_int(thr, 1);
    offset = (duk_uint_t) offset_signed;

    /* We need 'nbytes' even for a failed offset; return value must be
     * (offset + nbytes) even when write fails due to invalid offset.
     */
    if (magic_ftype != DUK__FLD_VARINT) {
        DUK_ASSERT(magic_ftype < (duk_small_uint_t) (sizeof(duk__buffer_nbytes_from_fldtype) / sizeof(duk_uint8_t)));
        nbytes = duk__buffer_nbytes_from_fldtype[magic_ftype];
    } else {
        nbytes = duk_get_int(thr, 2);
        if (nbytes < 1 || nbytes > 6) {
            goto fail_field_length;
        }
    }
    DUK_ASSERT(nbytes >= 1 && nbytes <= 8);

    /* Now we can check offset validity. */
    if (offset_signed < 0) {
        goto fail_bounds;
    }

    DUK_DDD(DUK_DDDPRINT("writefield, value=%!T, buffer_length=%ld, offset=%ld, no_assert=%d, "
                         "magic=%04x, magic_fieldtype=%d, magic_bigendian=%d, magic_signed=%d, "
                         "endswap=%u",
                         duk_get_tval(thr, 0), (long) buffer_length, (long) offset, (int) no_assert,
                         (unsigned int) magic, (int) magic_ftype, (int) (magic_bigendian >> 3),
                         (int) (magic_signed >> 4), (int) endswap));

    /* Coerce value to a number before computing check_length, so that
     * the field type specific coercion below can't have side effects
     * that would invalidate check_length.
     */
    duk_to_number(thr, 0);

    /* Update 'buffer_length' to be the effective, safe limit which
     * takes into account the underlying buffer.  This value will be
     * potentially invalidated by any side effect.
     */
    check_length = DUK_HBUFOBJ_CLAMP_BYTELENGTH(h_this, buffer_length);
    DUK_DDD(DUK_DDDPRINT("buffer_length=%ld, check_length=%ld",
                         (long) buffer_length, (long) check_length));

    if (h_this->buf) {
        buf = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this);
    } else {
        /* Neutered.  We could go into the switch-case safely with
         * buf == NULL because check_length == 0.  To avoid scanbuild
         * warnings, fail directly instead.
         */
        DUK_ASSERT(check_length == 0);
        goto fail_neutered;
    }
    DUK_ASSERT(buf != NULL);

    switch (magic_ftype) {
    case DUK__FLD_8BIT: {
        if (offset + 1U > check_length) {
            goto fail_bounds;
        }
        /* sign doesn't matter when writing */
        buf[offset] = (duk_uint8_t) duk_to_uint32(thr, 0);
        break;
    }
    case DUK__FLD_16BIT: {
        duk_uint16_t tmp;
        if (offset + 2U > check_length) {
            goto fail_bounds;
        }
        tmp = (duk_uint16_t) duk_to_uint32(thr, 0);
        if (endswap) {
            tmp = DUK_BSWAP16(tmp);
        }
        du.us[0] = tmp;
        /* sign doesn't matter when writing */
        duk_memcpy((void *) (buf + offset), (const void *) du.uc, 2);
        break;
    }
    case DUK__FLD_32BIT: {
        duk_uint32_t tmp;
        if (offset + 4U > check_length) {
            goto fail_bounds;
        }
        tmp = (duk_uint32_t) duk_to_uint32(thr, 0);
        if (endswap) {
            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_FLOAT: {
        duk_uint32_t tmp;
        if (offset + 4U > check_length) {
            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);
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/*
 *  Accessors for .buffer, .byteLength, .byteOffset
 */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_LOCAL duk_hbufobj *duk__autospawn_arraybuffer(duk_hthread *thr, duk_hbuffer *h_buf) {
    duk_hbufobj *h_res;

    h_res = duk_push_bufobj_raw(thr,
                                DUK_HOBJECT_FLAG_EXTENSIBLE |
                                DUK_HOBJECT_FLAG_BUFOBJ |
                                DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER),
                                DUK_BIDX_ARRAYBUFFER_PROTOTYPE);
    DUK_ASSERT(h_res != NULL);
    DUK_UNREF(h_res);

    duk__set_bufobj_buffer(thr, h_res, h_buf);
    DUK_HBUFOBJ_ASSERT_VALID(h_res);
    DUK_ASSERT(h_res->buf_prop == NULL);
    return h_res;
}

DUK_INTERNAL duk_ret_t duk_bi_typedarray_buffer_getter(duk_hthread *thr) {
    duk_hbufobj *h_bufobj;

    h_bufobj = (duk_hbufobj *) duk__getrequire_bufobj_this(thr, DUK__BUFOBJ_FLAG_THROW /*flags*/);
    DUK_ASSERT(h_bufobj != NULL);
    if (DUK_HEAPHDR_IS_BUFFER((duk_heaphdr *) h_bufobj)) {
        DUK_DD(DUK_DDPRINT("autospawn ArrayBuffer for plain buffer"));
        (void) duk__autospawn_arraybuffer(thr, (duk_hbuffer *) h_bufobj);
        return 1;
    } else {
        if (h_bufobj->buf_prop == NULL &&
            DUK_HOBJECT_GET_CLASS_NUMBER((duk_hobject *) h_bufobj) != DUK_HOBJECT_CLASS_ARRAYBUFFER &&
            h_bufobj->buf != NULL) {
            duk_hbufobj *h_arrbuf;

            DUK_DD(DUK_DDPRINT("autospawn ArrayBuffer for typed array or DataView"));
            h_arrbuf = duk__autospawn_arraybuffer(thr, h_bufobj->buf);

            if (h_bufobj->buf_prop == NULL) {
                /* Must recheck buf_prop, in case ArrayBuffer
                 * alloc had a side effect which already filled
                 * it!
                 */

                /* Set ArrayBuffer's .byteOffset and .byteLength based
                 * on the view so that Arraybuffer[view.byteOffset]
                 * matches view[0].
                 */
                h_arrbuf->offset = 0;
                DUK_ASSERT(h_bufobj->offset + h_bufobj->length >= h_bufobj->offset);  /* Wrap check on creation. */
                h_arrbuf->length = h_bufobj->offset + h_bufobj->length;
                DUK_ASSERT(h_arrbuf->buf_prop == NULL);

                DUK_ASSERT(h_bufobj->buf_prop == NULL);
                h_bufobj->buf_prop = (duk_hobject *) h_arrbuf;
                DUK_HBUFOBJ_INCREF(thr, h_arrbuf);  /* Now reachable and accounted for. */
            }

            /* Left on stack; pushed for the second time below (OK). */
        }
        if (h_bufobj->buf_prop) {
            duk_push_hobject(thr, h_bufobj->buf_prop);
            return 1;
        }
    }
    return 0;
}

DUK_INTERNAL duk_ret_t duk_bi_typedarray_byteoffset_getter(duk_hthread *thr) {
    duk_hbufobj *h_bufobj;

    h_bufobj = (duk_hbufobj *) duk__getrequire_bufobj_this(thr, DUK__BUFOBJ_FLAG_THROW /*flags*/);
    DUK_ASSERT(h_bufobj != NULL);
    if (DUK_HEAPHDR_IS_BUFFER((duk_heaphdr *) h_bufobj)) {
        duk_push_uint(thr, 0);
    } else {
        /* If neutered must return 0; offset is zeroed during
         * neutering.
         */
        duk_push_uint(thr, h_bufobj->offset);
    }
    return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_typedarray_bytelength_getter(duk_hthread *thr) {
    duk_hbufobj *h_bufobj;

    h_bufobj = (duk_hbufobj *) duk__getrequire_bufobj_this(thr, DUK__BUFOBJ_FLAG_THROW /*flags*/);
    DUK_ASSERT(h_bufobj != NULL);
    if (DUK_HEAPHDR_IS_BUFFER((duk_heaphdr *) h_bufobj)) {
        duk_hbuffer *h_buf;

        h_buf = (duk_hbuffer *) h_bufobj;
        DUK_ASSERT(DUK_HBUFFER_GET_SIZE(h_buf) <= DUK_UINT_MAX);  /* Buffer limits. */
        duk_push_uint(thr, (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_buf));
    } else {
        /* If neutered must return 0; length is zeroed during
         * neutering.
         */
        duk_push_uint(thr, h_bufobj->length);
    }
    return 1;
}
#else  /* DUK_USE_BUFFEROBJECT_SUPPORT */
/* No .buffer getter without ArrayBuffer support. */
#if 0
DUK_INTERNAL duk_ret_t duk_bi_typedarray_buffer_getter(duk_hthread *thr) {
    return 0;
}
#endif

DUK_INTERNAL duk_ret_t duk_bi_typedarray_byteoffset_getter(duk_hthread *thr) {
    duk_push_uint(thr, 0);
    return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_typedarray_bytelength_getter(duk_hthread *thr) {
    duk_hbuffer *h_buf;

    /* XXX: helper? */
    duk_push_this(thr);
    h_buf = duk_require_hbuffer(thr, -1);
    duk_push_uint(thr, DUK_HBUFFER_GET_SIZE(h_buf));
    return 1;
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/* automatic undefs */
#undef DUK__BUFOBJ_FLAG_PROMOTE
#undef DUK__BUFOBJ_FLAG_THROW
#undef DUK__FLD_16BIT
#undef DUK__FLD_32BIT
#undef DUK__FLD_8BIT
#undef DUK__FLD_BIGENDIAN
#undef DUK__FLD_DOUBLE
#undef DUK__FLD_FLOAT
#undef DUK__FLD_SIGNED
#undef DUK__FLD_TYPEDARRAY
#undef DUK__FLD_VARINT
//#line 1 "duk_bi_cbor.c"
/*
 *  CBOR bindings.
 *
 *  http://cbor.io/
 *  https://tools.ietf.org/html/rfc7049
 */

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

#if defined(DUK_USE_CBOR_SUPPORT)

/* #define DUK_CBOR_STRESS */

/* Default behavior for encoding strings: use CBOR text string if string
 * is UTF-8 compatible, otherwise use CBOR byte string.  These defines
 * can be used to force either type for all strings.  Using text strings
 * for non-UTF-8 data is technically invalid CBOR.
 */
/* #define DUK_CBOR_TEXT_STRINGS */
/* #define DUK_CBOR_BYTE_STRINGS */

/* Misc. defines. */
/* #define DUK_CBOR_PREFER_SIZE */
/* #define DUK_CBOR_DOUBLE_AS_IS */
/* #define DUK_CBOR_DECODE_FASTPATH */

typedef struct {
    duk_hthread *thr;
    duk_uint8_t *ptr;
    duk_uint8_t *buf;
    duk_uint8_t *buf_end;
    duk_size_t len;
    duk_idx_t idx_buf;
    duk_uint_t recursion_depth;
    duk_uint_t recursion_limit;
} duk_cbor_encode_context;

typedef struct {
    duk_hthread *thr;
    const duk_uint8_t *buf;
    duk_size_t off;
    duk_size_t len;
    duk_uint_t recursion_depth;
    duk_uint_t recursion_limit;
} duk_cbor_decode_context;

DUK_LOCAL void duk__cbor_encode_value(duk_cbor_encode_context *enc_ctx);
DUK_LOCAL void duk__cbor_decode_value(duk_cbor_decode_context *dec_ctx);

/*
 *  Misc
 */

DUK_LOCAL duk_uint32_t duk__cbor_double_to_uint32(double d) {
    /* Out of range casts are undefined behavior, so caller must avoid. */
    DUK_ASSERT(d >= 0.0 && d <= 4294967295.0);
    return (duk_uint32_t) d;
}

/*
 *  Encoding
 */

DUK_LOCAL void duk__cbor_encode_error(duk_cbor_encode_context *enc_ctx) {
    (void) duk_type_error(enc_ctx->thr, "cbor encode error");
}

DUK_LOCAL void duk__cbor_encode_req_stack(duk_cbor_encode_context *enc_ctx) {
    duk_require_stack(enc_ctx->thr, 4);
}

DUK_LOCAL void duk__cbor_encode_objarr_entry(duk_cbor_encode_context *enc_ctx) {
    duk_hthread *thr = enc_ctx->thr;

    /* Native stack check in object/array recursion. */
    duk_native_stack_check(thr);

    /* When working with deeply recursive structures, this is important
     * to ensure there's no effective depth limit.
     */
    duk__cbor_encode_req_stack(enc_ctx);

    DUK_ASSERT(enc_ctx->recursion_depth <= enc_ctx->recursion_limit);
    if (enc_ctx->recursion_depth >= enc_ctx->recursion_limit) {
        DUK_ERROR_RANGE(thr, DUK_STR_ENC_RECLIMIT);
        DUK_WO_NORETURN(return;);
    }
    enc_ctx->recursion_depth++;
}

DUK_LOCAL void duk__cbor_encode_objarr_exit(duk_cbor_encode_context *enc_ctx) {
    DUK_ASSERT(enc_ctx->recursion_depth > 0);
    enc_ctx->recursion_depth--;
}

/* Check that a size_t is in uint32 range to avoid out-of-range casts. */
DUK_LOCAL void duk__cbor_encode_sizet_uint32_check(duk_cbor_encode_context *enc_ctx, duk_size_t len) {
    if (DUK_UNLIKELY(sizeof(duk_size_t) > sizeof(duk_uint32_t) && len > (duk_size_t) DUK_UINT32_MAX)) {
        duk__cbor_encode_error(enc_ctx);
    }
}

DUK_LOCAL DUK_NOINLINE void duk__cbor_encode_ensure_slowpath(duk_cbor_encode_context *enc_ctx, duk_size_t len) {
    duk_size_t oldlen;
    duk_size_t minlen;
    duk_size_t newlen;
    duk_uint8_t *p_new;
    duk_size_t old_data_len;

    DUK_ASSERT(enc_ctx->ptr >= enc_ctx->buf);
    DUK_ASSERT(enc_ctx->buf_end >= enc_ctx->ptr);
    DUK_ASSERT(enc_ctx->buf_end >= enc_ctx->buf);

    /* Overflow check.
     *
     * Limit example: 0xffffffffUL / 2U = 0x7fffffffUL, we reject >= 0x80000000UL.
     */
    oldlen = enc_ctx->len;
    minlen = oldlen + len;
    if (DUK_UNLIKELY(oldlen > DUK_SIZE_MAX / 2U || minlen < oldlen)) {
        duk__cbor_encode_error(enc_ctx);
    }

#if defined(DUK_CBOR_STRESS)
    newlen = oldlen + 1U;
#else
    newlen = oldlen * 2U;
#endif
    DUK_ASSERT(newlen >= oldlen);

    if (minlen > newlen) {
        newlen = minlen;
    }
    DUK_ASSERT(newlen >= oldlen);
    DUK_ASSERT(newlen >= minlen);
    DUK_ASSERT(newlen > 0U);

    DUK_DD(DUK_DDPRINT("cbor encode buffer resized to %ld", (long) newlen));

    p_new = (duk_uint8_t *) duk_resize_buffer(enc_ctx->thr, enc_ctx->idx_buf, newlen);
    DUK_ASSERT(p_new != NULL);
    old_data_len = (duk_size_t) (enc_ctx->ptr - enc_ctx->buf);
    enc_ctx->buf = p_new;
    enc_ctx->buf_end = p_new + newlen;
    enc_ctx->ptr = p_new + old_data_len;
    enc_ctx->len = newlen;
}

DUK_LOCAL DUK_INLINE void duk__cbor_encode_ensure(duk_cbor_encode_context *enc_ctx, duk_size_t len) {
    if (DUK_LIKELY((duk_size_t) (enc_ctx->buf_end - enc_ctx->ptr) >= len)) {
        return;
    }
    duk__cbor_encode_ensure_slowpath(enc_ctx, len);
}

DUK_LOCAL duk_size_t duk__cbor_get_reserve(duk_cbor_encode_context *enc_ctx) {
    DUK_ASSERT(enc_ctx->ptr >= enc_ctx->buf);
    DUK_ASSERT(enc_ctx->ptr <= enc_ctx->buf_end);
    return (duk_size_t) (enc_ctx->buf_end - enc_ctx->ptr);
}

DUK_LOCAL void duk__cbor_encode_uint32(duk_cbor_encode_context *enc_ctx, duk_uint32_t u, duk_uint8_t base) {
    duk_uint8_t *p;

    /* Caller must ensure space. */
    DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 4);

    p = enc_ctx->ptr;
    if (DUK_LIKELY(u <= 23U)) {
        *p++ = (duk_uint8_t) (base + (duk_uint8_t) u);
    } else if (u <= 0xffUL) {
        *p++ = base + 0x18U;
        *p++ = (duk_uint8_t) u;
    } else if (u <= 0xffffUL) {
        *p++ = base + 0x19U;
        DUK_RAW_WRITEINC_U16_BE(p, (duk_uint16_t) u);
    } else {
        *p++ = base + 0x1aU;
        DUK_RAW_WRITEINC_U32_BE(p, u);
    }
    enc_ctx->ptr = p;
}

#if defined(DUK_CBOR_DOUBLE_AS_IS)
DUK_LOCAL void duk__cbor_encode_double(duk_cbor_encode_context *enc_ctx, double d) {
    duk_uint8_t *p;

    /* Caller must ensure space. */
    DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8);

    p = enc_ctx->ptr;
    *p++ = 0xfbU;
    DUK_RAW_WRITEINC_DOUBLE_BE(p, d);
    p += 8;
    enc_ctx->ptr = p;
}
#else  /* DUK_CBOR_DOUBLE_AS_IS */
DUK_LOCAL void duk__cbor_encode_double_fp(duk_cbor_encode_context *enc_ctx, double d) {
    duk_double_union u;
    duk_uint16_t u16;
    duk_int16_t expt;
    duk_uint8_t *p;

    DUK_ASSERT(DUK_FPCLASSIFY(d) != DUK_FP_ZERO);

    /* Caller must ensure space. */
    DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8);

    /* Organize into little endian (no-op if platform is little endian). */
    u.d = d;
    duk_dblunion_host_to_little(&u);

    /* Check if 'd' can represented as a normal half-float.
     * Denormal half-floats could also be used, but that check
     * isn't done now (denormal half-floats are decoded of course).
     * So just check exponent range and that at most 10 significant
     * bits (excluding implicit leading 1) are used in 'd'.
     */
    u16 = (((duk_uint16_t) u.uc[7]) << 8) | ((duk_uint16_t) u.uc[6]);
    expt = (duk_int16_t) ((u16 & 0x7ff0U) >> 4) - 1023;

    if (expt >= -14 && expt <= 15) {
        /* Half-float normal exponents (excl. denormals).
         *
         *          7        6        5        4        3        2        1        0  (LE index)
         * double: seeeeeee eeeemmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm
         * half:         seeeee mmmm mmmmmm00 00000000 00000000 00000000 00000000 00000000
         */
        duk_bool_t use_half_float;

        use_half_float =
            (u.uc[0] == 0 && u.uc[1] == 0 && u.uc[2] == 0 && u.uc[3] == 0 &&
             u.uc[4] == 0 && (u.uc[5] & 0x03U) == 0);

        if (use_half_float) {
            duk_uint32_t t;

            expt += 15;
            t = (duk_uint32_t) (u.uc[7] & 0x80U) << 8;
            t += (duk_uint32_t) expt << 10;
            t += ((duk_uint32_t) u.uc[6] & 0x0fU) << 6;
            t += ((duk_uint32_t) u.uc[5]) >> 2;

            /* seeeeemm mmmmmmmm */
            p = enc_ctx->ptr;
            *p++ = 0xf9U;
            DUK_RAW_WRITEINC_U16_BE(p, (duk_uint16_t) t);
            enc_ctx->ptr = p;
            return;
        }
    }

    /* Same check for plain float.  Also no denormal support here. */
    if (expt >= -126 && expt <= 127) {
        /* Float normal exponents (excl. denormals).
         *
         * double: seeeeeee eeeemmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm
         * float:     seeee eeeemmmm mmmmmmmm mmmmmmmm mmm00000 00000000 00000000 00000000
         */
        duk_bool_t use_float;
        duk_float_t d_float;

        /* We could do this explicit mantissa check, but doing
         * a double-float-double cast is fine because we've
         * already verified that the exponent is in range so
         * that the narrower cast is not undefined behavior.
         */
#if 0
        use_float =
            (u.uc[0] == 0 && u.uc[1] == 0 && u.uc[2] == 0 && (u.uc[3] & 0xe0U) == 0);
#endif
        d_float = (duk_float_t) d;
        use_float = duk_double_equals((duk_double_t) d_float, d);
        if (use_float) {
            p = enc_ctx->ptr;
            *p++ = 0xfaU;
            DUK_RAW_WRITEINC_FLOAT_BE(p, d_float);
            enc_ctx->ptr = p;
            return;
        }
    }

    /* Special handling for NaN and Inf which we want to encode as
     * half-floats.  They share the same (maximum) exponent.
     */
    if (expt == 1024) {
        DUK_ASSERT(DUK_ISNAN(d) || DUK_ISINF(d));
        p = enc_ctx->ptr;
        *p++ = 0xf9U;
        if (DUK_ISNAN(d)) {
            /* Shortest NaN encoding is using a half-float.  Lose the
             * exact NaN bits in the process.  IEEE double would be
             * 7ff8 0000 0000 0000, i.e. a quiet NaN in most architectures
             * (https://en.wikipedia.org/wiki/NaN#Encoding).  The
             * equivalent half float is 7e00.
             */
            *p++ = 0x7eU;
        } else {
            /* Shortest +/- Infinity encoding is using a half-float. */
            if (DUK_SIGNBIT(d)) {
                *p++ = 0xfcU;
            } else {
                *p++ = 0x7cU;
            }
        }
        *p++ = 0x00U;
        enc_ctx->ptr = p;
        return;
    }

    /* Cannot use half-float or float, encode as full IEEE double. */
    p = enc_ctx->ptr;
    *p++ = 0xfbU;
    DUK_RAW_WRITEINC_DOUBLE_BE(p, d);
    enc_ctx->ptr = p;
}

DUK_LOCAL void duk__cbor_encode_double(duk_cbor_encode_context *enc_ctx, double d) {
    duk_uint8_t *p;
    double d_floor;

    /* Integers and floating point values of all types are conceptually
     * equivalent in CBOR.  Try to always choose the shortest encoding
     * which is not always immediately obvious.  For example, NaN and Inf
     * can be most compactly represented as a half-float (assuming NaN
     * bits are not preserved), and 0x1'0000'0000 as a single precision
     * float.  Shortest forms in preference order (prefer integer over
     * float when equal length):
     *
     *   uint        1 byte    [0,23] (not -0)
     *   sint        1 byte    [-24,-1]
     *   uint+1      2 bytes   [24,255]
     *   sint+1      2 bytes   [-256,-25]
     *   uint+2      3 bytes   [256,65535]
     *   sint+2      3 bytes   [-65536,-257]
     *   half-float  3 bytes   -0, NaN, +/- Infinity, range [-65504,65504]
     *   uint+4      5 bytes   [65536,4294967295]
     *   sint+4      5 bytes   [-4294967296,-258]
     *   float       5 bytes   range [-(1 - 2^(-24)) * 2^128, (1 - 2^(-24)) * 2^128]
     *   uint+8      9 bytes   [4294967296,18446744073709551615]
     *   sint+8      9 bytes   [-18446744073709551616,-4294967297]
     *   double      9 bytes
     *
     * For whole numbers (compatible with integers):
     *   - 1-byte or 2-byte uint/sint representation is preferred for
     *     [-256,255].
     *   - 3-byte uint/sint is preferred for [-65536,65535].  Half floats
     *     are never preferred because they have the same length.
     *   - 5-byte uint/sint is preferred for [-4294967296,4294967295].
     *     Single precision floats are never preferred, and half-floats
     *     don't reach above the 3-byte uint/sint range so they're never
     *     preferred.
     *   - So, for all integers up to signed/unsigned 32-bit range the
     *     preferred encoding is always an integer uint/sint.
     *   - For integers above 32 bits the situation is more complicated.
     *     Half-floats are never useful for them because of their limited
     *     range, but IEEE single precision floats (5 bytes encoded) can
     *     represent some integers between the 32-bit and 64-bit ranges
     *     which require 9 bytes as a uint/sint.
     *
     * For floating point values not compatible with integers, the
     * preferred encoding is quite clear:
     *   - For +Inf/-Inf use half-float.
     *   - For NaN use a half-float, assuming NaN bits ("payload") is
     *     not worth preserving.  Duktape doesn't in general guarantee
     *     preservation of the NaN payload so using a half-float seems
     *     consistent with that.
     *   - For remaining values, prefer the shortest form which doesn't
     *     lose any precision.  For normal half-floats and single precision
     *     floats this is simple: just check exponent and mantissa bits
     *     using a fixed mask.  For denormal half-floats and single
     *     precision floats the check is a bit more complicated: a normal
     *     IEEE double can sometimes be represented as a denormal
     *     half-float or single precision float.
     *
     * https://en.wikipedia.org/wiki/Half-precision_floating-point_format#IEEE_754_half-precision_binary_floating-point_format:_binary16
     */

    /* Caller must ensure space. */
    DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8);

    /* Most important path is integers.  The floor() test will be true
     * for Inf too (but not NaN).
     */
    d_floor = DUK_FLOOR(d);  /* identity if d is +/- 0.0, NaN, or +/- Infinity */
    if (DUK_LIKELY(duk_double_equals(d_floor, d) != 0)) {
        DUK_ASSERT(!DUK_ISNAN(d));  /* NaN == NaN compares false. */
        if (DUK_SIGNBIT(d)) {
            if (d >= -4294967296.0) {
                d = -1.0 - d;
                if (d >= 0.0) {
                    DUK_ASSERT(d >= 0.0);
                    duk__cbor_encode_uint32(enc_ctx, duk__cbor_double_to_uint32(d), 0x20U);
                    return;
                }

                /* Input was negative zero, d == -1.0 < 0.0.
                 * Shortest -0 is using half-float.
                 */
                p = enc_ctx->ptr;
                *p++ = 0xf9U;
                *p++ = 0x80U;
                *p++ = 0x00U;
                enc_ctx->ptr = p;
                return;
            }
        } else {
            if (d <= 4294967295.0) {
                /* Positive zero needs no special handling. */
                DUK_ASSERT(d >= 0.0);
                duk__cbor_encode_uint32(enc_ctx, duk__cbor_double_to_uint32(d), 0x00U);
                return;
            }
        }
    }

    /* 64-bit integers are not supported at present.  So
     * we also don't need to deal with choosing between a
     * 64-bit uint/sint representation vs. IEEE double or
     * float.
     */

    DUK_ASSERT(DUK_FPCLASSIFY(d) != DUK_FP_ZERO);
    duk__cbor_encode_double_fp(enc_ctx, d);
}
#endif  /* DUK_CBOR_DOUBLE_AS_IS */

DUK_LOCAL void duk__cbor_encode_string_top(duk_cbor_encode_context *enc_ctx) {
    const duk_uint8_t *str;
    duk_size_t len;
    duk_uint8_t *p;

    /* CBOR differentiates between UTF-8 text strings and byte strings.
     * Text strings MUST be valid UTF-8, so not all Duktape strings can
     * be encoded as valid CBOR text strings.  Possible behaviors:
     *
     *   1. Use text string when input is valid UTF-8, otherwise use
     *      byte string (maybe tagged to indicate it was an extended
     *      UTF-8 string).
     *   2. Always use text strings, but sanitize input string so that
     *      invalid UTF-8 is replaced with U+FFFD for example.  Combine
     *      surrogates whenever possible.
     *   3. Always use byte strings.  This is simple and produces valid
     *      CBOR, but isn't ideal for interoperability.
     *   4. Always use text strings, even for invalid UTF-8 such as
     *      codepoints in the surrogate pair range.  This is simple but
     *      produces technically invalid CBOR for non-UTF-8 strings which
     *      may affect interoperability.
     *
     * Current default is 1; can be changed with defines.
     */

    /* Caller must ensure space. */
    DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8);

    str = (const duk_uint8_t *) duk_require_lstring(enc_ctx->thr, -1, &len);
    if (duk_is_symbol(enc_ctx->thr, -1)) {
        /* Symbols, encode as an empty table for now.  This matches
         * the behavior of cbor-js.
         *
         * XXX: Maybe encode String() coercion with a tag?
         * XXX: Option to keep enough information to recover
         * Symbols when decoding (this is not always desirable).
         */
        p = enc_ctx->ptr;
        *p++ = 0xa0U;
        enc_ctx->ptr = p;
        return;
    }

    duk__cbor_encode_sizet_uint32_check(enc_ctx, len);
#if defined(DUK_CBOR_TEXT_STRINGS)
    duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x60U);
#elif defined(DUK_CBOR_BYTE_STRINGS)
    duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x40U);
#else
    duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len,
                            (DUK_LIKELY(duk_unicode_is_utf8_compatible(str, len) != 0) ? 0x60U : 0x40U));
#endif
    duk__cbor_encode_ensure(enc_ctx, len);
    p = enc_ctx->ptr;
    duk_memcpy((void *) p, (const void *) str, len);
    p += len;
    enc_ctx->ptr = p;
}

DUK_LOCAL void duk__cbor_encode_object(duk_cbor_encode_context *enc_ctx) {
    duk_uint8_t *buf;
    duk_size_t len;
    duk_uint8_t *p;
    duk_size_t i;
    duk_size_t off_ib;
    duk_uint32_t count;

    /* Caller must ensure space. */
    DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8);

    duk__cbor_encode_objarr_entry(enc_ctx);

    /* XXX: Support for specific built-ins like Date and RegExp. */
    if (duk_is_array(enc_ctx->thr, -1)) {
        /* Shortest encoding for arrays >= 256 in length is actually
         * the indefinite length one (3 or more bytes vs. 2 bytes).
         * We still use the definite length version because it is
         * more decoding friendly.
         */
        len = duk_get_length(enc_ctx->thr, -1);
        duk__cbor_encode_sizet_uint32_check(enc_ctx, len);
        duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x80U);
        for (i = 0; i < len; i++) {
            duk_get_prop_index(enc_ctx->thr, -1, (duk_uarridx_t) i);
            duk__cbor_encode_value(enc_ctx);
        }
    } else if (duk_is_buffer_data(enc_ctx->thr, -1)) {
        /* XXX: Tag buffer data?
         * XXX: Encode typed arrays as integer arrays rather
         * than buffer data as is?
         */
        buf = (duk_uint8_t *) duk_require_buffer_data(enc_ctx->thr, -1, &len);
        duk__cbor_encode_sizet_uint32_check(enc_ctx, len);
        duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x40U);
        duk__cbor_encode_ensure(enc_ctx, len);
        p = enc_ctx->ptr;
        duk_memcpy_unsafe((void *) p, (const void *) buf, len);
        p += len;
        enc_ctx->ptr = p;
    } else {
        /* We don't know the number of properties in advance
         * but would still like to encode at least small
         * objects without indefinite length.  Emit an
         * indefinite length byte initially, and if the final
         * property count is small enough to also fit in one
         * byte, backpatch it later.  Otherwise keep the
         * indefinite length.  This works well up to 23
         * properties which is practical and good enough.
         */
        off_ib = (duk_size_t) (enc_ctx->ptr - enc_ctx->buf);  /* XXX: get_offset? */
        count = 0U;
        p = enc_ctx->ptr;
        *p++ = 0xa0U + 0x1fU;  /* indefinite length */
        enc_ctx->ptr = p;
        duk_enum(enc_ctx->thr, -1, DUK_ENUM_OWN_PROPERTIES_ONLY);
        while (duk_next(enc_ctx->thr, -1, 1 /*get_value*/)) {
            duk_insert(enc_ctx->thr, -2);  /* [ ... key value ] -> [ ... value key ] */
            duk__cbor_encode_value(enc_ctx);
            duk__cbor_encode_value(enc_ctx);
            count++;
            if (count == 0U) {
                duk__cbor_encode_error(enc_ctx);
            }
        }
        duk_pop(enc_ctx->thr);
        if (count <= 0x17U) {
            DUK_ASSERT(off_ib < enc_ctx->len);
            enc_ctx->buf[off_ib] = 0xa0U + (duk_uint8_t) count;
        } else {
            duk__cbor_encode_ensure(enc_ctx, 1);
            p = enc_ctx->ptr;
            *p++ = 0xffU;  /* break */
            enc_ctx->ptr = p;
        }
    }

    duk__cbor_encode_objarr_exit(enc_ctx);
}

DUK_LOCAL void duk__cbor_encode_buffer(duk_cbor_encode_context *enc_ctx) {
    duk_uint8_t *buf;
    duk_size_t len;
    duk_uint8_t *p;

    /* Caller must ensure space. */
    DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8);

    /* Tag buffer data? */
    buf = (duk_uint8_t *) duk_require_buffer(enc_ctx->thr, -1, &len);
    duk__cbor_encode_sizet_uint32_check(enc_ctx, len);
    duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x40U);
    duk__cbor_encode_ensure(enc_ctx, len);
    p = enc_ctx->ptr;
    duk_memcpy_unsafe((void *) p, (const void *) buf, len);
    p += len;
    enc_ctx->ptr = p;
}

DUK_LOCAL void duk__cbor_encode_pointer(duk_cbor_encode_context *enc_ctx) {
    /* Pointers (void *) are challenging to encode.  They can't
     * be relied to be even 64-bit integer compatible (there are
     * pointer models larger than that), nor can floats encode
     * them.  They could be encoded as strings (%p format) but
     * that's not portable.  They could be encoded as direct memory
     * representations.  Recovering pointers is non-portable in any
     * case but it would be nice to be able to detect and recover
     * compatible pointers.
     *
     * For now, encode as "(%p)" string, matching JX.  There doesn't
     * seem to be an appropriate tag, so pointers don't currently
     * survive a CBOR encode/decode roundtrip intact.
     */
    const char *ptr;

    ptr = duk_to_string(enc_ctx->thr, -1);
    DUK_ASSERT(ptr != NULL);
    duk_push_sprintf(enc_ctx->thr, "(%s)", ptr);
    duk_remove(enc_ctx->thr, -2);
    duk__cbor_encode_string_top(enc_ctx);
}

DUK_LOCAL void duk__cbor_encode_lightfunc(duk_cbor_encode_context *enc_ctx) {
    duk_uint8_t *p;

    /* Caller must ensure space. */
    DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8);

    /* For now encode as an empty object. */
    p = enc_ctx->ptr;
    *p++ = 0xa0U;
    enc_ctx->ptr = p;
}

DUK_LOCAL void duk__cbor_encode_value(duk_cbor_encode_context *enc_ctx) {
    duk_uint8_t *p;

    /* Encode/decode cycle currently loses some type information.
     * This can be improved by registering custom tags with IANA.
     */

    /* Reserve space for up to 64-bit types (1 initial byte + 8
     * followup bytes).  This allows encoding of integers, floats,
     * string/buffer length fields, etc without separate checks
     * in each code path.
     */
    duk__cbor_encode_ensure(enc_ctx, 1 + 8);

    switch (duk_get_type(enc_ctx->thr, -1)) {
    case DUK_TYPE_UNDEFINED: {
        p = enc_ctx->ptr;
        *p++ = 0xf7;
        enc_ctx->ptr = p;
        break;
    }
    case DUK_TYPE_NULL: {
        p = enc_ctx->ptr;
        *p++ = 0xf6;
        enc_ctx->ptr = p;
        break;
    }
    case DUK_TYPE_BOOLEAN: {
        duk_uint8_t u8 = duk_get_boolean(enc_ctx->thr, -1) ? 0xf5U : 0xf4U;
        p = enc_ctx->ptr;
        *p++ = u8;
        enc_ctx->ptr = p;
        break;
    }
    case DUK_TYPE_NUMBER: {
        duk__cbor_encode_double(enc_ctx, duk_get_number(enc_ctx->thr, -1));
        break;
    }
    case DUK_TYPE_STRING: {
        duk__cbor_encode_string_top(enc_ctx);
        break;
    }
    case DUK_TYPE_OBJECT: {
        duk__cbor_encode_object(enc_ctx);
        break;
    }
    case DUK_TYPE_BUFFER: {
        duk__cbor_encode_buffer(enc_ctx);
        break;
    }
    case DUK_TYPE_POINTER: {
        duk__cbor_encode_pointer(enc_ctx);
        break;
    }
    case DUK_TYPE_LIGHTFUNC: {
        duk__cbor_encode_lightfunc(enc_ctx);
        break;
    }
    case DUK_TYPE_NONE:
    default:
        goto fail;
    }

    duk_pop(enc_ctx->thr);
    return;

 fail:
    duk__cbor_encode_error(enc_ctx);
}

/*
 *  Decoding
 */

DUK_LOCAL void duk__cbor_decode_error(duk_cbor_decode_context *dec_ctx) {
    (void) duk_type_error(dec_ctx->thr, "cbor decode error");
}

DUK_LOCAL void duk__cbor_decode_req_stack(duk_cbor_decode_context *dec_ctx) {
    duk_require_stack(dec_ctx->thr, 4);
}

DUK_LOCAL void duk__cbor_decode_objarr_entry(duk_cbor_decode_context *dec_ctx) {
    duk_hthread *thr = dec_ctx->thr;

    /* Native stack check in object/array recursion. */
    duk_native_stack_check(thr);

    duk__cbor_decode_req_stack(dec_ctx);

    DUK_ASSERT(dec_ctx->recursion_depth <= dec_ctx->recursion_limit);
    if (dec_ctx->recursion_depth >= dec_ctx->recursion_limit) {
        DUK_ERROR_RANGE(thr, DUK_STR_DEC_RECLIMIT);
        DUK_WO_NORETURN(return;);
    }
    dec_ctx->recursion_depth++;
}

DUK_LOCAL void duk__cbor_decode_objarr_exit(duk_cbor_decode_context *dec_ctx) {
    DUK_ASSERT(dec_ctx->recursion_depth > 0);
    dec_ctx->recursion_depth--;
}

DUK_LOCAL duk_uint8_t duk__cbor_decode_readbyte(duk_cbor_decode_context *dec_ctx) {
    DUK_ASSERT(dec_ctx->off <= dec_ctx->len);
    if (DUK_UNLIKELY(dec_ctx->len - dec_ctx->off < 1U)) {
        duk__cbor_decode_error(dec_ctx);
    }
    return dec_ctx->buf[dec_ctx->off++];
}

DUK_LOCAL duk_uint16_t duk__cbor_decode_read_u16(duk_cbor_decode_context *dec_ctx) {
    duk_uint16_t res;

    DUK_ASSERT(dec_ctx->off <= dec_ctx->len);
    if (DUK_UNLIKELY(dec_ctx->len - dec_ctx->off < 2U)) {
        duk__cbor_decode_error(dec_ctx);
    }
    res = DUK_RAW_READ_U16_BE(dec_ctx->buf + dec_ctx->off);
    dec_ctx->off += 2;
    return res;
}

DUK_LOCAL duk_uint32_t duk__cbor_decode_read_u32(duk_cbor_decode_context *dec_ctx) {
    duk_uint32_t res;

    DUK_ASSERT(dec_ctx->off <= dec_ctx->len);
    if (DUK_UNLIKELY(dec_ctx->len - dec_ctx->off < 4U)) {
        duk__cbor_decode_error(dec_ctx);
    }
    res = DUK_RAW_READ_U32_BE(dec_ctx->buf + dec_ctx->off);
    dec_ctx->off += 4;
    return res;
}

DUK_LOCAL duk_uint8_t duk__cbor_decode_peekbyte(duk_cbor_decode_context *dec_ctx) {
    if (DUK_UNLIKELY(dec_ctx->off >= dec_ctx->len)) {
        duk__cbor_decode_error(dec_ctx);
    }
    return dec_ctx->buf[dec_ctx->off];
}

DUK_LOCAL void duk__cbor_decode_rewind(duk_cbor_decode_context *dec_ctx, duk_size_t len) {
    DUK_ASSERT(len <= dec_ctx->off);  /* Caller must ensure. */
    dec_ctx->off -= len;
}

#if 0
DUK_LOCAL void duk__cbor_decode_ensure(duk_cbor_decode_context *dec_ctx, duk_size_t len) {
    if (dec_ctx->off + len > dec_ctx->len) {
        duk__cbor_decode_error(dec_ctx);
    }
}
#endif

DUK_LOCAL const duk_uint8_t *duk__cbor_decode_consume(duk_cbor_decode_context *dec_ctx, duk_size_t len) {
    DUK_ASSERT(dec_ctx->off <= dec_ctx->len);
    if (DUK_LIKELY(dec_ctx->len - dec_ctx->off >= len)) {
        const duk_uint8_t *res = dec_ctx->buf + dec_ctx->off;
        dec_ctx->off += len;
        return res;
    }

    duk__cbor_decode_error(dec_ctx);  /* Not enough input. */
    return NULL;
}

DUK_LOCAL int duk__cbor_decode_checkbreak(duk_cbor_decode_context *dec_ctx) {
    if (duk__cbor_decode_peekbyte(dec_ctx) == 0xffU) {
        DUK_ASSERT(dec_ctx->off < dec_ctx->len);
        dec_ctx->off++;
#if 0
        (void) duk__cbor_decode_readbyte(dec_ctx);
#endif
        return 1;
    }
    return 0;
}

DUK_LOCAL void duk__cbor_decode_push_aival_int(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib, duk_bool_t negative) {
    duk_uint8_t ai;
    duk_uint32_t t, t1, t2;
#if 0
    duk_uint64_t t3;
#endif
    duk_double_t d1, d2;
    duk_double_t d;

    ai = ib & 0x1fU;
    if (ai <= 0x17U) {
        t = ai;
        goto shared_exit;
    }

    switch (ai) {
    case 0x18U:  /* 1 byte */
        t = (duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx);
        goto shared_exit;
    case 0x19U:  /* 2 byte */
        t = (duk_uint32_t) duk__cbor_decode_read_u16(dec_ctx);
        goto shared_exit;
    case 0x1aU:  /* 4 byte */
        t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx);
        goto shared_exit;
    case 0x1bU:  /* 8 byte */
        /* For uint64 it's important to handle the -1.0 part before
         * casting to double: otherwise the adjustment might be lost
         * in the cast.  Uses: -1.0 - d <=> -(d + 1.0).
         */
        t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx);
        t2 = t;
        t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx);
        t1 = t;
#if 0
        t3 = (duk_uint64_t) t2 * DUK_U64_CONSTANT(0x100000000) + (duk_uint64_t) t1;
        if (negative) {
            if (t3 == DUK_UINT64_MAX) {
                /* -(0xffff'ffff'ffff'ffffULL + 1) =
                 * -0x1'0000'0000'0000'0000
                 *
                 * >>> -0x10000000000000000
                 * -18446744073709551616L
                 */
                return -18446744073709551616.0;
            } else {
                return -((duk_double_t) (t3 + DUK_U64_CONSTANT(1)));
            }
        } else {
            return (duk_double_t) t3;  /* XXX: cast helper */
        }
#endif
#if 0
        t3 = (duk_uint64_t) t2 * DUK_U64_CONSTANT(0x100000000) + (duk_uint64_t) t1;
        if (negative) {
            /* Simpler version: take advantage of the fact that
             * 0xffff'ffff'ffff'ffff and 0x1'0000'0000'0000'0000
             * both round to 0x1'0000'0000'0000'0000:
             * > (0xffffffffffffffff).toString(16)
             * '10000000000000000'
             * > (0x10000000000000000).toString(16)
             * '10000000000000000'
             *
             * For the DUK_UINT64_MAX case we just skip the +1
             * increment to avoid wrapping; the result still
             * comes out right for an IEEE double cast.
             */
            if (t3 != DUK_UINT64_MAX) {
                t3++;
            }
            return -((duk_double_t) t3);
        } else {
            return (duk_double_t) t3;  /* XXX: cast helper */
        }
#endif
#if 1
        /* Use two double parts, avoids dependency on 64-bit type.
         * Avoid precision loss carefully, especially when dealing
         * with the required +1 for negative values.
         *
         * No fastint check for this path at present.
         */
        d1 = (duk_double_t) t1;  /* XXX: cast helpers */
        d2 = (duk_double_t) t2 * 4294967296.0;
        if (negative) {
            d1 += 1.0;
        }
        d = d2 + d1;
        if (negative) {
            d = -d;
        }
#endif
        /* XXX: a push and check for fastint API would be nice */
        duk_push_number(dec_ctx->thr, d);
        return;
    }

    duk__cbor_decode_error(dec_ctx);
    return;

 shared_exit:
    if (negative) {
        /* XXX: a push and check for fastint API would be nice */
        if ((duk_uint_t) t <= (duk_uint_t) -(DUK_INT_MIN + 1)) {
            duk_push_int(dec_ctx->thr, -1 - ((duk_int_t) t));
        } else {
            duk_push_number(dec_ctx->thr, -1.0 - (duk_double_t) t);
        }
    } else {
        duk_push_uint(dec_ctx->thr, (duk_uint_t) t);
    }
}

DUK_LOCAL void duk__cbor_decode_skip_aival_int(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib) {
    const duk_int8_t skips[32] = {
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 8, -1, -1, -1, -1
    };
    duk_uint8_t ai;
    duk_int8_t skip;

    ai = ib & 0x1fU;
    skip = skips[ai];
    if (DUK_UNLIKELY(skip < 0)) {
        duk__cbor_decode_error(dec_ctx);
    }
    duk__cbor_decode_consume(dec_ctx, (duk_size_t) skip);
    return;
}

DUK_LOCAL duk_uint32_t duk__cbor_decode_aival_uint32(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib) {
    duk_uint8_t ai;
    duk_uint32_t t;

    ai = ib & 0x1fU;
    if (ai <= 0x17U) {
        return (duk_uint32_t) ai;
    }

    switch (ai) {
    case 0x18U:  /* 1 byte */
        t = (duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx);
        return t;
    case 0x19U:  /* 2 byte */
        t = (duk_uint32_t) duk__cbor_decode_read_u16(dec_ctx);
        return t;
    case 0x1aU:  /* 4 byte */
        t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx);
        return t;
    case 0x1bU:  /* 8 byte */
        t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx);
        if (t != 0U) {
            break;
        }
        t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx);
        return t;
    }

    duk__cbor_decode_error(dec_ctx);
    return 0U;
}

DUK_LOCAL void duk__cbor_decode_buffer(duk_cbor_decode_context *dec_ctx, duk_uint8_t expected_base) {
    duk_uint32_t len;
    duk_uint8_t *buf;
    const duk_uint8_t *inp;
    duk_uint8_t ib;

    ib = duk__cbor_decode_readbyte(dec_ctx);
    if ((ib & 0xe0U) != expected_base) {
        duk__cbor_decode_error(dec_ctx);
    }
    /* Indefinite format is rejected by the following on purpose. */
    len = duk__cbor_decode_aival_uint32(dec_ctx, ib);
    inp = duk__cbor_decode_consume(dec_ctx, len);
    /* XXX: duk_push_fixed_buffer_with_data() would be a nice API addition. */
    buf = (duk_uint8_t *) duk_push_fixed_buffer(dec_ctx->thr, (duk_size_t) len);
    duk_memcpy((void *) buf, (const void *) inp, (size_t) len);
}

DUK_LOCAL void duk__cbor_decode_join_buffers(duk_cbor_decode_context *dec_ctx, duk_idx_t count) {
    duk_size_t total_size = 0;
    duk_idx_t top = duk_get_top(dec_ctx->thr);
    duk_idx_t base = top - count;  /* count is >= 1 */
    duk_idx_t idx;
    duk_uint8_t *p = NULL;

    DUK_ASSERT(count >= 1);
    DUK_ASSERT(top >= count);

    for (;;) {
        /* First round: compute total size.
         * Second round: copy into place.
         */
        for (idx = base; idx < top; idx++) {
            duk_uint8_t *buf_data;
            duk_size_t buf_size;

            buf_data = (duk_uint8_t *) duk_require_buffer(dec_ctx->thr, idx, &buf_size);
            if (p != NULL) {
                duk_memcpy_unsafe((void *) p, (const void *) buf_data, buf_size);
                p += buf_size;
            } else {
                total_size += buf_size;
                if (DUK_UNLIKELY(total_size < buf_size)) {  /* Wrap check. */
                    duk__cbor_decode_error(dec_ctx);
                }
            }
        }

        if (p != NULL) {
            break;
        } else {
            p = (duk_uint8_t *) duk_push_fixed_buffer(dec_ctx->thr, total_size);
            DUK_ASSERT(p != NULL);
        }
    }

    duk_replace(dec_ctx->thr, base);
    duk_pop_n(dec_ctx->thr, count - 1);
}

DUK_LOCAL void duk__cbor_decode_and_join_strbuf(duk_cbor_decode_context *dec_ctx, duk_uint8_t expected_base) {
    duk_idx_t count = 0;
    for (;;) {
        if (duk__cbor_decode_checkbreak(dec_ctx)) {
            break;
        }
        duk_require_stack(dec_ctx->thr, 1);
        duk__cbor_decode_buffer(dec_ctx, expected_base);
        count++;
        if (DUK_UNLIKELY(count <= 0)) {  /* Wrap check. */
            duk__cbor_decode_error(dec_ctx);
        }
    }
    if (count == 0) {
        (void) duk_push_fixed_buffer(dec_ctx->thr, 0);
    } else if (count > 1) {
        duk__cbor_decode_join_buffers(dec_ctx, count);
    }
}

DUK_LOCAL duk_double_t duk__cbor_decode_half_float(duk_cbor_decode_context *dec_ctx) {
    duk_double_union u;
    const duk_uint8_t *inp;
    duk_int_t expt;
    duk_uint_t u16;
    duk_uint_t tmp;
    duk_double_t res;

    inp = duk__cbor_decode_consume(dec_ctx, 2);
    u16 = ((duk_uint_t) inp[0] << 8) + (duk_uint_t) inp[1];
    expt = (duk_int_t) ((u16 >> 10) & 0x1fU) - 15;

    /* Reconstruct IEEE double into little endian order first, then convert
     * to host order.
     */

    duk_memzero((void *) &u, sizeof(u));

    if (expt == -15) {
        /* Zero or denormal; but note that half float
         * denormals become double normals.
         */
        if ((u16 & 0x03ffU) == 0) {
            u.uc[7] = inp[0] & 0x80U;
        } else {
            /* Create denormal by first creating a double that
             * contains the denormal bits and a leading implicit
             * 1-bit.  Then subtract away the implicit 1-bit.
             *
             *    0.mmmmmmmmmm * 2^-14
             *    1.mmmmmmmmmm 0.... * 2^-14
             *   -1.0000000000 0.... * 2^-14
             *
             * Double exponent: -14 + 1023 = 0x3f1
             */
            u.uc[7] = 0x3fU;
            u.uc[6] = 0x10U + (duk_uint8_t) ((u16 >> 6) & 0x0fU);
            u.uc[5] = (duk_uint8_t) ((u16 << 2) & 0xffU);  /* Mask is really 0xfcU */

            duk_dblunion_little_to_host(&u);
            res = u.d - 0.00006103515625;  /* 2^(-14) */
            if (u16 & 0x8000U) {
                res = -res;
            }
            return res;
        }
    } else if (expt == 16) {
        /* +/- Inf or NaN. */
        if ((u16 & 0x03ffU) == 0) {
            u.uc[7] = (inp[0] & 0x80U) + 0x7fU;
            u.uc[6] = 0xf0U;
        } else {
            /* Create a 'quiet NaN' with highest
             * bit set (there are some platforms
             * where the NaN payload convention is
             * the opposite).  Keep sign.
             */
            u.uc[7] = (inp[0] & 0x80U) + 0x7fU;
            u.uc[6] = 0xf8U;
        }
    } else {
        /* Normal. */
        tmp = (inp[0] & 0x80U) ? 0x80000000UL : 0UL;
        tmp += (duk_uint_t) (expt + 1023) << 20;
        tmp += (duk_uint_t) (inp[0] & 0x03U) << 18;
        tmp += (duk_uint_t) (inp[1] & 0xffU) << 10;
        u.uc[7] = (tmp >> 24) & 0xffU;
        u.uc[6] = (tmp >> 16) & 0xffU;
        u.uc[5] = (tmp >> 8) & 0xffU;
        u.uc[4] = (tmp >> 0) & 0xffU;
    }

    duk_dblunion_little_to_host(&u);
    return u.d;
}

DUK_LOCAL void duk__cbor_decode_string(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib, duk_uint8_t ai) {
    /* If the CBOR string data is not valid UTF-8 it is technically
     * invalid CBOR.  Possible behaviors at least:
     *
     *   1. Reject the input, i.e. throw TypeError.
     *
     *   2. Accept the input, but sanitize non-UTF-8 data into UTF-8
     *      using U+FFFD replacements.  Also it might make sense to
     *      decode non-BMP codepoints into surrogates for better
     *      ECMAScript compatibility.
     *
     *   3. Accept the input as a Duktape string (which are not always
     *      valid UTF-8), but reject any input that would create a
     *      Symbol representation.
     *
     * Current behavior is 3.
     */

    if (ai == 0x1fU) {
        duk_uint8_t *buf_data;
        duk_size_t buf_size;

        duk__cbor_decode_and_join_strbuf(dec_ctx, 0x60U);
        buf_data = (duk_uint8_t *) duk_require_buffer(dec_ctx->thr, -1, &buf_size);
        (void) duk_push_lstring(dec_ctx->thr, (const char *) buf_data, buf_size);
        duk_remove(dec_ctx->thr, -2);
    } else {
        duk_uint32_t len;
        const duk_uint8_t *inp;

        len = duk__cbor_decode_aival_uint32(dec_ctx, ib);
        inp = duk__cbor_decode_consume(dec_ctx, len);
        (void) duk_push_lstring(dec_ctx->thr, (const char *) inp, (duk_size_t) len);
    }
    if (duk_is_symbol(dec_ctx->thr, -1)) {
        /* Refuse to create Symbols when decoding. */
        duk__cbor_decode_error(dec_ctx);
    }

    /* XXX: Here a Duktape API call to convert input -> utf-8 with
     * replacements would be nice.
     */
}

DUK_LOCAL duk_bool_t duk__cbor_decode_array(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib, duk_uint8_t ai) {
    duk_uint32_t idx, len;

    duk__cbor_decode_objarr_entry(dec_ctx);

    /* Support arrays up to 0xfffffffeU in length.  0xffffffff is
     * used as an indefinite length marker.
     */
    if (ai == 0x1fU) {
        len = 0xffffffffUL;
    } else {
        len = duk__cbor_decode_aival_uint32(dec_ctx, ib);
        if (len == 0xffffffffUL) {
            goto failure;
        }
    }

    /* XXX: use bare array? */
    duk_push_array(dec_ctx->thr);
    for (idx = 0U; ;) {
        if (len == 0xffffffffUL && duk__cbor_decode_checkbreak(dec_ctx)) {
            break;
        }
        if (idx == len) {
            if (ai == 0x1fU) {
                goto failure;
            }
            break;
        }
        duk__cbor_decode_value(dec_ctx);
        duk_put_prop_index(dec_ctx->thr, -2, (duk_uarridx_t) idx);
        idx++;
        if (idx == 0U) {
            goto failure;  /* wrapped */
        }
    }

#if 0
 success:
#endif
    duk__cbor_decode_objarr_exit(dec_ctx);
    return 1;

 failure:
    /* No need to unwind recursion checks, caller will throw. */
    return 0;
}

DUK_LOCAL duk_bool_t duk__cbor_decode_map(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib, duk_uint8_t ai) {
    duk_uint32_t count;

    duk__cbor_decode_objarr_entry(dec_ctx);

    if (ai == 0x1fU) {
        count = 0xffffffffUL;
    } else {
        count = duk__cbor_decode_aival_uint32(dec_ctx, ib);
        if (count == 0xffffffffUL) {
            goto failure;
        }
    }

    /* XXX: use bare object? */
    duk_push_object(dec_ctx->thr);
    for (;;) {
        if (count == 0xffffffffUL) {
            if (duk__cbor_decode_checkbreak(dec_ctx)) {
                break;
            }
        } else {
            if (count == 0UL) {
                break;
            }
            count--;
        }

        /* Non-string keys are coerced to strings,
         * possibly leading to overwriting previous
         * keys.  Last key of a certain coerced name
         * wins.  If key is an object, it will coerce
         * to '[object Object]' which is consistent
         * but potentially misleading.  One alternative
         * would be to skip non-string keys.
         */
        duk__cbor_decode_value(dec_ctx);
        duk__cbor_decode_value(dec_ctx);
        duk_put_prop(dec_ctx->thr, -3);
    }

#if 0
 success:
#endif
    duk__cbor_decode_objarr_exit(dec_ctx);
    return 1;

 failure:
    /* No need to unwind recursion checks, caller will throw. */
    return 0;
}

DUK_LOCAL duk_double_t duk__cbor_decode_float(duk_cbor_decode_context *dec_ctx) {
    duk_float_union u;
    const duk_uint8_t *inp;
    inp = duk__cbor_decode_consume(dec_ctx, 4);
    duk_memcpy((void *) u.uc, (const void *) inp, 4);
    duk_fltunion_big_to_host(&u);
    return (duk_double_t) u.f;
}

DUK_LOCAL duk_double_t duk__cbor_decode_double(duk_cbor_decode_context *dec_ctx) {
    duk_double_union u;
    const duk_uint8_t *inp;
    inp = duk__cbor_decode_consume(dec_ctx, 8);
    duk_memcpy((void *) u.uc, (const void *) inp, 8);
    duk_dblunion_big_to_host(&u);
    return u.d;
}

#if defined(DUK_CBOR_DECODE_FASTPATH)
#define DUK__CBOR_AI  (ib & 0x1fU)

DUK_LOCAL void duk__cbor_decode_value(duk_cbor_decode_context *dec_ctx) {
    duk_uint8_t ib;

    /* Any paths potentially recursing back to duk__cbor_decode_value()
     * must perform a Duktape value stack growth check.  Avoid the check
     * here for simple paths like primitive values.
     */

 reread_initial_byte:
    DUK_DDD(DUK_DDDPRINT("cbor decode off=%ld len=%ld", (long) dec_ctx->off, (long) dec_ctx->len));

    ib = duk__cbor_decode_readbyte(dec_ctx);

    /* Full initial byte switch, footprint cost over baseline is ~+1kB. */
    /* XXX: Force full switch with no range check. */

    switch (ib) {
    case 0x00U: case 0x01U: case 0x02U: case 0x03U: case 0x04U: case 0x05U: case 0x06U: case 0x07U:
    case 0x08U: case 0x09U: case 0x0aU: case 0x0bU: case 0x0cU: case 0x0dU: case 0x0eU: case 0x0fU:
    case 0x10U: case 0x11U: case 0x12U: case 0x13U: case 0x14U: case 0x15U: case 0x16U: case 0x17U:
        duk_push_uint(dec_ctx->thr, ib);
        break;
    case 0x18U: case 0x19U: case 0x1aU: case 0x1bU:
        duk__cbor_decode_push_aival_int(dec_ctx, ib, 0 /*negative*/);
        break;
    case 0x1cU: case 0x1dU: case 0x1eU: case 0x1fU:
        goto format_error;
    case 0x20U: case 0x21U: case 0x22U: case 0x23U: case 0x24U: case 0x25U: case 0x26U: case 0x27U:
    case 0x28U: case 0x29U: case 0x2aU: case 0x2bU: case 0x2cU: case 0x2dU: case 0x2eU: case 0x2fU:
    case 0x30U: case 0x31U: case 0x32U: case 0x33U: case 0x34U: case 0x35U: case 0x36U: case 0x37U:
        duk_push_int(dec_ctx->thr, -((duk_int_t) ((ib - 0x20U) + 1U)));
        break;
    case 0x38U: case 0x39U: case 0x3aU: case 0x3bU:
        duk__cbor_decode_push_aival_int(dec_ctx, ib, 1 /*negative*/);
        break;
    case 0x3cU: case 0x3dU: case 0x3eU: case 0x3fU:
        goto format_error;
    case 0x40U: case 0x41U: case 0x42U: case 0x43U: case 0x44U: case 0x45U: case 0x46U: case 0x47U:
    case 0x48U: case 0x49U: case 0x4aU: case 0x4bU: case 0x4cU: case 0x4dU: case 0x4eU: case 0x4fU:
    case 0x50U: case 0x51U: case 0x52U: case 0x53U: case 0x54U: case 0x55U: case 0x56U: case 0x57U:
        /* XXX: Avoid rewind, we know the length already. */
        DUK_ASSERT(dec_ctx->off > 0U);
        dec_ctx->off--;
        duk__cbor_decode_buffer(dec_ctx, 0x40U);
        break;
    case 0x58U: case 0x59U: case 0x5aU: case 0x5bU:
        /* XXX: Avoid rewind, decode length inline. */
        DUK_ASSERT(dec_ctx->off > 0U);
        dec_ctx->off--;
        duk__cbor_decode_buffer(dec_ctx, 0x40U);
        break;
    case 0x5cU: case 0x5dU: case 0x5eU:
        goto format_error;
    case 0x5fU:
        duk__cbor_decode_and_join_strbuf(dec_ctx, 0x40U);
        break;
    case 0x60U: case 0x61U: case 0x62U: case 0x63U: case 0x64U: case 0x65U: case 0x66U: case 0x67U:
    case 0x68U: case 0x69U: case 0x6aU: case 0x6bU: case 0x6cU: case 0x6dU: case 0x6eU: case 0x6fU:
    case 0x70U: case 0x71U: case 0x72U: case 0x73U: case 0x74U: case 0x75U: case 0x76U: case 0x77U:
        /* XXX: Avoid double decode of length. */
        duk__cbor_decode_string(dec_ctx, ib, DUK__CBOR_AI);
        break;
    case 0x78U: case 0x79U: case 0x7aU: case 0x7bU:
        /* XXX: Avoid double decode of length. */
        duk__cbor_decode_string(dec_ctx, ib, DUK__CBOR_AI);
        break;
    case 0x7cU: case 0x7dU: case 0x7eU:
        goto format_error;
    case 0x7fU:
        duk__cbor_decode_string(dec_ctx, ib, DUK__CBOR_AI);
        break;
    case 0x80U: case 0x81U: case 0x82U: case 0x83U: case 0x84U: case 0x85U: case 0x86U: case 0x87U:
    case 0x88U: case 0x89U: case 0x8aU: case 0x8bU: case 0x8cU: case 0x8dU: case 0x8eU: case 0x8fU:
    case 0x90U: case 0x91U: case 0x92U: case 0x93U: case 0x94U: case 0x95U: case 0x96U: case 0x97U:
        if (DUK_UNLIKELY(duk__cbor_decode_array(dec_ctx, ib, DUK__CBOR_AI) == 0)) {
            goto format_error;
        }
        break;
    case 0x98U: case 0x99U: case 0x9aU: case 0x9bU:
        if (DUK_UNLIKELY(duk__cbor_decode_array(dec_ctx, ib, DUK__CBOR_AI) == 0)) {
            goto format_error;
        }
        break;
    case 0x9cU: case 0x9dU: case 0x9eU:
        goto format_error;
    case 0x9fU:
        if (DUK_UNLIKELY(duk__cbor_decode_array(dec_ctx, ib, DUK__CBOR_AI) == 0)) {
            goto format_error;
        }
        break;
    case 0xa0U: case 0xa1U: case 0xa2U: case 0xa3U: case 0xa4U: case 0xa5U: case 0xa6U: case 0xa7U:
    case 0xa8U: case 0xa9U: case 0xaaU: case 0xabU: case 0xacU: case 0xadU: case 0xaeU: case 0xafU:
    case 0xb0U: case 0xb1U: case 0xb2U: case 0xb3U: case 0xb4U: case 0xb5U: case 0xb6U: case 0xb7U:
        if (DUK_UNLIKELY(duk__cbor_decode_map(dec_ctx, ib, DUK__CBOR_AI) == 0)) {
            goto format_error;
        }
        break;
    case 0xb8U: case 0xb9U: case 0xbaU: case 0xbbU:
        if (DUK_UNLIKELY(duk__cbor_decode_map(dec_ctx, ib, DUK__CBOR_AI) == 0)) {
            goto format_error;
        }
        break;
    case 0xbcU: case 0xbdU: case 0xbeU:
        goto format_error;
    case 0xbfU:
        if (DUK_UNLIKELY(duk__cbor_decode_map(dec_ctx, ib, DUK__CBOR_AI) == 0)) {
            goto format_error;
        }
        break;
    case 0xc0U: case 0xc1U: case 0xc2U: case 0xc3U: case 0xc4U: case 0xc5U: case 0xc6U: case 0xc7U:
    case 0xc8U: case 0xc9U: case 0xcaU: case 0xcbU: case 0xccU: case 0xcdU: case 0xceU: case 0xcfU:
    case 0xd0U: case 0xd1U: case 0xd2U: case 0xd3U: case 0xd4U: case 0xd5U: case 0xd6U: case 0xd7U:
        /* Tag 0-23: drop. */
        goto reread_initial_byte;
    case 0xd8U: case 0xd9U: case 0xdaU: case 0xdbU:
        duk__cbor_decode_skip_aival_int(dec_ctx, ib);
        goto reread_initial_byte;
    case 0xdcU: case 0xddU: case 0xdeU: case 0xdfU:
        goto format_error;
    case 0xe0U:
        goto format_error;
    case 0xe1U:
        goto format_error;
    case 0xe2U:
        goto format_error;
    case 0xe3U:
        goto format_error;
    case 0xe4U:
        goto format_error;
    case 0xe5U:
        goto format_error;
    case 0xe6U:
        goto format_error;
    case 0xe7U:
        goto format_error;
    case 0xe8U:
        goto format_error;
    case 0xe9U:
        goto format_error;
    case 0xeaU:
        goto format_error;
    case 0xebU:
        goto format_error;
    case 0xecU:
        goto format_error;
    case 0xedU:
        goto format_error;
    case 0xeeU:
        goto format_error;
    case 0xefU:
        goto format_error;
    case 0xf0U:
        goto format_error;
    case 0xf1U:
        goto format_error;
    case 0xf2U:
        goto format_error;
    case 0xf3U:
        goto format_error;
    case 0xf4U:
        duk_push_false(dec_ctx->thr);
        break;
    case 0xf5U:
        duk_push_true(dec_ctx->thr);
        break;
    case 0xf6U:
        duk_push_null(dec_ctx->thr);
        break;
    case 0xf7U:
        duk_push_undefined(dec_ctx->thr);
        break;
    case 0xf8U:
        /* Simple value 32-255, nothing defined yet, so reject. */
        goto format_error;
    case 0xf9U: {
        duk_double_t d;
        d = duk__cbor_decode_half_float(dec_ctx);
        duk_push_number(dec_ctx->thr, d);
        break;
    }
    case 0xfaU: {
        duk_double_t d;
        d = duk__cbor_decode_float(dec_ctx);
        duk_push_number(dec_ctx->thr, d);
        break;
    }
    case 0xfbU: {
        duk_double_t d;
        d = duk__cbor_decode_double(dec_ctx);
        duk_push_number(dec_ctx->thr, d);
        break;
    }
    case 0xfcU:
    case 0xfdU:
    case 0xfeU:
    case 0xffU:
        goto format_error;
    }  /* end switch */

    return;

 format_error:
    duk__cbor_decode_error(dec_ctx);
}
#else  /* DUK_CBOR_DECODE_FASTPATH */
DUK_LOCAL void duk__cbor_decode_value(duk_cbor_decode_context *dec_ctx) {
    duk_uint8_t ib, mt, ai;

    /* Any paths potentially recursing back to duk__cbor_decode_value()
     * must perform a Duktape value stack growth check.  Avoid the check
     * here for simple paths like primitive values.
     */

 reread_initial_byte:
    DUK_DDD(DUK_DDDPRINT("cbor decode off=%ld len=%ld", (long) dec_ctx->off, (long) dec_ctx->len));

    ib = duk__cbor_decode_readbyte(dec_ctx);
    mt = ib >> 5U;
    ai = ib & 0x1fU;

    /* Additional information in [24,27] = [0x18,0x1b] has relatively
     * uniform handling for all major types: read 1/2/4/8 additional
     * bytes.  For major type 7 the 1-byte value is a 'simple type', and
     * 2/4/8-byte values are floats.  For other major types the 1/2/4/8
     * byte values are integers.  The lengths are uniform, but the typing
     * is not.
     */

    switch (mt) {
    case 0U: {  /* unsigned integer */
        duk__cbor_decode_push_aival_int(dec_ctx, ib, 0 /*negative*/);
        break;
    }
    case 1U: {  /* negative integer */
        duk__cbor_decode_push_aival_int(dec_ctx, ib, 1 /*negative*/);
        break;
    }
    case 2U: {  /* byte string */
        if (ai == 0x1fU) {
            duk__cbor_decode_and_join_strbuf(dec_ctx, 0x40U);
        } else {
            duk__cbor_decode_rewind(dec_ctx, 1U);
            duk__cbor_decode_buffer(dec_ctx, 0x40U);
        }
        break;
    }
    case 3U: {  /* text string */
        duk__cbor_decode_string(dec_ctx, ib, ai);
        break;
    }
    case 4U: {  /* array of data items */
        if (DUK_UNLIKELY(duk__cbor_decode_array(dec_ctx, ib, ai) == 0)) {
            goto format_error;
        }
        break;
    }
    case 5U: {  /* map of pairs of data items */
        if (DUK_UNLIKELY(duk__cbor_decode_map(dec_ctx, ib, ai) == 0)) {
            goto format_error;
        }
        break;
    }
    case 6U: {  /* semantic tagging */
        /* Tags are ignored now, re-read initial byte.  A tagged
         * value may itself be tagged (an unlimited number of times)
         * so keep on peeling away tags.
         */
        duk__cbor_decode_skip_aival_int(dec_ctx, ib);
        goto reread_initial_byte;
    }
    case 7U: {  /* floating point numbers, simple data types, break; other */
        switch (ai) {
        case 0x14U: {
            duk_push_false(dec_ctx->thr);
            break;
        }
        case 0x15U: {
            duk_push_true(dec_ctx->thr);
            break;
        }
        case 0x16U: {
            duk_push_null(dec_ctx->thr);
            break;
        }
        case 0x17U: {
            duk_push_undefined(dec_ctx->thr);
            break;
        }
        case 0x18U: {  /* more simple values (1 byte) */
            /* Simple value encoded in additional byte (none
             * are defined so far).  RFC 7049 states that the
             * follow-up byte must be 32-255 to minimize
             * confusion.  So, a non-shortest encoding like
             * f815 (= true, shortest encoding f5) must be
             * rejected.  cbor.me tester rejects f815, but
             * e.g. Python CBOR binding decodes it as true.
             */
            goto format_error;
        }
        case 0x19U: {  /* half-float (2 bytes) */
            duk_double_t d;
            d = duk__cbor_decode_half_float(dec_ctx);
            duk_push_number(dec_ctx->thr, d);
            break;
        }
        case 0x1aU: {  /* float (4 bytes) */
            duk_double_t d;
            d = duk__cbor_decode_float(dec_ctx);
            duk_push_number(dec_ctx->thr, d);
            break;
        }
        case 0x1bU: {  /* double (8 bytes) */
            duk_double_t d;
            d = duk__cbor_decode_double(dec_ctx);
            duk_push_number(dec_ctx->thr, d);
            break;
        }
        case 0xffU:  /* unexpected break */
        default: {
            goto format_error;
        }
        }  /* end switch */
        break;
    }
    default: {
        goto format_error;  /* will never actually occur */
    }
    }  /* end switch */

    return;

 format_error:
    duk__cbor_decode_error(dec_ctx);
}
#endif  /* DUK_CBOR_DECODE_FASTPATH */

DUK_LOCAL void duk__cbor_encode(duk_hthread *thr, duk_idx_t idx, duk_uint_t encode_flags) {
    duk_cbor_encode_context enc_ctx;
    duk_uint8_t *buf;

    DUK_UNREF(encode_flags);

    idx = duk_require_normalize_index(thr, idx);

    enc_ctx.thr = thr;
    enc_ctx.idx_buf = duk_get_top(thr);

    enc_ctx.len = 64;
    buf = (duk_uint8_t *) duk_push_dynamic_buffer(thr, enc_ctx.len);
    enc_ctx.ptr = buf;
    enc_ctx.buf = buf;
    enc_ctx.buf_end = buf + enc_ctx.len;

    enc_ctx.recursion_depth = 0;
    enc_ctx.recursion_limit = DUK_USE_CBOR_ENC_RECLIMIT;

    duk_dup(thr, idx);
    duk__cbor_encode_req_stack(&enc_ctx);
    duk__cbor_encode_value(&enc_ctx);
    DUK_ASSERT(enc_ctx.recursion_depth == 0);
    duk_resize_buffer(enc_ctx.thr, enc_ctx.idx_buf, (duk_size_t) (enc_ctx.ptr - enc_ctx.buf));
    duk_replace(thr, idx);
}

DUK_LOCAL void duk__cbor_decode(duk_hthread *thr, duk_idx_t idx, duk_uint_t decode_flags) {
    duk_cbor_decode_context dec_ctx;

    DUK_UNREF(decode_flags);

    /* Suppress compile warnings for functions only needed with e.g.
     * asserts enabled.
     */
    DUK_UNREF(duk__cbor_get_reserve);

    idx = duk_require_normalize_index(thr, idx);

    dec_ctx.thr = thr;
    dec_ctx.buf = (const duk_uint8_t *) duk_require_buffer_data(thr, idx, &dec_ctx.len);
    dec_ctx.off = 0;
    /* dec_ctx.len: set above */

    dec_ctx.recursion_depth = 0;
    dec_ctx.recursion_limit = DUK_USE_CBOR_DEC_RECLIMIT;

    duk__cbor_decode_req_stack(&dec_ctx);
    duk__cbor_decode_value(&dec_ctx);
    DUK_ASSERT(dec_ctx.recursion_depth == 0);
    if (dec_ctx.off != dec_ctx.len) {
        (void) duk_type_error(thr, "trailing garbage");
    }

    duk_replace(thr, idx);
}

#else  /* DUK_USE_CBOR_SUPPORT */

DUK_LOCAL void duk__cbor_encode(duk_hthread *thr, duk_idx_t idx, duk_uint_t encode_flags) {
    DUK_UNREF(idx);
    DUK_UNREF(encode_flags);
    DUK_ERROR_UNSUPPORTED(thr);
}

DUK_LOCAL void duk__cbor_decode(duk_hthread *thr, duk_idx_t idx, duk_uint_t decode_flags) {
    DUK_UNREF(idx);
    DUK_UNREF(decode_flags);
    DUK_ERROR_UNSUPPORTED(thr);
}

#endif  /* DUK_USE_CBOR_SUPPORT */

/*
 *  Public APIs
 */

DUK_EXTERNAL void duk_cbor_encode(duk_hthread *thr, duk_idx_t idx, duk_uint_t encode_flags) {
    DUK_ASSERT_API_ENTRY(thr);
    duk__cbor_encode(thr, idx, encode_flags);
}
DUK_EXTERNAL void duk_cbor_decode(duk_hthread *thr, duk_idx_t idx, duk_uint_t decode_flags) {
    DUK_ASSERT_API_ENTRY(thr);
    duk__cbor_decode(thr, idx, decode_flags);
}

#if defined(DUK_USE_CBOR_BUILTIN)
#if defined(DUK_USE_CBOR_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_cbor_encode(duk_hthread *thr) {
    DUK_ASSERT_TOP(thr, 1);

    duk__cbor_encode(thr, -1, 0 /*flags*/);

    /* Produce an ArrayBuffer by first decoding into a plain buffer which
     * mimics a Uint8Array and gettings its .buffer property.
     */
    /* XXX: shortcut */
    (void) duk_get_prop_stridx(thr, -1, DUK_STRIDX_LC_BUFFER);
    return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_cbor_decode(duk_hthread *thr) {
    DUK_ASSERT_TOP(thr, 1);

    duk__cbor_decode(thr, -1, 0 /*flags*/);
    return 1;
}
#else  /* DUK_USE_CBOR_SUPPORT */
DUK_INTERNAL duk_ret_t duk_bi_cbor_encode(duk_hthread *thr) {
    DUK_ERROR_UNSUPPORTED(thr);
    DUK_WO_NORETURN(return 0;);
}
DUK_INTERNAL duk_ret_t duk_bi_cbor_decode(duk_hthread *thr) {
    DUK_ERROR_UNSUPPORTED(thr);
    DUK_WO_NORETURN(return 0;);
}
#endif  /* DUK_USE_CBOR_SUPPORT */
#endif  /* DUK_USE_CBOR_BUILTIN */

/* automatic undefs */
#undef DUK__CBOR_AI
//#line 1 "duk_bi_date.c"
/*
 *  Date built-ins
 *
 *  Unlike most built-ins, Date has some platform dependencies for getting
 *  UTC time, converting between UTC and local time, and parsing and
 *  formatting time values.  These are all abstracted behind DUK_USE_xxx
 *  config options.  There are built-in platform specific providers for
 *  POSIX and Windows, but external providers can also be used.
 *
 *  See doc/datetime.rst.
 *
 */

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

/* XXX: currently defines unnecessary symbols when DUK_USE_DATE_BUILTIN is disabled. */

/*
 *  Forward declarations
 */

DUK_LOCAL_DECL duk_double_t duk__push_this_get_timeval_tzoffset(duk_hthread *thr, duk_small_uint_t flags, duk_int_t *out_tzoffset);
DUK_LOCAL_DECL duk_double_t duk__push_this_get_timeval(duk_hthread *thr, duk_small_uint_t flags);
DUK_LOCAL_DECL void duk__twodigit_year_fixup(duk_hthread *thr, duk_idx_t idx_val);
DUK_LOCAL_DECL duk_ret_t duk__set_this_timeval_from_dparts(duk_hthread *thr, duk_double_t *dparts, duk_small_uint_t flags);

/*
 *  Other file level defines
 */

/* Debug macro to print all parts and dparts (used manually because of debug level). */
#define  DUK__DPRINT_PARTS_AND_DPARTS(parts,dparts)  do { \
        DUK_D(DUK_DPRINT("parts: %ld %ld %ld %ld %ld %ld %ld %ld, dparts: %lf %lf %lf %lf %lf %lf %lf %lf", \
                         (long) (parts)[0], (long) (parts)[1], \
                         (long) (parts)[2], (long) (parts)[3], \
                         (long) (parts)[4], (long) (parts)[5], \
                         (long) (parts)[6], (long) (parts)[7], \
                         (double) (dparts)[0], (double) (dparts)[1], \
                         (double) (dparts)[2], (double) (dparts)[3], \
                         (double) (dparts)[4], (double) (dparts)[5], \
                         (double) (dparts)[6], (double) (dparts)[7])); \
    } while (0)
#define  DUK__DPRINT_PARTS(parts)  do { \
        DUK_D(DUK_DPRINT("parts: %ld %ld %ld %ld %ld %ld %ld %ld", \
                         (long) (parts)[0], (long) (parts)[1], \
                         (long) (parts)[2], (long) (parts)[3], \
                         (long) (parts)[4], (long) (parts)[5], \
                         (long) (parts)[6], (long) (parts)[7])); \
    } while (0)
#define  DUK__DPRINT_DPARTS(dparts)  do { \
        DUK_D(DUK_DPRINT("dparts: %lf %lf %lf %lf %lf %lf %lf %lf", \
                         (double) (dparts)[0], (double) (dparts)[1], \
                         (double) (dparts)[2], (double) (dparts)[3], \
                         (double) (dparts)[4], (double) (dparts)[5], \
                         (double) (dparts)[6], (double) (dparts)[7])); \
    } while (0)

/* Equivalent year for DST calculations outside [1970,2038[ range, see
 * E5 Section 15.9.1.8.  Equivalent year has the same leap-year-ness and
 * starts with the same weekday on Jan 1.
 * https://bugzilla.mozilla.org/show_bug.cgi?id=351066
 */
#define DUK__YEAR(x) ((duk_uint8_t) ((x) - 1970))
DUK_LOCAL duk_uint8_t duk__date_equivyear[14] = {
#if 1
    /* This is based on V8 EquivalentYear() algorithm (see util/genequivyear.py):
     * http://code.google.com/p/v8/source/browse/trunk/src/date.h#146
     */

    /* non-leap year: sunday, monday, ... */
    DUK__YEAR(2023), DUK__YEAR(2035), DUK__YEAR(2019), DUK__YEAR(2031),
    DUK__YEAR(2015), DUK__YEAR(2027), DUK__YEAR(2011),

    /* leap year: sunday, monday, ... */
    DUK__YEAR(2012), DUK__YEAR(2024), DUK__YEAR(2008), DUK__YEAR(2020),
    DUK__YEAR(2032), DUK__YEAR(2016), DUK__YEAR(2028)
#endif

#if 0
    /* This is based on Rhino EquivalentYear() algorithm:
     * https://github.com/mozilla/rhino/blob/f99cc11d616f0cdda2c42bde72b3484df6182947/src/org/mozilla/javascript/NativeDate.java
     */

    /* non-leap year: sunday, monday, ... */
    DUK__YEAR(1978), DUK__YEAR(1973), DUK__YEAR(1985), DUK__YEAR(1986),
    DUK__YEAR(1981), DUK__YEAR(1971), DUK__YEAR(1977),

    /* leap year: sunday, monday, ... */
    DUK__YEAR(1984), DUK__YEAR(1996), DUK__YEAR(1980), DUK__YEAR(1992),
    DUK__YEAR(1976), DUK__YEAR(1988), DUK__YEAR(1972)
#endif
};

/*
 *  ISO 8601 subset parser.
 */

/* Parser part count. */
#define DUK__NUM_ISO8601_PARSER_PARTS  9

/* Parser part indices. */
#define DUK__PI_YEAR         0
#define DUK__PI_MONTH        1
#define DUK__PI_DAY          2
#define DUK__PI_HOUR         3
#define DUK__PI_MINUTE       4
#define DUK__PI_SECOND       5
#define DUK__PI_MILLISECOND  6
#define DUK__PI_TZHOUR       7
#define DUK__PI_TZMINUTE     8

/* Parser part masks. */
#define DUK__PM_YEAR         (1 << DUK__PI_YEAR)
#define DUK__PM_MONTH        (1 << DUK__PI_MONTH)
#define DUK__PM_DAY          (1 << DUK__PI_DAY)
#define DUK__PM_HOUR         (1 << DUK__PI_HOUR)
#define DUK__PM_MINUTE       (1 << DUK__PI_MINUTE)
#define DUK__PM_SECOND       (1 << DUK__PI_SECOND)
#define DUK__PM_MILLISECOND  (1 << DUK__PI_MILLISECOND)
#define DUK__PM_TZHOUR       (1 << DUK__PI_TZHOUR)
#define DUK__PM_TZMINUTE     (1 << DUK__PI_TZMINUTE)

/* Parser separator indices. */
#define DUK__SI_PLUS         0
#define DUK__SI_MINUS        1
#define DUK__SI_T            2
#define DUK__SI_SPACE        3
#define DUK__SI_COLON        4
#define DUK__SI_PERIOD       5
#define DUK__SI_Z            6
#define DUK__SI_NUL          7

/* Parser separator masks. */
#define DUK__SM_PLUS         (1 << DUK__SI_PLUS)
#define DUK__SM_MINUS        (1 << DUK__SI_MINUS)
#define DUK__SM_T            (1 << DUK__SI_T)
#define DUK__SM_SPACE        (1 << DUK__SI_SPACE)
#define DUK__SM_COLON        (1 << DUK__SI_COLON)
#define DUK__SM_PERIOD       (1 << DUK__SI_PERIOD)
#define DUK__SM_Z            (1 << DUK__SI_Z)
#define DUK__SM_NUL          (1 << DUK__SI_NUL)

/* Rule control flags. */
#define DUK__CF_NEG          (1 << 0)  /* continue matching, set neg_tzoffset flag */
#define DUK__CF_ACCEPT       (1 << 1)  /* accept string */
#define DUK__CF_ACCEPT_NUL   (1 << 2)  /* accept string if next char is NUL (otherwise reject) */

#define DUK__PACK_RULE(partmask,sepmask,nextpart,flags)  \
    ((duk_uint32_t) (partmask) + \
     (((duk_uint32_t) (sepmask)) << 9) + \
     (((duk_uint32_t) (nextpart)) << 17) + \
     (((duk_uint32_t) (flags)) << 21))

#define DUK__UNPACK_RULE(rule,var_nextidx,var_flags)  do { \
        (var_nextidx) = (duk_small_uint_t) (((rule) >> 17) & 0x0f); \
        (var_flags) = (duk_small_uint_t) ((rule) >> 21); \
    } while (0)

#define DUK__RULE_MASK_PART_SEP  0x1ffffUL

/* Matching separator index is used in the control table */
DUK_LOCAL const duk_uint8_t duk__parse_iso8601_seps[] = {
    DUK_ASC_PLUS /*0*/, DUK_ASC_MINUS /*1*/, DUK_ASC_UC_T /*2*/, DUK_ASC_SPACE /*3*/,
    DUK_ASC_COLON /*4*/, DUK_ASC_PERIOD /*5*/, DUK_ASC_UC_Z /*6*/, DUK_ASC_NUL /*7*/
};

/* Rule table: first matching rule is used to determine what to do next. */
DUK_LOCAL const duk_uint32_t duk__parse_iso8601_control[] = {
    DUK__PACK_RULE(DUK__PM_YEAR, DUK__SM_MINUS, DUK__PI_MONTH, 0),
    DUK__PACK_RULE(DUK__PM_MONTH, DUK__SM_MINUS, DUK__PI_DAY, 0),
    DUK__PACK_RULE(DUK__PM_YEAR | DUK__PM_MONTH | DUK__PM_DAY, DUK__SM_T | DUK__SM_SPACE, DUK__PI_HOUR, 0),
    DUK__PACK_RULE(DUK__PM_HOUR, DUK__SM_COLON, DUK__PI_MINUTE, 0),
    DUK__PACK_RULE(DUK__PM_MINUTE, DUK__SM_COLON, DUK__PI_SECOND, 0),
    DUK__PACK_RULE(DUK__PM_SECOND, DUK__SM_PERIOD, DUK__PI_MILLISECOND, 0),
    DUK__PACK_RULE(DUK__PM_TZHOUR, DUK__SM_COLON, DUK__PI_TZMINUTE, 0),
    DUK__PACK_RULE(DUK__PM_YEAR | DUK__PM_MONTH | DUK__PM_DAY | DUK__PM_HOUR /*Note1*/ | DUK__PM_MINUTE | DUK__PM_SECOND | DUK__PM_MILLISECOND, DUK__SM_PLUS, DUK__PI_TZHOUR, 0),
    DUK__PACK_RULE(DUK__PM_YEAR | DUK__PM_MONTH | DUK__PM_DAY | DUK__PM_HOUR /*Note1*/ | DUK__PM_MINUTE | DUK__PM_SECOND | DUK__PM_MILLISECOND, DUK__SM_MINUS, DUK__PI_TZHOUR, DUK__CF_NEG),
    DUK__PACK_RULE(DUK__PM_YEAR | DUK__PM_MONTH | DUK__PM_DAY | DUK__PM_HOUR /*Note1*/ | DUK__PM_MINUTE | DUK__PM_SECOND | DUK__PM_MILLISECOND, DUK__SM_Z, 0, DUK__CF_ACCEPT_NUL),
    DUK__PACK_RULE(DUK__PM_YEAR | DUK__PM_MONTH | DUK__PM_DAY | DUK__PM_HOUR /*Note1*/ | DUK__PM_MINUTE | DUK__PM_SECOND | DUK__PM_MILLISECOND | DUK__PM_TZHOUR /*Note2*/ | DUK__PM_TZMINUTE, DUK__SM_NUL, 0, DUK__CF_ACCEPT)

    /* Note1: the specification doesn't require matching a time form with
     *        just hours ("HH"), but we accept it here, e.g. "2012-01-02T12Z".
     *
     * Note2: the specification doesn't require matching a timezone offset
     *        with just hours ("HH"), but accept it here, e.g. "2012-01-02T03:04:05+02"
     */
};

DUK_LOCAL duk_bool_t duk__parse_string_iso8601_subset(duk_hthread *thr, const char *str) {
    duk_int_t parts[DUK__NUM_ISO8601_PARSER_PARTS];
    duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS];
    duk_double_t d;
    const duk_uint8_t *p;
    duk_small_uint_t part_idx = 0;
    duk_int_t accum = 0;
    duk_small_uint_t ndigits = 0;
    duk_bool_t neg_year = 0;
    duk_bool_t neg_tzoffset = 0;
    duk_uint_fast8_t ch;
    duk_small_uint_t i;

    /* During parsing, month and day are one-based; set defaults here. */
    duk_memzero(parts, sizeof(parts));
    DUK_ASSERT(parts[DUK_DATE_IDX_YEAR] == 0);  /* don't care value, year is mandatory */
    parts[DUK_DATE_IDX_MONTH] = 1;
    parts[DUK_DATE_IDX_DAY] = 1;

    /* Special handling for year sign. */
    p = (const duk_uint8_t *) str;
    ch = p[0];
    if (ch == DUK_ASC_PLUS) {
        p++;
    } else if (ch == DUK_ASC_MINUS) {
        neg_year = 1;
        p++;
    }

    for (;;) {
        ch = *p++;
        DUK_DDD(DUK_DDDPRINT("parsing, part_idx=%ld, char=%ld ('%c')",
                             (long) part_idx, (long) ch,
                             (int) ((ch >= 0x20 && ch <= 0x7e) ? ch : DUK_ASC_QUESTION)));

        if (ch >= DUK_ASC_0 && ch <= DUK_ASC_9) {
            if (ndigits >= 9) {
                DUK_DDD(DUK_DDDPRINT("too many digits -> reject"));
                goto reject;
            }
            if (part_idx == DUK__PI_MILLISECOND && ndigits >= 3) {
                /* ignore millisecond fractions after 3 */
            } else {
                accum = accum * 10 + ((duk_int_t) ch) - ((duk_int_t) DUK_ASC_0) + 0x00;
                ndigits++;
            }
        } else {
            duk_uint_fast32_t match_val;
            duk_small_uint_t sep_idx;

            if (ndigits <= 0) {
                goto reject;
            }
            if (part_idx == DUK__PI_MILLISECOND) {
                /* complete the millisecond field */
                while (ndigits < 3) {
                    accum *= 10;
                    ndigits++;
                }
            }
            parts[part_idx] = accum;
            DUK_DDD(DUK_DDDPRINT("wrote part %ld -> value %ld", (long) part_idx, (long) accum));

            accum = 0;
            ndigits = 0;

            for (i = 0; i < (duk_small_uint_t) (sizeof(duk__parse_iso8601_seps) / sizeof(duk_uint8_t)); i++) {
                if (duk__parse_iso8601_seps[i] == ch) {
                    break;
                }
            }
            if (i == (duk_small_uint_t) (sizeof(duk__parse_iso8601_seps) / sizeof(duk_uint8_t))) {
                DUK_DDD(DUK_DDDPRINT("separator character doesn't match -> reject"));
                goto reject;
            }

            sep_idx = i;
            match_val = (1UL << part_idx) + (1UL << (sep_idx + 9));  /* match against rule part/sep bits */

            for (i = 0; i < (duk_small_uint_t) (sizeof(duk__parse_iso8601_control) / sizeof(duk_uint32_t)); i++) {
                duk_uint_fast32_t rule = duk__parse_iso8601_control[i];
                duk_small_uint_t nextpart;
                duk_small_uint_t cflags;

                DUK_DDD(DUK_DDDPRINT("part_idx=%ld, sep_idx=%ld, match_val=0x%08lx, considering rule=0x%08lx",
                                     (long) part_idx, (long) sep_idx,
                                     (unsigned long) match_val, (unsigned long) rule));

                if ((rule & match_val) != match_val) {
                    continue;
                }

                DUK__UNPACK_RULE(rule, nextpart, cflags);

                DUK_DDD(DUK_DDDPRINT("rule match -> part_idx=%ld, sep_idx=%ld, match_val=0x%08lx, "
                                     "rule=0x%08lx -> nextpart=%ld, cflags=0x%02lx",
                                     (long) part_idx, (long) sep_idx,
                                     (unsigned long) match_val, (unsigned long) rule,
                                     (long) nextpart, (unsigned long) cflags));

                if (cflags & DUK__CF_NEG) {
                    neg_tzoffset = 1;
                }

                if (cflags & DUK__CF_ACCEPT) {
                    goto accept;
                }

                if (cflags & DUK__CF_ACCEPT_NUL) {
                    DUK_ASSERT(*(p - 1) != (char) 0);
                    if (*p == DUK_ASC_NUL) {
                        goto accept;
                    }
                    goto reject;
                }

                part_idx = nextpart;
                break;
            }  /* rule match */

            if (i == (duk_small_uint_t) (sizeof(duk__parse_iso8601_control) / sizeof(duk_uint32_t))) {
                DUK_DDD(DUK_DDDPRINT("no rule matches -> reject"));
                goto reject;
            }

            if (ch == 0) {
                /* This shouldn't be necessary, but check just in case
                 * to avoid any chance of overruns.
                 */
                DUK_DDD(DUK_DDDPRINT("NUL after rule matching (should not happen) -> reject"));
                goto reject;
            }
        }  /* if-digit-else-ctrl */
    }  /* char loop */

    /* We should never exit the loop above. */
    DUK_UNREACHABLE();

 reject:
    DUK_DDD(DUK_DDDPRINT("reject"));
    return 0;

 accept:
    DUK_DDD(DUK_DDDPRINT("accept"));

    /* Apply timezone offset to get the main parts in UTC */
    if (neg_year) {
        parts[DUK__PI_YEAR] = -parts[DUK__PI_YEAR];
    }
    if (neg_tzoffset) {
        parts[DUK__PI_HOUR] += parts[DUK__PI_TZHOUR];
        parts[DUK__PI_MINUTE] += parts[DUK__PI_TZMINUTE];
    } else {
        parts[DUK__PI_HOUR] -= parts[DUK__PI_TZHOUR];
        parts[DUK__PI_MINUTE] -= parts[DUK__PI_TZMINUTE];
    }
    parts[DUK__PI_MONTH] -= 1;  /* zero-based month */
    parts[DUK__PI_DAY] -= 1;  /* zero-based day */

    /* Use double parts, they tolerate unnormalized time.
     *
     * Note: DUK_DATE_IDX_WEEKDAY is initialized with a bogus value (DUK__PI_TZHOUR)
     * on purpose.  It won't be actually used by duk_bi_date_get_timeval_from_dparts(),
     * but will make the value initialized just in case, and avoid any
     * potential for Valgrind issues.
     */
    for (i = 0; i < DUK_DATE_IDX_NUM_PARTS; i++) {
        DUK_DDD(DUK_DDDPRINT("part[%ld] = %ld", (long) i, (long) parts[i]));
        dparts[i] = parts[i];
    }

    d = duk_bi_date_get_timeval_from_dparts(dparts, 0 /*flags*/);
    duk_push_number(thr, d);
    return 1;
}

/*
 *  Date/time parsing helper.
 *
 *  Parse a datetime string into a time value.  We must first try to parse
 *  the input according to the standard format in E5.1 Section 15.9.1.15.
 *  If that fails, we can try to parse using custom parsing, which can
 *  either be platform neutral (custom code) or platform specific (using
 *  existing platform API calls).
 *
 *  Note in particular that we must parse whatever toString(), toUTCString(),
 *  and toISOString() can produce; see E5.1 Section 15.9.4.2.
 *
 *  Returns 1 to allow tail calling.
 *
 *  There is much room for improvement here with respect to supporting
 *  alternative datetime formats.  For instance, V8 parses '2012-01-01' as
 *  UTC and '2012/01/01' as local time.
 */

DUK_LOCAL duk_ret_t duk__parse_string(duk_hthread *thr, const char *str) {
    /* XXX: there is a small risk here: because the ISO 8601 parser is
     * very loose, it may end up parsing some datetime values which
     * would be better parsed with a platform specific parser.
     */

    DUK_ASSERT(str != NULL);
    DUK_DDD(DUK_DDDPRINT("parse datetime from string '%s'", (const char *) str));

    if (duk__parse_string_iso8601_subset(thr, str) != 0) {
        return 1;
    }

#if defined(DUK_USE_DATE_PARSE_STRING)
    /* Contract, either:
     * - Push value on stack and return 1
     * - Don't push anything on stack and return 0
     */

    if (DUK_USE_DATE_PARSE_STRING(thr, str) != 0) {
        return 1;
    }
#else
    /* No platform-specific parsing, this is not an error. */
#endif

    duk_push_nan(thr);
    return 1;
}

/*
 *  Calendar helpers
 *
 *  Some helpers are used for getters and can operate on normalized values
 *  which can be represented with 32-bit signed integers.  Other helpers are
 *  needed by setters and operate on un-normalized double values, must watch
 *  out for non-finite numbers etc.
 */

DUK_LOCAL duk_uint8_t duk__days_in_month[12] = {
    (duk_uint8_t) 31, (duk_uint8_t) 28, (duk_uint8_t) 31, (duk_uint8_t) 30,
    (duk_uint8_t) 31, (duk_uint8_t) 30, (duk_uint8_t) 31, (duk_uint8_t) 31,
    (duk_uint8_t) 30, (duk_uint8_t) 31, (duk_uint8_t) 30, (duk_uint8_t) 31
};

/* Maximum iteration count for computing UTC-to-local time offset when
 * creating an ECMAScript time value from local parts.
 */
#define DUK__LOCAL_TZOFFSET_MAXITER   4

/* Because 'day since epoch' can be negative and is used to compute weekday
 * using a modulo operation, add this multiple of 7 to avoid negative values
 * when year is below 1970 epoch.  ECMAScript time values are restricted to
 * +/- 100 million days from epoch, so this adder fits nicely into 32 bits.
 * Round to a multiple of 7 (= floor(100000000 / 7) * 7) and add margin.
 */
#define DUK__WEEKDAY_MOD_ADDER  (20000000 * 7)  /* 0x08583b00 */

DUK_INTERNAL duk_bool_t duk_bi_date_is_leap_year(duk_int_t year) {
    if ((year % 4) != 0) {
        return 0;
    }
    if ((year % 100) != 0) {
        return 1;
    }
    if ((year % 400) != 0) {
        return 0;
    }
    return 1;
}

DUK_INTERNAL duk_bool_t duk_bi_date_timeval_in_valid_range(duk_double_t x) {
    return (x >= -DUK_DATE_MSEC_100M_DAYS && x <= DUK_DATE_MSEC_100M_DAYS);
}

DUK_INTERNAL duk_bool_t duk_bi_date_timeval_in_leeway_range(duk_double_t x) {
    return (x >= -DUK_DATE_MSEC_100M_DAYS_LEEWAY && x <= DUK_DATE_MSEC_100M_DAYS_LEEWAY);
}

DUK_INTERNAL duk_bool_t duk_bi_date_year_in_valid_range(duk_double_t x) {
    return (x >= DUK_DATE_MIN_ECMA_YEAR && x <= DUK_DATE_MAX_ECMA_YEAR);
}

DUK_LOCAL duk_double_t duk__timeclip(duk_double_t x) {
    if (!DUK_ISFINITE(x)) {
        return DUK_DOUBLE_NAN;
    }

    if (!duk_bi_date_timeval_in_valid_range(x)) {
        return DUK_DOUBLE_NAN;
    }

    x = duk_js_tointeger_number(x);

    /* Here we'd have the option to normalize -0 to +0. */
    return x;
}

/* Integer division which floors also negative values correctly. */
DUK_LOCAL duk_int_t duk__div_floor(duk_int_t a, duk_int_t b) {
    DUK_ASSERT(b > 0);
    if (a >= 0) {
        return a / b;
    } else {
        /* e.g. a = -4, b = 5  -->  -4 - 5 + 1 / 5  -->  -8 / 5  -->  -1
         *      a = -5, b = 5  -->  -5 - 5 + 1 / 5  -->  -9 / 5  -->  -1
         *      a = -6, b = 5  -->  -6 - 5 + 1 / 5  -->  -10 / 5  -->  -2
         */
        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_double_equals(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(duk_double_equals(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_equals((duk_double_t) t1, d1));
    DUK_ASSERT(duk_double_equals((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;
    DUK_ASSERT(parts[DUK_DATE_IDX_MILLISECOND] >= 0 && parts[DUK_DATE_IDX_MILLISECOND] <= 999);
    DUK_ASSERT(parts[DUK_DATE_IDX_SECOND] >= 0 && parts[DUK_DATE_IDX_SECOND] <= 59);
    DUK_ASSERT(parts[DUK_DATE_IDX_MINUTE] >= 0 && parts[DUK_DATE_IDX_MINUTE] <= 59);
    DUK_ASSERT(parts[DUK_DATE_IDX_HOUR] >= 0 && parts[DUK_DATE_IDX_HOUR] <= 23);

    DUK_DDD(DUK_DDDPRINT("d=%lf, d1=%lf, d2=%lf, t1=%ld, t2=%ld, parts: hour=%ld min=%ld sec=%ld msec=%ld",
                         (double) d, (double) d1, (double) d2, (long) t1, (long) t2,
                         (long) parts[DUK_DATE_IDX_HOUR],
                         (long) parts[DUK_DATE_IDX_MINUTE],
                         (long) parts[DUK_DATE_IDX_SECOND],
                         (long) parts[DUK_DATE_IDX_MILLISECOND]));

    /* This assert depends on the input parts representing time inside
     * the ECMAScript range.
     */
    DUK_ASSERT(t2 + DUK__WEEKDAY_MOD_ADDER >= 0);
    parts[DUK_DATE_IDX_WEEKDAY] = (t2 + 4 + DUK__WEEKDAY_MOD_ADDER) % 7;  /* E5.1 Section 15.9.1.6 */
    DUK_ASSERT(parts[DUK_DATE_IDX_WEEKDAY] >= 0 && parts[DUK_DATE_IDX_WEEKDAY] <= 6);

    year = duk__year_from_day(t2, &day_in_year);
    day = day_in_year;
    is_leap = duk_bi_date_is_leap_year(year);
    for (month = 0; month < 12; month++) {
        dim = duk__days_in_month[month];
        if (month == 1 && is_leap) {
            dim++;
        }
        DUK_DDD(DUK_DDDPRINT("month=%ld, dim=%ld, day=%ld",
                             (long) month, (long) dim, (long) day));
        if (day < dim) {
            break;
        }
        day -= dim;
    }
    DUK_DDD(DUK_DDDPRINT("final month=%ld", (long) month));
    DUK_ASSERT(month >= 0 && month <= 11);
    DUK_ASSERT(day >= 0 && day <= 31);

    /* Equivalent year mapping, used to avoid DST trouble when platform
     * may fail to provide reasonable DST answers for dates outside the
     * ordinary range (e.g. 1970-2038).  An equivalent year has the same
     * leap-year-ness as the original year and begins on the same weekday
     * (Jan 1).
     *
     * The year 2038 is avoided because there seem to be problems with it
     * on some platforms.  The year 1970 is also avoided as there were
     * practical problems with it; an equivalent year is used for it too,
     * which breaks some DST computations for 1970 right now, see e.g.
     * test-bi-date-tzoffset-brute-fi.js.
     */
    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_xget_owndataprop_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 ] */

    /* Must force write because e.g. .setYear() must work even when
     * the Date instance is frozen.
     */
    duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_W);

    /* 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;

        if (tzoffset >= 0) {
            tmp = tzoffset;
            fmt = "+%02d:%02d";
        } else {
            tmp = -tzoffset;
            fmt = "-%02d:%02d";
        }
        tmp = tmp / 60;
        arg_hours = tmp / 60;
        arg_minutes = tmp % 60;
        DUK_ASSERT(arg_hours <= 24);  /* Even less is actually guaranteed for a valid tzoffset. */
        arg_hours = arg_hours & 0x3f;  /* For [0,24] this is a no-op, but fixes GCC 7 warning, see https://github.com/svaarala/duktape/issues/1602. */

        DUK_SNPRINTF(tzstr, sizeof(tzstr), fmt, (int) arg_hours, (int) arg_minutes);
        tzstr[sizeof(tzstr) - 1] = (char) 0;
    } else {
        tzstr[0] = DUK_ASC_UC_Z;
        tzstr[1] = (char) 0;
    }

    /* Unlike year, the other parts fit into 16 bits so %d format
     * is portable.
     */
    if ((flags & DUK_DATE_FLAG_TOSTRING_DATE) && (flags & DUK_DATE_FLAG_TOSTRING_TIME)) {
        DUK_SPRINTF((char *) out_buf, "%s-%02d-%02d%c%02d:%02d:%02d.%03d%s",
                    (const char *) yearstr, (int) parts[DUK_DATE_IDX_MONTH], (int) parts[DUK_DATE_IDX_DAY], (int) sep,
                    (int) parts[DUK_DATE_IDX_HOUR], (int) parts[DUK_DATE_IDX_MINUTE],
                    (int) parts[DUK_DATE_IDX_SECOND], (int) parts[DUK_DATE_IDX_MILLISECOND], (const char *) tzstr);
    } else if (flags & DUK_DATE_FLAG_TOSTRING_DATE) {
        DUK_SPRINTF((char *) out_buf, "%s-%02d-%02d",
                    (const char *) yearstr, (int) parts[DUK_DATE_IDX_MONTH], (int) parts[DUK_DATE_IDX_DAY]);
    } else {
        DUK_ASSERT(flags & DUK_DATE_FLAG_TOSTRING_TIME);
        DUK_SPRINTF((char *) out_buf, "%02d:%02d:%02d.%03d%s",
                    (int) parts[DUK_DATE_IDX_HOUR], (int) parts[DUK_DATE_IDX_MINUTE],
                    (int) parts[DUK_DATE_IDX_SECOND], (int) parts[DUK_DATE_IDX_MILLISECOND],
                    (const char *) tzstr);
    }
}

/* Helper for string conversion calls: check 'this' binding, get the
 * internal time value, and format date and/or time in a few formats.
 * Return value allows tail calls.
 */
DUK_LOCAL duk_ret_t duk__to_string_helper(duk_hthread *thr, duk_small_uint_t flags) {
    duk_double_t d;
    duk_int_t parts[DUK_DATE_IDX_NUM_PARTS];
    duk_int_t tzoffset;  /* seconds, doesn't fit into 16 bits */
    duk_bool_t rc;
    duk_uint8_t buf[DUK_BI_DATE_ISO8601_BUFSIZE];

    DUK_UNREF(rc);  /* unreferenced with some options */

    d = duk__push_this_get_timeval_tzoffset(thr, flags, &tzoffset);
    if (DUK_ISNAN(d)) {
        duk_push_hstring_stridx(thr, DUK_STRIDX_INVALID_DATE);
        return 1;
    }
    DUK_ASSERT(DUK_ISFINITE(d));

    /* formatters always get one-based month/day-of-month */
    duk_bi_date_timeval_to_parts(d, parts, NULL, DUK_DATE_FLAG_ONEBASED);
    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);

    if (flags & DUK_DATE_FLAG_TOSTRING_LOCALE) {
        /* try locale specific formatter; if it refuses to format the
         * string, fall back to an ISO 8601 formatted value in local
         * time.
         */
#if defined(DUK_USE_DATE_FORMAT_STRING)
        /* Contract, either:
         * - Push string to value stack and return 1
         * - Don't push anything and return 0
         */

        rc = DUK_USE_DATE_FORMAT_STRING(thr, parts, tzoffset, flags);
        if (rc != 0) {
            return 1;
        }
#else
        /* No locale specific formatter; this is OK, we fall back
         * to ISO 8601.
         */
#endif
    }

    /* Different calling convention than above used because the helper
     * 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 {
            /* All components default to 0 except day-of-month which defaults
             * to 1.  However, because our internal day-of-month is zero-based,
             * it also defaults to zero here.
             */
            d = 0.0;
        }
        dparts[idx] = d;
    }

    DUK_DDD(DUK_DDDPRINT("parts from args -> %lf %lf %lf %lf %lf %lf %lf %lf",
                         (double) dparts[0], (double) dparts[1],
                         (double) dparts[2], (double) dparts[3],
                         (double) dparts[4], (double) dparts[5],
                         (double) dparts[6], (double) dparts[7]));
}

/*
 *  Indirect magic value lookup for Date methods.
 *
 *  Date methods don't put their control flags into the function magic value
 *  because they wouldn't fit into a LIGHTFUNC's magic field.  Instead, the
 *  magic value is set to an index pointing to the array of control flags
 *  below.
 *
 *  This must be kept in strict sync with genbuiltins.py!
 */

static duk_uint16_t duk__date_magics[] = {
    /* 0: toString */
    DUK_DATE_FLAG_TOSTRING_DATE + DUK_DATE_FLAG_TOSTRING_TIME + DUK_DATE_FLAG_LOCALTIME,

    /* 1: toDateString */
    DUK_DATE_FLAG_TOSTRING_DATE + DUK_DATE_FLAG_LOCALTIME,

    /* 2: toTimeString */
    DUK_DATE_FLAG_TOSTRING_TIME + DUK_DATE_FLAG_LOCALTIME,

    /* 3: toLocaleString */
    DUK_DATE_FLAG_TOSTRING_DATE + DUK_DATE_FLAG_TOSTRING_TIME + DUK_DATE_FLAG_TOSTRING_LOCALE + DUK_DATE_FLAG_LOCALTIME,

    /* 4: toLocaleDateString */
    DUK_DATE_FLAG_TOSTRING_DATE + DUK_DATE_FLAG_TOSTRING_LOCALE + DUK_DATE_FLAG_LOCALTIME,

    /* 5: toLocaleTimeString */
    DUK_DATE_FLAG_TOSTRING_TIME + DUK_DATE_FLAG_TOSTRING_LOCALE + DUK_DATE_FLAG_LOCALTIME,

    /* 6: toUTCString */
    DUK_DATE_FLAG_TOSTRING_DATE + DUK_DATE_FLAG_TOSTRING_TIME,

    /* 7: toISOString */
    DUK_DATE_FLAG_TOSTRING_DATE + DUK_DATE_FLAG_TOSTRING_TIME + DUK_DATE_FLAG_NAN_TO_RANGE_ERROR + DUK_DATE_FLAG_SEP_T,

    /* 8: getFullYear */
    DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_YEAR << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 9: getUTCFullYear */
    0 + (DUK_DATE_IDX_YEAR << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 10: getMonth */
    DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_MONTH << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 11: getUTCMonth */
    0 + (DUK_DATE_IDX_MONTH << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 12: getDate */
    DUK_DATE_FLAG_ONEBASED + DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_DAY << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 13: getUTCDate */
    DUK_DATE_FLAG_ONEBASED + (DUK_DATE_IDX_DAY << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 14: getDay */
    DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_WEEKDAY << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 15: getUTCDay */
    0 + (DUK_DATE_IDX_WEEKDAY << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 16: getHours */
    DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_HOUR << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 17: getUTCHours */
    0 + (DUK_DATE_IDX_HOUR << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 18: getMinutes */
    DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_MINUTE << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 19: getUTCMinutes */
    0 + (DUK_DATE_IDX_MINUTE << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 20: getSeconds */
    DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_SECOND << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 21: getUTCSeconds */
    0 + (DUK_DATE_IDX_SECOND << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 22: getMilliseconds */
    DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_MILLISECOND << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 23: getUTCMilliseconds */
    0 + (DUK_DATE_IDX_MILLISECOND << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 24: setMilliseconds */
    DUK_DATE_FLAG_TIMESETTER + DUK_DATE_FLAG_LOCALTIME + (1 << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 25: setUTCMilliseconds */
    DUK_DATE_FLAG_TIMESETTER + (1 << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 26: setSeconds */
    DUK_DATE_FLAG_TIMESETTER + DUK_DATE_FLAG_LOCALTIME + (2 << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 27: setUTCSeconds */
    DUK_DATE_FLAG_TIMESETTER + (2 << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 28: setMinutes */
    DUK_DATE_FLAG_TIMESETTER + DUK_DATE_FLAG_LOCALTIME + (3 << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 29: setUTCMinutes */
    DUK_DATE_FLAG_TIMESETTER + (3 << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 30: setHours */
    DUK_DATE_FLAG_TIMESETTER + DUK_DATE_FLAG_LOCALTIME + (4 << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 31: setUTCHours */
    DUK_DATE_FLAG_TIMESETTER + (4 << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 32: setDate */
    DUK_DATE_FLAG_LOCALTIME + (1 << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 33: setUTCDate */
    0 + (1 << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 34: setMonth */
    DUK_DATE_FLAG_LOCALTIME + (2 << DUK_DATE_FLAG_VALUE_SHIFT),

    /* 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_double_equals(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);
    /* Must force write because .setTime() must work even when
     * the Date instance is frozen.
     */
    duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_W);
    /* -> [ 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 */

#endif  /* DUK_USE_DATE_BUILTIN */

/* automatic undefs */
#undef DUK__CF_ACCEPT
#undef DUK__CF_ACCEPT_NUL
#undef DUK__CF_NEG
#undef DUK__DPRINT_DPARTS
#undef DUK__DPRINT_PARTS
#undef DUK__DPRINT_PARTS_AND_DPARTS
#undef DUK__LOCAL_TZOFFSET_MAXITER
#undef DUK__NUM_ISO8601_PARSER_PARTS
#undef DUK__PACK_RULE
#undef DUK__PI_DAY
#undef DUK__PI_HOUR
#undef DUK__PI_MILLISECOND
#undef DUK__PI_MINUTE
#undef DUK__PI_MONTH
#undef DUK__PI_SECOND
#undef DUK__PI_TZHOUR
#undef DUK__PI_TZMINUTE
#undef DUK__PI_YEAR
#undef DUK__PM_DAY
#undef DUK__PM_HOUR
#undef DUK__PM_MILLISECOND
#undef DUK__PM_MINUTE
#undef DUK__PM_MONTH
#undef DUK__PM_SECOND
#undef DUK__PM_TZHOUR
#undef DUK__PM_TZMINUTE
#undef DUK__PM_YEAR
#undef DUK__RULE_MASK_PART_SEP
#undef DUK__SI_COLON
#undef DUK__SI_MINUS
#undef DUK__SI_NUL
#undef DUK__SI_PERIOD
#undef DUK__SI_PLUS
#undef DUK__SI_SPACE
#undef DUK__SI_T
#undef DUK__SI_Z
#undef DUK__SM_COLON
#undef DUK__SM_MINUS
#undef DUK__SM_NUL
#undef DUK__SM_PERIOD
#undef DUK__SM_PLUS
#undef DUK__SM_SPACE
#undef DUK__SM_T
#undef DUK__SM_Z
#undef DUK__UNPACK_RULE
#undef DUK__WEEKDAY_MOD_ADDER
#undef DUK__YEAR
//#line 1 "duk_bi_date_unix.c"
/*
 *  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.
     *
     *  The following has useful discussion and links:
     *
     *    https://bugzilla.mozilla.org/show_bug.cgi?id=351066
     */

    duk_bi_date_timeval_to_parts(d, parts, dparts, DUK_DATE_FLAG_EQUIVYEAR /*flags*/);
    DUK_ASSERT(parts[DUK_DATE_IDX_YEAR] >= 1970 && parts[DUK_DATE_IDX_YEAR] <= 2038);

    d = duk_bi_date_get_timeval_from_dparts(dparts, 0 /*flags*/);
    DUK_ASSERT(d >= 0 && d < 2147483648.0 * 1000.0);  /* unsigned 31-bit range */
    t = (time_t) (d / 1000.0);
    DUK_DDD(DUK_DDDPRINT("timeval: %lf -> time_t %ld", (double) d, (long) t));

    duk_memzero((void *) tms, sizeof(struct tm) * 2);

#if defined(DUK_USE_DATE_TZO_GMTIME_R)
    (void) gmtime_r(&t, &tms[0]);
    (void) localtime_r(&t, &tms[1]);
#elif defined(DUK_USE_DATE_TZO_GMTIME_S)
    (void) gmtime_s(&t, &tms[0]);
    (void) localtime_s(&t, &tms[1]);
#elif defined(DUK_USE_DATE_TZO_GMTIME)
    tm_ptr = gmtime(&t);
    duk_memcpy((void *) &tms[0], tm_ptr, sizeof(struct tm));
    tm_ptr = localtime(&t);
    duk_memcpy((void *) &tms[1], tm_ptr, sizeof(struct tm));
#else
#error internal error
#endif
    DUK_DDD(DUK_DDDPRINT("gmtime result: tm={sec:%ld,min:%ld,hour:%ld,mday:%ld,mon:%ld,year:%ld,"
                         "wday:%ld,yday:%ld,isdst:%ld}",
                         (long) tms[0].tm_sec, (long) tms[0].tm_min, (long) tms[0].tm_hour,
                         (long) tms[0].tm_mday, (long) tms[0].tm_mon, (long) tms[0].tm_year,
                         (long) tms[0].tm_wday, (long) tms[0].tm_yday, (long) tms[0].tm_isdst));
    DUK_DDD(DUK_DDDPRINT("localtime result: tm={sec:%ld,min:%ld,hour:%ld,mday:%ld,mon:%ld,year:%ld,"
                         "wday:%ld,yday:%ld,isdst:%ld}",
                         (long) tms[1].tm_sec, (long) tms[1].tm_min, (long) tms[1].tm_hour,
                         (long) tms[1].tm_mday, (long) tms[1].tm_mon, (long) tms[1].tm_year,
                         (long) tms[1].tm_wday, (long) tms[1].tm_yday, (long) tms[1].tm_isdst));

    /* tm_isdst is both an input and an output to mktime(), use 0 to
     * avoid DST handling in mktime():
     * - https://github.com/svaarala/duktape/issues/406
     * - http://stackoverflow.com/questions/8558919/mktime-and-tm-isdst
     */
    tms[0].tm_isdst = 0;
    tms[1].tm_isdst = 0;
    t1 = mktime(&tms[0]);  /* UTC */
    t2 = mktime(&tms[1]);  /* local */
    if (t1 == (time_t) -1 || t2 == (time_t) -1) {
        /* This check used to be for (t < 0) but on some platforms
         * time_t is unsigned and apparently the proper way to detect
         * an mktime() error return is the cast above.  See e.g.:
         * http://pubs.opengroup.org/onlinepubs/009695299/functions/mktime.html
         */
        goto mktime_error;
    }
    DUK_DDD(DUK_DDDPRINT("t1=%ld (utc), t2=%ld (local)", (long) t1, (long) t2));

    /* Compute final offset in seconds, positive if local time ahead of
     * UTC (returned value is UTC-to-local offset).
     *
     * difftime() returns a double, so coercion to int generates quite
     * a lot of code.  Direct subtraction is not portable, however.
     * XXX: allow direct subtraction on known platforms.
     */
#if 0
    return (duk_int_t) (t2 - t1);
#endif
    return (duk_int_t) difftime(t2, t1);

 mktime_error:
    /* XXX: return something more useful, so that caller can throw? */
    DUK_D(DUK_DPRINT("mktime() failed, d=%lf", (double) d));
    return 0;
}
#endif  /* DUK_USE_DATE_TZO_GMTIME */

#if defined(DUK_USE_DATE_PRS_STRPTIME)
DUK_INTERNAL duk_bool_t duk_bi_date_parse_string_strptime(duk_hthread *thr, const char *str) {
    struct tm tm;
    time_t t;
    char buf[DUK__STRPTIME_BUF_SIZE];

    /* Copy to buffer with slack to avoid Valgrind gripes from strptime. */
    DUK_ASSERT(str != NULL);
    duk_memzero(buf, sizeof(buf));  /* valgrind whine without this */
    DUK_SNPRINTF(buf, sizeof(buf), "%s", (const char *) str);
    buf[sizeof(buf) - 1] = (char) 0;

    DUK_DDD(DUK_DDDPRINT("parsing: '%s'", (const char *) buf));

    duk_memzero(&tm, sizeof(tm));
    if (strptime((const char *) buf, "%c", &tm) != NULL) {
        DUK_DDD(DUK_DDDPRINT("before mktime: tm={sec:%ld,min:%ld,hour:%ld,mday:%ld,mon:%ld,year:%ld,"
                             "wday:%ld,yday:%ld,isdst:%ld}",
                             (long) tm.tm_sec, (long) tm.tm_min, (long) tm.tm_hour,
                             (long) tm.tm_mday, (long) tm.tm_mon, (long) tm.tm_year,
                             (long) tm.tm_wday, (long) tm.tm_yday, (long) tm.tm_isdst));
        tm.tm_isdst = -1;  /* negative: dst info not available */

        t = mktime(&tm);
        DUK_DDD(DUK_DDDPRINT("mktime() -> %ld", (long) t));
        if (t >= 0) {
            duk_push_number(thr, ((duk_double_t) t) * 1000.0);
            return 1;
        }
    }

    return 0;
}
#endif  /* DUK_USE_DATE_PRS_STRPTIME */

#if defined(DUK_USE_DATE_PRS_GETDATE)
DUK_INTERNAL duk_bool_t duk_bi_date_parse_string_getdate(duk_hthread *thr, const char *str) {
    struct tm tm;
    duk_small_int_t rc;
    time_t t;

    /* For this to work, DATEMSK must be set, so this is not very
     * convenient for an embeddable interpreter.
     */

    duk_memzero(&tm, sizeof(struct tm));
    rc = (duk_small_int_t) getdate_r(str, &tm);
    DUK_DDD(DUK_DDDPRINT("getdate_r() -> %ld", (long) rc));

    if (rc == 0) {
        t = mktime(&tm);
        DUK_DDD(DUK_DDDPRINT("mktime() -> %ld", (long) t));
        if (t >= 0) {
            duk_push_number(thr, (duk_double_t) t);
            return 1;
        }
    }

    return 0;
}
#endif  /* DUK_USE_DATE_PRS_GETDATE */

#if defined(DUK_USE_DATE_FMT_STRFTIME)
DUK_INTERNAL duk_bool_t duk_bi_date_format_parts_strftime(duk_hthread *thr, duk_int_t *parts, duk_int_t tzoffset, duk_small_uint_t flags) {
    char buf[DUK__STRFTIME_BUF_SIZE];
    struct tm tm;
    const char *fmt;

    DUK_UNREF(tzoffset);

    /* If the platform doesn't support the entire ECMAScript range, we need
     * to return 0 so that the caller can fall back to the default formatter.
     *
     * For now, assume that if time_t is 8 bytes or more, the whole ECMAScript
     * range is supported.  For smaller time_t values (4 bytes in practice),
     * assumes that the signed 32-bit range is supported.
     *
     * XXX: detect this more correctly per platform.  The size of time_t is
     * probably not an accurate guarantee of strftime() supporting or not
     * supporting a large time range (the full ECMAScript range).
     */
    if (sizeof(time_t) < 8 &&
        (parts[DUK_DATE_IDX_YEAR] < 1970 || parts[DUK_DATE_IDX_YEAR] > 2037)) {
        /* be paranoid for 32-bit time values (even avoiding negative ones) */
        return 0;
    }

    duk_memzero(&tm, sizeof(tm));
    tm.tm_sec = parts[DUK_DATE_IDX_SECOND];
    tm.tm_min = parts[DUK_DATE_IDX_MINUTE];
    tm.tm_hour = parts[DUK_DATE_IDX_HOUR];
    tm.tm_mday = parts[DUK_DATE_IDX_DAY];       /* already one-based */
    tm.tm_mon = parts[DUK_DATE_IDX_MONTH] - 1;  /* one-based -> zero-based */
    tm.tm_year = parts[DUK_DATE_IDX_YEAR] - 1900;
    tm.tm_wday = parts[DUK_DATE_IDX_WEEKDAY];
    tm.tm_isdst = 0;

    duk_memzero(buf, sizeof(buf));
    if ((flags & DUK_DATE_FLAG_TOSTRING_DATE) && (flags & DUK_DATE_FLAG_TOSTRING_TIME)) {
        fmt = "%c";
    } else if (flags & DUK_DATE_FLAG_TOSTRING_DATE) {
        fmt = "%x";
    } else {
        DUK_ASSERT(flags & DUK_DATE_FLAG_TOSTRING_TIME);
        fmt = "%X";
    }
    (void) strftime(buf, sizeof(buf) - 1, fmt, &tm);
    DUK_ASSERT(buf[sizeof(buf) - 1] == 0);

    duk_push_string(thr, buf);
    return 1;
}
#endif  /* DUK_USE_DATE_FMT_STRFTIME */

#if defined(DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME)
DUK_INTERNAL duk_double_t duk_bi_date_get_monotonic_time_clock_gettime(void) {
    struct timespec ts;

    if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
        return (duk_double_t) ts.tv_sec * 1000.0 + (duk_double_t) ts.tv_nsec / 1000000.0;
    } else {
        DUK_D(DUK_DPRINT("clock_gettime(CLOCK_MONOTONIC) failed"));
        return 0.0;
    }
}
#endif

/* automatic undefs */
#undef DUK__STRFTIME_BUF_SIZE
#undef DUK__STRPTIME_BUF_SIZE
//#line 1 "duk_bi_date_windows.c"
/*
 *  Windows Date providers
 *
 *  Platform specific links:
 *
 *    - http://msdn.microsoft.com/en-us/library/windows/desktop/ms725473(v=vs.85).aspx
 */

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

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

#if defined(DUK_USE_DATE_NOW_WINDOWS) || defined(DUK_USE_DATE_TZO_WINDOWS)
/* Shared Windows helpers. */
DUK_LOCAL void duk__convert_systime_to_ularge(const SYSTEMTIME *st, ULARGE_INTEGER *res) {
    FILETIME ft;
    if (SystemTimeToFileTime(st, &ft) == 0) {
        DUK_D(DUK_DPRINT("SystemTimeToFileTime() failed, returning 0"));
        res->QuadPart = 0;
    } else {
        res->LowPart = ft.dwLowDateTime;
        res->HighPart = ft.dwHighDateTime;
    }
}

#if defined(DUK_USE_DATE_NOW_WINDOWS_SUBMS)
DUK_LOCAL void duk__convert_filetime_to_ularge(const FILETIME *ft, ULARGE_INTEGER *res) {
    res->LowPart = ft->dwLowDateTime;
    res->HighPart = ft->dwHighDateTime;
}
#endif  /* DUK_USE_DATE_NOW_WINDOWS_SUBMS */

DUK_LOCAL void duk__set_systime_jan1970(SYSTEMTIME *st) {
    duk_memzero((void *) st, sizeof(*st));
    st->wYear = 1970;
    st->wMonth = 1;
    st->wDayOfWeek = 4;  /* not sure whether or not needed; Thursday */
    st->wDay = 1;
    DUK_ASSERT(st->wHour == 0);
    DUK_ASSERT(st->wMinute == 0);
    DUK_ASSERT(st->wSecond == 0);
    DUK_ASSERT(st->wMilliseconds == 0);
}
#endif  /* defined(DUK_USE_DATE_NOW_WINDOWS) || defined(DUK_USE_DATE_TZO_WINDOWS) */

#if defined(DUK_USE_DATE_NOW_WINDOWS)
DUK_INTERNAL duk_double_t duk_bi_date_get_now_windows(void) {
    /* Suggested step-by-step method from documentation of RtlTimeToSecondsSince1970:
     * http://msdn.microsoft.com/en-us/library/windows/desktop/ms724928(v=vs.85).aspx
     */
    SYSTEMTIME st1, st2;
    ULARGE_INTEGER tmp1, tmp2;

    GetSystemTime(&st1);
    duk__convert_systime_to_ularge((const SYSTEMTIME *) &st1, &tmp1);

    duk__set_systime_jan1970(&st2);
    duk__convert_systime_to_ularge((const SYSTEMTIME *) &st2, &tmp2);

    /* Difference is in 100ns units, convert to milliseconds, keeping
     * fractions since Duktape 2.2.0.  This is only theoretical because
     * SYSTEMTIME is limited to milliseconds.
     */
    return (duk_double_t) ((LONGLONG) tmp1.QuadPart - (LONGLONG) tmp2.QuadPart) / 10000.0;
}
#endif  /* DUK_USE_DATE_NOW_WINDOWS */

#if defined(DUK_USE_DATE_NOW_WINDOWS_SUBMS)
DUK_INTERNAL duk_double_t duk_bi_date_get_now_windows_subms(void) {
    /* Variant of the basic algorithm using GetSystemTimePreciseAsFileTime()
     * for more accuracy.
     */
    FILETIME ft1;
    SYSTEMTIME st2;
    ULARGE_INTEGER tmp1, tmp2;

    GetSystemTimePreciseAsFileTime(&ft1);
    duk__convert_filetime_to_ularge((const FILETIME *) &ft1, &tmp1);

    duk__set_systime_jan1970(&st2);
    duk__convert_systime_to_ularge((const SYSTEMTIME *) &st2, &tmp2);

    /* Difference is in 100ns units, convert to milliseconds, keeping
     * fractions since Duktape 2.2.0.
     */
    return (duk_double_t) ((LONGLONG) tmp1.QuadPart - (LONGLONG) tmp2.QuadPart) / 10000.0;
}
#endif  /* DUK_USE_DATE_NOW_WINDOWS */

#if defined(DUK_USE_DATE_TZO_WINDOWS)
DUK_INTERNAL duk_int_t duk_bi_date_get_local_tzoffset_windows(duk_double_t d) {
    SYSTEMTIME st1;
    SYSTEMTIME st2;
    SYSTEMTIME st3;
    ULARGE_INTEGER tmp1;
    ULARGE_INTEGER tmp2;
    ULARGE_INTEGER tmp3;
    FILETIME ft1;

    /* XXX: handling of timestamps outside Windows supported range.
     * How does Windows deal with dates before 1600?  Does windows
     * support all ECMAScript years (like -200000 and +200000)?
     * Should equivalent year mapping be used here too?  If so, use
     * a shared helper (currently integrated into timeval-to-parts).
     */

    /* Use the approach described in "Remarks" of FileTimeToLocalFileTime:
     * http://msdn.microsoft.com/en-us/library/windows/desktop/ms724277(v=vs.85).aspx
     */

    duk__set_systime_jan1970(&st1);
    duk__convert_systime_to_ularge((const SYSTEMTIME *) &st1, &tmp1);
    tmp2.QuadPart = (ULONGLONG) (d * 10000.0);  /* millisec -> 100ns units since jan 1, 1970 */
    tmp2.QuadPart += tmp1.QuadPart;             /* input 'd' in Windows UTC, 100ns units */

    ft1.dwLowDateTime = tmp2.LowPart;
    ft1.dwHighDateTime = tmp2.HighPart;
    if (FileTimeToSystemTime((const FILETIME *) &ft1, &st2) == 0) {
        DUK_D(DUK_DPRINT("FileTimeToSystemTime() failed, return tzoffset 0"));
        return 0;
    }
    if (SystemTimeToTzSpecificLocalTime((LPTIME_ZONE_INFORMATION) NULL, &st2, &st3) == 0) {
        DUK_D(DUK_DPRINT("SystemTimeToTzSpecificLocalTime() failed, return tzoffset 0"));
        return 0;
    }
    duk__convert_systime_to_ularge((const SYSTEMTIME *) &st3, &tmp3);

    /* Positive if local time ahead of UTC. */
    return (duk_int_t) (((LONGLONG) tmp3.QuadPart - (LONGLONG) tmp2.QuadPart) / DUK_I64_CONSTANT(10000000));  /* seconds */
}
#endif  /* DUK_USE_DATE_TZO_WINDOWS */

#if defined(DUK_USE_DATE_TZO_WINDOWS_NO_DST)
DUK_INTERNAL duk_int_t duk_bi_date_get_local_tzoffset_windows_no_dst(duk_double_t d) {
    SYSTEMTIME st1;
    SYSTEMTIME st2;
    FILETIME ft1;
    FILETIME ft2;
    ULARGE_INTEGER tmp1;
    ULARGE_INTEGER tmp2;

    /* Do a similar computation to duk_bi_date_get_local_tzoffset_windows
     * but without accounting for daylight savings time.  Use this on
     * Windows platforms (like Durango) that don't support the
     * SystemTimeToTzSpecificLocalTime() call.
     */

    /* current time not needed for this computation */
    DUK_UNREF(d);

    duk__set_systime_jan1970(&st1);
    duk__convert_systime_to_ularge((const SYSTEMTIME *) &st1, &tmp1);

    ft1.dwLowDateTime = tmp1.LowPart;
    ft1.dwHighDateTime = tmp1.HighPart;
    if (FileTimeToLocalFileTime((const FILETIME *) &ft1, &ft2) == 0) {
        DUK_D(DUK_DPRINT("FileTimeToLocalFileTime() failed, return tzoffset 0"));
        return 0;
    }
    if (FileTimeToSystemTime((const FILETIME *) &ft2, &st2) == 0) {
        DUK_D(DUK_DPRINT("FileTimeToSystemTime() failed, return tzoffset 0"));
        return 0;
    }
    duk__convert_systime_to_ularge((const SYSTEMTIME *) &st2, &tmp2);

    return (duk_int_t) (((LONGLONG) tmp2.QuadPart - (LONGLONG) tmp1.QuadPart) / DUK_I64_CONSTANT(10000000));  /* seconds */
}
#endif  /* DUK_USE_DATE_TZO_WINDOWS_NO_DST */

#if defined(DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC)
DUK_INTERNAL duk_double_t duk_bi_date_get_monotonic_time_windows_qpc(void) {
    LARGE_INTEGER count, freq;

    /* There are legacy issues with QueryPerformanceCounter():
     * - Potential jumps: https://support.microsoft.com/en-us/help/274323/performance-counter-value-may-unexpectedly-leap-forward
     * - Differences between cores (XP): https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx#qpc_support_in_windows_versions
     *
     * We avoid these by enabling QPC by default only for Vista or later.
     */

    if (QueryPerformanceCounter(&count) && QueryPerformanceFrequency(&freq)) {
        /* XXX: QueryPerformanceFrequency() can be cached */
        return (duk_double_t) count.QuadPart / (duk_double_t) freq.QuadPart * 1000.0;
    } else {
        /* MSDN: "On systems that run Windows XP or later, the function
         * will always succeed and will thus never return zero."
         * Provide minimal error path just in case user enables this
         * feature in pre-XP Windows.
         */
        return 0.0;
    }
}
#endif  /* DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC */
//#line 1 "duk_bi_duktape.c"
/*
 *  Duktape built-ins
 *
 *  Size optimization note: it might seem that vararg multipurpose functions
 *  like fin(), enc(), and dec() are not very size optimal, but using a single
 *  user-visible ECMAScript function saves a lot of run-time footprint; each
 *  Function instance takes >100 bytes.  Using a shared native helper and a
 *  'magic' value won't save much if there are multiple Function instances
 *  anyway.
 */

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

#if defined(DUK_USE_DUKTAPE_BUILTIN)

DUK_INTERNAL duk_ret_t duk_bi_duktape_object_info(duk_hthread *thr) {
    duk_inspect_value(thr, -1);
    return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_duktape_object_act(duk_hthread *thr) {
    duk_int_t level;

    level = duk_to_int(thr, 0);
    duk_inspect_callstack_entry(thr, level);
    return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_duktape_object_gc(duk_hthread *thr) {
    duk_small_uint_t flags;

    flags = (duk_small_uint_t) duk_get_uint(thr, 0);
    duk_heap_mark_and_sweep(thr->heap, flags);

    /* XXX: Not sure what the best return value would be in the API.
     * Return true for now.
     */
    duk_push_true(thr);
    return 1;
}

#if defined(DUK_USE_FINALIZER_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_duktape_object_fin(duk_hthread *thr) {
    (void) duk_require_hobject(thr, 0);
    if (duk_get_top(thr) >= 2) {
        /* Set: currently a finalizer is disabled by setting it to
         * undefined; this does not remove the property at the moment.
         * The value could be type checked to be either a function
         * or something else; if something else, the property could
         * be deleted.  Must use duk_set_finalizer() to keep
         * DUK_HOBJECT_FLAG_HAVE_FINALIZER in sync.
         */
        duk_set_top(thr, 2);
        duk_set_finalizer(thr, 0);
        return 0;
    } else {
        /* Get. */
        DUK_ASSERT(duk_get_top(thr) == 1);
        duk_get_finalizer(thr, 0);
        return 1;
    }
}
#endif  /* DUK_USE_FINALIZER_SUPPORT */

DUK_INTERNAL duk_ret_t duk_bi_duktape_object_enc(duk_hthread *thr) {
    duk_hstring *h_str;

    /* Vararg function: must be careful to check/require arguments.
     * The JSON helpers accept invalid indices and treat them like
     * non-existent optional parameters.
     */

    h_str = duk_require_hstring(thr, 0);  /* Could reject symbols, but no point: won't match comparisons. */
    duk_require_valid_index(thr, 1);

    if (h_str == DUK_HTHREAD_STRING_HEX(thr)) {
        duk_set_top(thr, 2);
        duk_hex_encode(thr, 1);
        DUK_ASSERT_TOP(thr, 2);
    } else if (h_str == DUK_HTHREAD_STRING_BASE64(thr)) {
        duk_set_top(thr, 2);
        duk_base64_encode(thr, 1);
        DUK_ASSERT_TOP(thr, 2);
#if defined(DUK_USE_JSON_SUPPORT) && defined(DUK_USE_JX)
    } else if (h_str == DUK_HTHREAD_STRING_JX(thr)) {
        duk_bi_json_stringify_helper(thr,
                                     1 /*idx_value*/,
                                     2 /*idx_replacer*/,
                                     3 /*idx_space*/,
                                     DUK_JSON_FLAG_EXT_CUSTOM |
                                     DUK_JSON_FLAG_ASCII_ONLY |
                                     DUK_JSON_FLAG_AVOID_KEY_QUOTES /*flags*/);
#endif
#if defined(DUK_USE_JSON_SUPPORT) && defined(DUK_USE_JC)
    } else if (h_str == DUK_HTHREAD_STRING_JC(thr)) {
        duk_bi_json_stringify_helper(thr,
                                     1 /*idx_value*/,
                                     2 /*idx_replacer*/,
                                     3 /*idx_space*/,
                                     DUK_JSON_FLAG_EXT_COMPATIBLE |
                                     DUK_JSON_FLAG_ASCII_ONLY /*flags*/);
#endif
    } else {
        DUK_DCERROR_TYPE_INVALID_ARGS(thr);
    }
    return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_duktape_object_dec(duk_hthread *thr) {
    duk_hstring *h_str;

    /* Vararg function: must be careful to check/require arguments.
     * The JSON helpers accept invalid indices and treat them like
     * non-existent optional parameters.
     */

    h_str = duk_require_hstring(thr, 0);  /* Could reject symbols, but no point: won't match comparisons */
    duk_require_valid_index(thr, 1);

    if (h_str == DUK_HTHREAD_STRING_HEX(thr)) {
        duk_set_top(thr, 2);
        duk_hex_decode(thr, 1);
        DUK_ASSERT_TOP(thr, 2);
    } else if (h_str == DUK_HTHREAD_STRING_BASE64(thr)) {
        duk_set_top(thr, 2);
        duk_base64_decode(thr, 1);
        DUK_ASSERT_TOP(thr, 2);
#if defined(DUK_USE_JSON_SUPPORT) && defined(DUK_USE_JX)
    } else if (h_str == DUK_HTHREAD_STRING_JX(thr)) {
        duk_bi_json_parse_helper(thr,
                                 1 /*idx_value*/,
                                 2 /*idx_replacer*/,
                                 DUK_JSON_FLAG_EXT_CUSTOM /*flags*/);
#endif
#if defined(DUK_USE_JSON_SUPPORT) && defined(DUK_USE_JC)
    } else if (h_str == DUK_HTHREAD_STRING_JC(thr)) {
        duk_bi_json_parse_helper(thr,
                                 1 /*idx_value*/,
                                 2 /*idx_replacer*/,
                                 DUK_JSON_FLAG_EXT_COMPATIBLE /*flags*/);
#endif
    } else {
        DUK_DCERROR_TYPE_INVALID_ARGS(thr);
    }
    return 1;
}

/*
 *  Compact an object
 */

DUK_INTERNAL duk_ret_t duk_bi_duktape_object_compact(duk_hthread *thr) {
    DUK_ASSERT_TOP(thr, 1);
    duk_compact(thr, 0);
    return 1;  /* return the argument object */
}

#endif  /* DUK_USE_DUKTAPE_BUILTIN */
//#line 1 "duk_bi_encoding.c"
/*
 *  WHATWG Encoding API built-ins
 *
 *  API specification: https://encoding.spec.whatwg.org/#api
 *  Web IDL: https://www.w3.org/TR/WebIDL/
 */

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

/*
 *  Data structures for encoding/decoding
 */

typedef struct {
    duk_uint8_t *out;      /* where to write next byte(s) */
    duk_codepoint_t lead;  /* lead surrogate */
} duk__encode_context;

typedef struct {
    /* UTF-8 decoding state */
    duk_codepoint_t codepoint;  /* built up incrementally */
    duk_uint8_t upper;          /* max value of next byte (decode error otherwise) */
    duk_uint8_t lower;          /* min value of next byte (ditto) */
    duk_uint8_t needed;         /* how many more bytes we need */
    duk_uint8_t bom_handled;    /* BOM seen or no longer expected */

    /* Decoder configuration */
    duk_uint8_t fatal;
    duk_uint8_t ignore_bom;
} duk__decode_context;

/* The signed duk_codepoint_t type is used to signal a decoded codepoint
 * (>= 0) or various other states using negative values.
 */
#define DUK__CP_CONTINUE   (-1)  /* continue to next byte, no completed codepoint */
#define DUK__CP_ERROR      (-2)  /* decoding error */
#define DUK__CP_RETRY      (-3)  /* decoding error; retry last byte */

/*
 *  Raw helpers for encoding/decoding
 */

/* Emit UTF-8 (= CESU-8) encoded U+FFFD (replacement char), i.e. ef bf bd. */
DUK_LOCAL duk_uint8_t *duk__utf8_emit_repl(duk_uint8_t *ptr) {
    *ptr++ = 0xef;
    *ptr++ = 0xbf;
    *ptr++ = 0xbd;
    return ptr;
}

DUK_LOCAL void duk__utf8_decode_init(duk__decode_context *dec_ctx) {
    /* (Re)init the decoding state of 'dec_ctx' but leave decoder
     * configuration fields untouched.
     */
    dec_ctx->codepoint = 0x0000L;
    dec_ctx->upper = 0xbf;
    dec_ctx->lower = 0x80;
    dec_ctx->needed = 0;
    dec_ctx->bom_handled = 0;
}

DUK_LOCAL duk_codepoint_t duk__utf8_decode_next(duk__decode_context *dec_ctx, duk_uint8_t x) {
    /*
     *  UTF-8 algorithm based on the Encoding specification:
     *  https://encoding.spec.whatwg.org/#utf-8-decoder
     *
     *  Two main states: decoding initial byte vs. decoding continuation
     *  bytes.  Shortest length encoding is validated by restricting the
     *  allowed range of first continuation byte using 'lower' and 'upper'.
     */

    if (dec_ctx->needed == 0) {
        /* process initial byte */
        if (x <= 0x7f) {
            /* U+0000-U+007F, 1 byte (ASCII) */
            return (duk_codepoint_t) x;
        } else if (x >= 0xc2 && x <= 0xdf) {
            /* U+0080-U+07FF, 2 bytes */
            dec_ctx->needed = 1;
            dec_ctx->codepoint = x & 0x1f;
            DUK_ASSERT(dec_ctx->lower == 0x80);
            DUK_ASSERT(dec_ctx->upper == 0xbf);
            return DUK__CP_CONTINUE;
        } else if (x >= 0xe0 && x <= 0xef) {
            /* U+0800-U+FFFF, 3 bytes */
            if (x == 0xe0) {
                dec_ctx->lower = 0xa0;
                DUK_ASSERT(dec_ctx->upper == 0xbf);
            } else if (x == 0xed) {
                DUK_ASSERT(dec_ctx->lower == 0x80);
                dec_ctx->upper = 0x9f;
            }
            dec_ctx->needed = 2;
            dec_ctx->codepoint = x & 0x0f;
            return DUK__CP_CONTINUE;
        } else if (x >= 0xf0 && x <= 0xf4) {
            /* U+010000-U+10FFFF, 4 bytes */
            if (x == 0xf0) {
                dec_ctx->lower = 0x90;
                DUK_ASSERT(dec_ctx->upper == 0xbf);
            } else if (x == 0xf4) {
                DUK_ASSERT(dec_ctx->lower == 0x80);
                dec_ctx->upper = 0x8f;
            }
            dec_ctx->needed = 3;
            dec_ctx->codepoint = x & 0x07;
            return DUK__CP_CONTINUE;
        } else {
            /* not a legal initial byte */
            return DUK__CP_ERROR;
        }
    } else {
        /* process continuation byte */
        if (x >= dec_ctx->lower && x <= dec_ctx->upper) {
            dec_ctx->lower = 0x80;
            dec_ctx->upper = 0xbf;
            dec_ctx->codepoint = (dec_ctx->codepoint << 6) | (x & 0x3f);
            if (--dec_ctx->needed > 0) {
                /* need more bytes */
                return DUK__CP_CONTINUE;
            } else {
                /* got a codepoint */
                duk_codepoint_t ret;
                DUK_ASSERT(dec_ctx->codepoint <= 0x10ffffL);  /* Decoding rules guarantee. */
                ret = dec_ctx->codepoint;
                dec_ctx->codepoint = 0x0000L;
                dec_ctx->needed = 0;
                return ret;
            }
        } else {
            /* We just encountered an illegal UTF-8 continuation byte.  This might
             * be the initial byte of the next character; if we return a plain
             * error status and the decoder is in replacement mode, the character
             * will be masked.  We still need to alert the caller to the error
             * though.
             */
            dec_ctx->codepoint = 0x0000L;
            dec_ctx->needed = 0;
            dec_ctx->lower = 0x80;
            dec_ctx->upper = 0xbf;
            return DUK__CP_RETRY;
        }
    }
}

#if defined(DUK_USE_ENCODING_BUILTINS)
DUK_LOCAL void duk__utf8_encode_char(void *udata, duk_codepoint_t codepoint) {
    duk__encode_context *enc_ctx;

    DUK_ASSERT(codepoint >= 0);
    enc_ctx = (duk__encode_context *) udata;
    DUK_ASSERT(enc_ctx != NULL);

#if !defined(DUK_USE_PREFER_SIZE)
    if (codepoint <= 0x7f && enc_ctx->lead == 0x0000L) {
        /* Fast path for ASCII. */
        *enc_ctx->out++ = (duk_uint8_t) codepoint;
        return;
    }
#endif

    if (DUK_UNLIKELY(codepoint > 0x10ffffL)) {
        /* cannot legally encode in UTF-8 */
        codepoint = DUK_UNICODE_CP_REPLACEMENT_CHARACTER;
    } else if (codepoint >= 0xd800L && codepoint <= 0xdfffL) {
        if (codepoint <= 0xdbffL) {
            /* high surrogate */
            duk_codepoint_t prev_lead = enc_ctx->lead;
            enc_ctx->lead = codepoint;
            if (prev_lead == 0x0000L) {
                /* high surrogate, no output */
                return;
            } else {
                /* consecutive high surrogates, consider first one unpaired */
                codepoint = DUK_UNICODE_CP_REPLACEMENT_CHARACTER;
            }
        } else {
            /* low surrogate */
            if (enc_ctx->lead != 0x0000L) {
                codepoint = (duk_codepoint_t) (0x010000L + ((enc_ctx->lead - 0xd800L) << 10) + (codepoint - 0xdc00L));
                enc_ctx->lead = 0x0000L;
            } else {
                /* unpaired low surrogate */
                DUK_ASSERT(enc_ctx->lead == 0x0000L);
                codepoint = DUK_UNICODE_CP_REPLACEMENT_CHARACTER;
            }
        }
    } else {
        if (enc_ctx->lead != 0x0000L) {
            /* unpaired high surrogate: emit replacement character and the input codepoint */
            enc_ctx->lead = 0x0000L;
            enc_ctx->out = duk__utf8_emit_repl(enc_ctx->out);
        }
    }

    /* Codepoint may be original input, a decoded surrogate pair, or may
     * have been replaced with U+FFFD.
     */
    enc_ctx->out += duk_unicode_encode_xutf8((duk_ucodepoint_t) codepoint, enc_ctx->out);
}
#endif  /* DUK_USE_ENCODING_BUILTINS */

/* Shared helper for buffer-to-string using a TextDecoder() compatible UTF-8
 * decoder.
 */
DUK_LOCAL duk_ret_t duk__decode_helper(duk_hthread *thr, duk__decode_context *dec_ctx) {
    const duk_uint8_t *input;
    duk_size_t len = 0;
    duk_size_t len_tmp;
    duk_bool_t stream = 0;
    duk_codepoint_t codepoint;
    duk_uint8_t *output;
    const duk_uint8_t *in;
    duk_uint8_t *out;

    DUK_ASSERT(dec_ctx != NULL);

    /* Careful with input buffer pointer: any side effects involving
     * code execution (e.g. getters, coercion calls, and finalizers)
     * may cause a resize and invalidate a pointer we've read.  This
     * is why the pointer is actually looked up at the last minute.
     * Argument validation must still happen first to match WHATWG
     * required side effect order.
     */

    if (duk_is_undefined(thr, 0)) {
        duk_push_fixed_buffer_nozero(thr, 0);
        duk_replace(thr, 0);
    }
    (void) duk_require_buffer_data(thr, 0, &len);  /* Need 'len', avoid pointer. */

    if (duk_check_type_mask(thr, 1, DUK_TYPE_MASK_UNDEFINED |
                                    DUK_TYPE_MASK_NULL |
                                    DUK_TYPE_MASK_NONE)) {
        /* Use defaults, treat missing value like undefined. */
    } else {
        duk_require_type_mask(thr, 1, DUK_TYPE_MASK_UNDEFINED |
                                          DUK_TYPE_MASK_NULL |
                                          DUK_TYPE_MASK_LIGHTFUNC |
                                          DUK_TYPE_MASK_BUFFER |
                                      DUK_TYPE_MASK_OBJECT);
        if (duk_get_prop_literal(thr, 1, "stream")) {
            stream = duk_to_boolean(thr, -1);
        }
    }

    /* Allowance is 3*len in the general case because all bytes may potentially
     * become U+FFFD.  If the first byte completes a non-BMP codepoint it will
     * decode to a CESU-8 surrogate pair (6 bytes) so we allow 3 extra bytes to
     * compensate: (1*3)+3 = 6.  Non-BMP codepoints are safe otherwise because
     * the 4->6 expansion is well under the 3x allowance.
     *
     * XXX: As with TextEncoder, need a better buffer allocation strategy here.
     */
    if (len >= (DUK_HBUFFER_MAX_BYTELEN / 3) - 3) {
        DUK_ERROR_TYPE(thr, DUK_STR_RESULT_TOO_LONG);
        DUK_WO_NORETURN(return 0;);
    }
    output = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, 3 + (3 * len));  /* used parts will be always manually written over */

    input = (const duk_uint8_t *) duk_get_buffer_data(thr, 0, &len_tmp);
    DUK_ASSERT(input != NULL || len == 0);
    if (DUK_UNLIKELY(len != len_tmp)) {
        /* Very unlikely but possible: source buffer was resized by
         * a side effect when fixed buffer was pushed.  Output buffer
         * may not be large enough to hold output, so just fail if
         * length has changed.
         */
        DUK_D(DUK_DPRINT("input buffer resized by side effect, fail"));
        goto fail_type;
    }

    /* From this point onwards it's critical that no side effect occur
     * which may disturb 'input': finalizer execution, property accesses,
     * active coercions, etc.  Even an allocation related mark-and-sweep
     * may affect the pointer because it may trigger a pending finalizer.
     */

    in = input;
    out = output;
    while (in < input + len) {
        codepoint = duk__utf8_decode_next(dec_ctx, *in++);
        if (codepoint < 0) {
            if (codepoint == DUK__CP_CONTINUE) {
                continue;
            }

            /* Decoding error with or without retry. */
            DUK_ASSERT(codepoint == DUK__CP_ERROR || codepoint == DUK__CP_RETRY);
            if (codepoint == DUK__CP_RETRY) {
                --in;  /* retry last byte */
            }
            /* replacement mode: replace with U+FFFD */
            codepoint = DUK_UNICODE_CP_REPLACEMENT_CHARACTER;
            if (dec_ctx->fatal) {
                /* fatal mode: throw a TypeError */
                goto fail_type;
            }
            /* Continue with 'codepoint', Unicode replacement. */
        }
        DUK_ASSERT(codepoint >= 0x0000L && codepoint <= 0x10ffffL);

        if (!dec_ctx->bom_handled) {
            dec_ctx->bom_handled = 1;
            if (codepoint == 0xfeffL && !dec_ctx->ignore_bom) {
                continue;
            }
        }

        out += duk_unicode_encode_cesu8((duk_ucodepoint_t) codepoint, out);
        DUK_ASSERT(out <= output + (3 + (3 * len)));
    }

    if (!stream) {
        if (dec_ctx->needed != 0) {
            /* truncated sequence at end of buffer */
            if (dec_ctx->fatal) {
                goto fail_type;
            } else {
                out += duk_unicode_encode_cesu8(DUK_UNICODE_CP_REPLACEMENT_CHARACTER, out);
                DUK_ASSERT(out <= output + (3 + (3 * len)));
            }
        }
        duk__utf8_decode_init(dec_ctx);  /* Initialize decoding state for potential reuse. */
    }

    /* Output buffer is fixed and thus stable even if there had been
     * side effects (which there shouldn't be).
     */
    duk_push_lstring(thr, (const char *) output, (duk_size_t) (out - output));
    return 1;

 fail_type:
    DUK_ERROR_TYPE(thr, DUK_STR_UTF8_DECODE_FAILED);
    DUK_WO_NORETURN(return 0;);
}

/*
 *  Built-in bindings
 */

#if defined(DUK_USE_ENCODING_BUILTINS)
DUK_INTERNAL duk_ret_t duk_bi_textencoder_constructor(duk_hthread *thr) {
    /* TextEncoder currently requires no persistent state, so the constructor
     * does nothing on purpose.
     */

    duk_require_constructor_call(thr);
    return 0;
}

DUK_INTERNAL duk_ret_t duk_bi_textencoder_prototype_encoding_getter(duk_hthread *thr) {
    duk_push_literal(thr, "utf-8");
    return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_textencoder_prototype_encode(duk_hthread *thr) {
    duk__encode_context enc_ctx;
    duk_size_t len;
    duk_size_t final_len;
    duk_uint8_t *output;

    DUK_ASSERT_TOP(thr, 1);
    if (duk_is_undefined(thr, 0)) {
        len = 0;
    } else {
        duk_hstring *h_input;

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

        len = (duk_size_t) DUK_HSTRING_GET_CHARLEN(h_input);
        if (len >= DUK_HBUFFER_MAX_BYTELEN / 3) {
            DUK_ERROR_TYPE(thr, DUK_STR_RESULT_TOO_LONG);
            DUK_WO_NORETURN(return 0;);
        }
    }

    /* Allowance is 3*len because all bytes can potentially be replaced with
     * U+FFFD -- which rather inconveniently encodes to 3 bytes in UTF-8.
     * Rely on dynamic buffer data pointer stability: no other code has
     * access to the data pointer.
     *
     * XXX: The buffer allocation strategy used here is rather inefficient.
     * Maybe switch to a chunk-based strategy, or preprocess the string to
     * figure out the space needed ahead of time?
     */
    DUK_ASSERT(3 * len >= len);
    output = (duk_uint8_t *) duk_push_dynamic_buffer(thr, 3 * len);

    if (len > 0) {
        DUK_ASSERT(duk_is_string(thr, 0));  /* True if len > 0. */

        /* XXX: duk_decode_string() is used to process the input
         * string.  For standard ECMAScript strings, represented
         * internally as CESU-8, this is fine.  However, behavior
         * beyond CESU-8 is not very strict: codepoints using an
         * extended form of UTF-8 are also accepted, and invalid
         * codepoint sequences (which are allowed in Duktape strings)
         * are not handled as well as they could (e.g. invalid
         * continuation bytes may mask following codepoints).
         * This is how ECMAScript code would also see such strings.
         * Maybe replace duk_decode_string() with an explicit strict
         * CESU-8 decoder here?
         */
        enc_ctx.lead = 0x0000L;
        enc_ctx.out = output;
        duk_decode_string(thr, 0, duk__utf8_encode_char, (void *) &enc_ctx);
        if (enc_ctx.lead != 0x0000L) {
            /* unpaired high surrogate at end of string */
            enc_ctx.out = duk__utf8_emit_repl(enc_ctx.out);
            DUK_ASSERT(enc_ctx.out <= output + (3 * len));
        }

        /* The output buffer is usually very much oversized, so shrink it to
         * actually needed size.  Pointer stability assumed up to this point.
         */
        DUK_ASSERT_TOP(thr, 2);
        DUK_ASSERT(output == (duk_uint8_t *) duk_get_buffer_data(thr, -1, NULL));

        final_len = (duk_size_t) (enc_ctx.out - output);
        duk_resize_buffer(thr, -1, final_len);
        /* 'output' and 'enc_ctx.out' are potentially invalidated by the resize. */
    } else {
        final_len = 0;
    }

    /* Standard WHATWG output is a Uint8Array.  Here the Uint8Array will
     * be backed by a dynamic buffer which differs from e.g. Uint8Arrays
     * created as 'new Uint8Array(N)'.  ECMAScript code won't see the
     * difference but C code will.  When bufferobjects are not supported,
     * returns a plain dynamic buffer.
     */
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
    duk_push_buffer_object(thr, -1, 0, final_len, DUK_BUFOBJ_UINT8ARRAY);
#endif
    return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_textdecoder_constructor(duk_hthread *thr) {
    duk__decode_context *dec_ctx;
    duk_bool_t fatal = 0;
    duk_bool_t ignore_bom = 0;

    DUK_ASSERT_TOP(thr, 2);
    duk_require_constructor_call(thr);
    if (!duk_is_undefined(thr, 0)) {
        /* XXX: For now ignore 'label' (encoding identifier). */
        duk_to_string(thr, 0);
    }
    if (!duk_is_null_or_undefined(thr, 1)) {
        if (duk_get_prop_literal(thr, 1, "fatal")) {
            fatal = duk_to_boolean(thr, -1);
        }
        if (duk_get_prop_literal(thr, 1, "ignoreBOM")) {
            ignore_bom = duk_to_boolean(thr, -1);
        }
    }

    duk_push_this(thr);

    /* The decode context is not assumed to be zeroed; all fields are
     * initialized explicitly.
     */
    dec_ctx = (duk__decode_context *) duk_push_fixed_buffer(thr, sizeof(duk__decode_context));
    dec_ctx->fatal = (duk_uint8_t) fatal;
    dec_ctx->ignore_bom = (duk_uint8_t) ignore_bom;
    duk__utf8_decode_init(dec_ctx);  /* Initializes remaining fields. */

    duk_put_prop_literal(thr, -2, DUK_INTERNAL_SYMBOL("Context"));
    return 0;
}

/* Get TextDecoder context from 'this'; leaves garbage on stack. */
DUK_LOCAL duk__decode_context *duk__get_textdecoder_context(duk_hthread *thr) {
    duk__decode_context *dec_ctx;
    duk_push_this(thr);
    duk_get_prop_literal(thr, -1, DUK_INTERNAL_SYMBOL("Context"));
    dec_ctx = (duk__decode_context *) duk_require_buffer(thr, -1, NULL);
    DUK_ASSERT(dec_ctx != NULL);
    return dec_ctx;
}

DUK_INTERNAL duk_ret_t duk_bi_textdecoder_prototype_shared_getter(duk_hthread *thr) {
    duk__decode_context *dec_ctx;
    duk_int_t magic;

    dec_ctx = duk__get_textdecoder_context(thr);
    magic = duk_get_current_magic(thr);
    switch (magic) {
    case 0:
        /* Encoding is now fixed, so _Context lookup is only needed to
         * validate the 'this' binding (TypeError if not TextDecoder-like).
         */
        duk_push_literal(thr, "utf-8");
        break;
    case 1:
        duk_push_boolean(thr, dec_ctx->fatal);
        break;
    default:
        duk_push_boolean(thr, dec_ctx->ignore_bom);
        break;
    }

    return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_textdecoder_prototype_decode(duk_hthread *thr) {
    duk__decode_context *dec_ctx;

    dec_ctx = duk__get_textdecoder_context(thr);
    return duk__decode_helper(thr, dec_ctx);
}
#endif  /* DUK_USE_ENCODING_BUILTINS */

/*
 *  Internal helper for Node.js Buffer
 */

/* Internal helper used for Node.js Buffer .toString().  Value stack convention
 * is currently odd: it mimics TextDecoder .decode() so that argument must be at
 * index 0, and decode options (not present for Buffer) at index 1.  Return value
 * is a Duktape/C function return value.
 */
DUK_INTERNAL duk_ret_t duk_textdecoder_decode_utf8_nodejs(duk_hthread *thr) {
    duk__decode_context dec_ctx;

    dec_ctx.fatal = 0;  /* use replacement chars */
    dec_ctx.ignore_bom = 1;  /* ignore BOMs (matches Node.js Buffer .toString()) */
    duk__utf8_decode_init(&dec_ctx);

    return duk__decode_helper(thr, &dec_ctx);
}

/* automatic undefs */
#undef DUK__CP_CONTINUE
#undef DUK__CP_ERROR
#undef DUK__CP_RETRY
//#line 1 "duk_bi_error.c"
/*
 *  Error built-ins
 */

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

DUK_INTERNAL duk_ret_t duk_bi_error_constructor_shared(duk_hthread *thr) {
    /* Behavior for constructor and non-constructor call is
     * the same except for augmenting the created error.  When
     * called as a constructor, the caller (duk_new()) will handle
     * augmentation; when called as normal function, we need to do
     * it here.
     */

    duk_small_int_t bidx_prototype = duk_get_current_magic(thr);

    /* same for both error and each subclass like TypeError */
    duk_uint_t flags_and_class = DUK_HOBJECT_FLAG_EXTENSIBLE |
                                 DUK_HOBJECT_FLAG_FASTREFS |
                                 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ERROR);

    (void) duk_push_object_helper(thr, flags_and_class, bidx_prototype);

    /* If message is undefined, the own property 'message' is not set at
     * all to save property space.  An empty message is inherited anyway.
     */
    if (!duk_is_undefined(thr, 0)) {
        duk_to_string(thr, 0);
        duk_dup_0(thr);  /* [ message error message ] */
        duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC);
    }

    /* Augment the error if called as a normal function.  __FILE__ and __LINE__
     * are not desirable in this case.
     */

#if defined(DUK_USE_AUGMENT_ERROR_CREATE)
    if (!duk_is_constructor_call(thr)) {
        duk_err_augment_error_create(thr, thr, NULL, 0, DUK_AUGMENT_FLAG_NOBLAME_FILELINE);
    }
#endif

    return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_error_prototype_to_string(duk_hthread *thr) {
    /* XXX: optimize with more direct internal access */

    duk_push_this(thr);
    (void) duk_require_hobject_promote_mask(thr, -1, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);

    /* [ ... this ] */

    duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_NAME);
    if (duk_is_undefined(thr, -1)) {
        duk_pop(thr);
        duk_push_literal(thr, "Error");
    } else {
        duk_to_string(thr, -1);
    }

    /* [ ... this name ] */

    /* XXX: Are steps 6 and 7 in E5 Section 15.11.4.4 duplicated by
     * accident or are they actually needed?  The first ToString()
     * could conceivably return 'undefined'.
     */
    duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_MESSAGE);
    if (duk_is_undefined(thr, -1)) {
        duk_pop(thr);
        duk_push_hstring_empty(thr);
    } else {
        duk_to_string(thr, -1);
    }

    /* [ ... this name message ] */

    if (duk_get_length(thr, -2) == 0) {
        /* name is empty -> return message */
        return 1;
    }
    if (duk_get_length(thr, -1) == 0) {
        /* message is empty -> return name */
        duk_pop(thr);
        return 1;
    }
    duk_push_literal(thr, ": ");
    duk_insert(thr, -2);  /* ... name ': ' message */
    duk_concat(thr, 3);

    return 1;
}

#if defined(DUK_USE_TRACEBACKS)

/*
 *  Traceback handling
 *
 *  The unified helper decodes the traceback and produces various requested
 *  outputs.  It should be optimized for size, and may leave garbage on stack,
 *  only the topmost return value matters.  For instance, traceback separator
 *  and decoded strings are pushed even when looking for filename only.
 *
 *  NOTE: although _Tracedata is an internal property, user code can currently
 *  write to the array (or replace it with something other than an array).
 *  The code below must tolerate arbitrary _Tracedata.  It can throw errors
 *  etc, but cannot cause a segfault or memory unsafe behavior.
 */

/* constants arbitrary, chosen for small loads */
#define DUK__OUTPUT_TYPE_TRACEBACK   (-1)
#define DUK__OUTPUT_TYPE_FILENAME    0
#define DUK__OUTPUT_TYPE_LINENUMBER  1

DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_hthread *thr, duk_small_int_t output_type) {
    duk_idx_t idx_td;
    duk_small_int_t i;  /* traceback depth fits into 16 bits */
    duk_small_int_t t;  /* stack type fits into 16 bits */
    duk_small_int_t count_func = 0;  /* traceback depth ensures fits into 16 bits */
    const char *str_tailcall = " tailcall";
    const char *str_strict = " strict";
    const char *str_construct = " construct";
    const char *str_prevyield = " preventsyield";
    const char *str_directeval = " directeval";
    const char *str_empty = "";

    DUK_ASSERT_TOP(thr, 0);  /* fixed arg count */

    duk_push_this(thr);
    duk_xget_owndataprop_stridx_short(thr, -1, DUK_STRIDX_INT_TRACEDATA);
    idx_td = duk_get_top_index(thr);

    duk_push_hstring_stridx(thr, DUK_STRIDX_NEWLINE_4SPACE);
    duk_push_this(thr);

    /* [ ... this tracedata sep this ] */

    /* XXX: skip null filename? */

    if (duk_check_type(thr, idx_td, DUK_TYPE_OBJECT)) {
        /* Current tracedata contains 2 entries per callstack entry. */
        for (i = 0; ; i += 2) {
            duk_int_t pc;
            duk_uint_t line;
            duk_uint_t flags;
            duk_double_t d;
            const char *funcname;
            const char *filename;
            duk_hobject *h_func;
            duk_hstring *h_name;

            duk_require_stack(thr, 5);
            duk_get_prop_index(thr, idx_td, (duk_uarridx_t) i);
            duk_get_prop_index(thr, idx_td, (duk_uarridx_t) (i + 1));
            d = duk_to_number_m1(thr);
            pc = duk_double_to_int_t(DUK_FMOD(d, DUK_DOUBLE_2TO32));
            flags = duk_double_to_uint_t(DUK_FLOOR(d / DUK_DOUBLE_2TO32));
            t = (duk_small_int_t) duk_get_type(thr, -2);

            if (t == DUK_TYPE_OBJECT || t == DUK_TYPE_LIGHTFUNC) {
                /*
                 *  ECMAScript/native function call or lightfunc call
                 */

                count_func++;

                /* [ ... v1(func) v2(pc+flags) ] */

                /* These may be systematically omitted by Duktape
                 * with certain config options, but allow user to
                 * set them on a case-by-case basis.
                 */
                duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_NAME);
                duk_get_prop_stridx_short(thr, -3, DUK_STRIDX_FILE_NAME);

#if defined(DUK_USE_PC2LINE)
                line = (duk_uint_t) duk_hobject_pc2line_query(thr, -4, (duk_uint_fast32_t) pc);
#else
                line = 0;
#endif

                /* [ ... v1 v2 name filename ] */

                /* When looking for .fileName/.lineNumber, blame first
                 * function which has a .fileName.
                 */
                if (duk_is_string_notsymbol(thr, -1)) {
                    if (output_type == DUK__OUTPUT_TYPE_FILENAME) {
                        return 1;
                    } else if (output_type == DUK__OUTPUT_TYPE_LINENUMBER) {
                        duk_push_uint(thr, line);
                        return 1;
                    }
                }

                /* XXX: Change 'anon' handling here too, to use empty string for anonymous functions? */
                /* XXX: Could be improved by coercing to a readable duk_tval (especially string escaping) */
                h_name = duk_get_hstring_notsymbol(thr, -2);  /* may be NULL */
                funcname = (h_name == NULL || h_name == DUK_HTHREAD_STRING_EMPTY_STRING(thr)) ?
                           "[anon]" : (const char *) DUK_HSTRING_GET_DATA(h_name);
                filename = duk_get_string_notsymbol(thr, -1);
                filename = filename ? filename : "";
                DUK_ASSERT(funcname != NULL);
                DUK_ASSERT(filename != NULL);

                h_func = duk_get_hobject(thr, -4);  /* NULL for lightfunc */

                if (h_func == NULL) {
                    duk_push_sprintf(thr, "at %s light%s%s%s%s%s",
                                     (const char *) funcname,
                                     (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty),
                                     (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcall : str_empty),
                                     (const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty),
                                     (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty),
                                     (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty));
                } else if (DUK_HOBJECT_HAS_NATFUNC(h_func)) {
                    duk_push_sprintf(thr, "at %s (%s) native%s%s%s%s%s",
                                     (const char *) funcname,
                                     (const char *) filename,
                                     (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty),
                                     (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcall : str_empty),
                                     (const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty),
                                     (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty),
                                     (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty));
                } else {
                    duk_push_sprintf(thr, "at %s (%s:%lu)%s%s%s%s%s",
                                     (const char *) funcname,
                                     (const char *) filename,
                                     (unsigned long) line,
                                     (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty),
                                     (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcall : str_empty),
                                     (const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty),
                                     (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty),
                                     (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty));
                }
                duk_replace(thr, -5);   /* [ ... v1 v2 name filename str ] -> [ ... str v2 name filename ] */
                duk_pop_3(thr);         /* -> [ ... str ] */
            } else if (t == DUK_TYPE_STRING) {
                const char *str_file;

                /*
                 *  __FILE__ / __LINE__ entry, here 'pc' is line number directly.
                 *  Sometimes __FILE__ / __LINE__ is reported as the source for
                 *  the error (fileName, lineNumber), sometimes not.
                 */

                /* [ ... v1(filename) v2(line+flags) ] */

                /* When looking for .fileName/.lineNumber, blame compilation
                 * or C call site unless flagged not to do so.
                 */
                if (!(flags & DUK_TB_FLAG_NOBLAME_FILELINE)) {
                    if (output_type == DUK__OUTPUT_TYPE_FILENAME) {
                        duk_pop(thr);
                        return 1;
                    } else if (output_type == DUK__OUTPUT_TYPE_LINENUMBER) {
                        duk_push_int(thr, pc);
                        return 1;
                    }
                }

                /* Tracedata is trusted but avoid any risk of using a NULL
                 * for %s format because it has undefined behavior.  Symbols
                 * don't need to be explicitly rejected as they pose no memory
                 * safety issues.
                 */
                str_file = (const char *) duk_get_string(thr, -2);
                duk_push_sprintf(thr, "at [anon] (%s:%ld) internal",
                                 (const char *) (str_file ? str_file : "null"), (long) pc);
                duk_replace(thr, -3);  /* [ ... v1 v2 str ] -> [ ... str v2 ] */
                duk_pop(thr);          /* -> [ ... str ] */
            } else {
                /* unknown, ignore */
                duk_pop_2(thr);
                break;
            }
        }

        if (count_func >= DUK_USE_TRACEBACK_DEPTH) {
            /* Possibly truncated; there is no explicit truncation
             * marker so this is the best we can do.
             */

            duk_push_hstring_stridx(thr, DUK_STRIDX_BRACKETED_ELLIPSIS);
        }
    }

    /* [ ... this tracedata sep this str1 ... strN ] */

    if (output_type != DUK__OUTPUT_TYPE_TRACEBACK) {
        return 0;
    } else {
        /* The 'this' after 'sep' will get ToString() coerced by
         * duk_join() automatically.  We don't want to do that
         * coercion when providing .fileName or .lineNumber (GH-254).
         */
        duk_join(thr, duk_get_top(thr) - (idx_td + 2) /*count, not including sep*/);
        return 1;
    }
}

/* XXX: Output type could be encoded into native function 'magic' value to
 * save space.  For setters the stridx could be encoded into 'magic'.
 */

DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_getter(duk_hthread *thr) {
    return duk__error_getter_helper(thr, DUK__OUTPUT_TYPE_TRACEBACK);
}

DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_getter(duk_hthread *thr) {
    return duk__error_getter_helper(thr, DUK__OUTPUT_TYPE_FILENAME);
}

DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_getter(duk_hthread *thr) {
    return duk__error_getter_helper(thr, DUK__OUTPUT_TYPE_LINENUMBER);
}

#else  /* DUK_USE_TRACEBACKS */

/*
 *  Traceback handling when tracebacks disabled.
 *
 *  The fileName / lineNumber stubs are now necessary because built-in
 *  data will include the accessor properties in Error.prototype.  If those
 *  are removed for builds without tracebacks, these can also be removed.
 *  'stack' should still be present and produce a ToString() equivalent:
 *  this is useful for user code which prints a stacktrace and expects to
 *  see something useful.  A normal stacktrace also begins with a ToString()
 *  of the error so this makes sense.
 */

DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_getter(duk_hthread *thr) {
    /* XXX: remove this native function and map 'stack' accessor
     * to the toString() implementation directly.
     */
    return duk_bi_error_prototype_to_string(thr);
}

DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_getter(duk_hthread *thr) {
    DUK_UNREF(thr);
    return 0;
}

DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_getter(duk_hthread *thr) {
    DUK_UNREF(thr);
    return 0;
}

#endif  /* DUK_USE_TRACEBACKS */

DUK_LOCAL duk_ret_t duk__error_setter_helper(duk_hthread *thr, duk_small_uint_t stridx_key) {
    /* Attempt to write 'stack', 'fileName', 'lineNumber' works as if
     * user code called Object.defineProperty() to create an overriding
     * own property.  This allows user code to overwrite .fileName etc
     * intuitively as e.g. "err.fileName = 'dummy'" as one might expect.
     * See https://github.com/svaarala/duktape/issues/387.
     */

    DUK_ASSERT_TOP(thr, 1);  /* fixed arg count: value */

    duk_push_this(thr);
    duk_push_hstring_stridx(thr, stridx_key);
    duk_dup_0(thr);

    /* [ ... obj key value ] */

    DUK_DD(DUK_DDPRINT("error setter: %!T %!T %!T",
                       duk_get_tval(thr, -3), duk_get_tval(thr, -2), duk_get_tval(thr, -1)));

    duk_def_prop(thr, -3, DUK_DEFPROP_HAVE_VALUE |
                          DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_WRITABLE |
                          DUK_DEFPROP_HAVE_ENUMERABLE | /*not enumerable*/
                          DUK_DEFPROP_HAVE_CONFIGURABLE | DUK_DEFPROP_CONFIGURABLE);
    return 0;
}

DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_setter(duk_hthread *thr) {
    return duk__error_setter_helper(thr, DUK_STRIDX_STACK);
}

DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_setter(duk_hthread *thr) {
    return duk__error_setter_helper(thr, DUK_STRIDX_FILE_NAME);
}

DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_setter(duk_hthread *thr) {
    return duk__error_setter_helper(thr, DUK_STRIDX_LINE_NUMBER);
}

/* automatic undefs */
#undef DUK__OUTPUT_TYPE_FILENAME
#undef DUK__OUTPUT_TYPE_LINENUMBER
#undef DUK__OUTPUT_TYPE_TRACEBACK
//#line 1 "duk_bi_function.c"
/*
 *  Function built-ins
 */

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

/* Needed even when Function built-in is disabled. */
DUK_INTERNAL duk_ret_t duk_bi_function_prototype(duk_hthread *thr) {
    /* ignore arguments, return undefined (E5 Section 15.3.4) */
    DUK_UNREF(thr);
    return 0;
}

#if defined(DUK_USE_FUNCTION_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_function_constructor(duk_hthread *thr) {
    duk_hstring *h_sourcecode;
    duk_idx_t nargs;
    duk_idx_t i;
    duk_small_uint_t comp_flags;
    duk_hcompfunc *func;
    duk_hobject *outer_lex_env;
    duk_hobject *outer_var_env;

    /* normal and constructor calls have identical semantics */

    nargs = duk_get_top(thr);
    for (i = 0; i < nargs; i++) {
        duk_to_string(thr, i);  /* Rejects Symbols during coercion. */
    }

    if (nargs == 0) {
        duk_push_hstring_empty(thr);
        duk_push_hstring_empty(thr);
    } else if (nargs == 1) {
        /* XXX: cover this with the generic >1 case? */
        duk_push_hstring_empty(thr);
    } else {
        duk_insert(thr, 0);   /* [ arg1 ... argN-1 body] -> [body arg1 ... argN-1] */
        duk_push_literal(thr, ",");
        duk_insert(thr, 1);
        duk_join(thr, nargs - 1);
    }

    /* [ body formals ], formals is comma separated list that needs to be parsed */

    DUK_ASSERT_TOP(thr, 2);

    /* XXX: this placeholder is not always correct, but use for now.
     * It will fail in corner cases; see test-dev-func-cons-args.js.
     */
    duk_push_literal(thr, "function(");
    duk_dup_1(thr);
    duk_push_literal(thr, "){");
    duk_dup_0(thr);
    duk_push_literal(thr, "\n}");  /* Newline is important to handle trailing // comment. */
    duk_concat(thr, 5);

    /* [ body formals source ] */

    DUK_ASSERT_TOP(thr, 3);

    /* strictness is not inherited, intentional */
    comp_flags = DUK_COMPILE_FUNCEXPR;

    duk_push_hstring_stridx(thr, DUK_STRIDX_COMPILE);  /* XXX: copy from caller? */  /* XXX: ignored now */
    h_sourcecode = duk_require_hstring(thr, -2);  /* no symbol check needed; -2 is concat'd code */
    duk_js_compile(thr,
                   (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_sourcecode),
                   (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_sourcecode),
                   comp_flags);

    /* Force .name to 'anonymous' (ES2015). */
    duk_push_literal(thr, "anonymous");
    duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C);

    func = (duk_hcompfunc *) duk_known_hobject(thr, -1);
    DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) func));
    DUK_ASSERT(DUK_HOBJECT_HAS_CONSTRUCTABLE((duk_hobject *) func));

    /* [ body formals source template ] */

    /* only outer_lex_env matters, as functions always get a new
     * variable declaration environment.
     */

    outer_lex_env = thr->builtins[DUK_BIDX_GLOBAL_ENV];
    outer_var_env = thr->builtins[DUK_BIDX_GLOBAL_ENV];

    duk_js_push_closure(thr, func, outer_var_env, outer_lex_env, 1 /*add_auto_proto*/);

    /* [ body formals source template closure ] */

    return 1;
}
#endif  /* DUK_USE_FUNCTION_BUILTIN */

#if defined(DUK_USE_FUNCTION_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_function_prototype_to_string(duk_hthread *thr) {
    duk_tval *tv;

    /*
     *  E5 Section 15.3.4.2 places few requirements on the output of
     *  this function: the result is implementation dependent, must
     *  follow FunctionDeclaration syntax (in particular, must have a
     *  name even for anonymous functions or functions with empty name).
     *  The output does NOT need to compile into anything useful.
     *
     *  E6 Section 19.2.3.5 changes the requirements completely: the
     *  result must either eval() to a functionally equivalent object
     *  OR eval() to a SyntaxError.
     *
     *  We opt for the SyntaxError approach for now, with a syntax that
     *  mimics V8's native function syntax:
     *
     *      'function cos() { [native code] }'
     *
     *  but extended with [ecmascript code], [bound code], and
     *  [lightfunc code].
     */

    duk_push_this(thr);
    tv = DUK_GET_TVAL_NEGIDX(thr, -1);
    DUK_ASSERT(tv != NULL);

    if (DUK_TVAL_IS_OBJECT(tv)) {
        duk_hobject *obj = DUK_TVAL_GET_OBJECT(tv);
        const char *func_name;

        /* Function name: missing/undefined is mapped to empty string,
         * otherwise coerce to string.  No handling for invalid identifier
         * characters or e.g. '{' in the function name.  This doesn't
         * really matter as long as a SyntaxError results.  Technically
         * if the name contained a suitable prefix followed by '//' it
         * might cause the result to parse without error.
         */
        duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_NAME);
        if (duk_is_undefined(thr, -1)) {
            func_name = "";
        } else {
            func_name = duk_to_string(thr, -1);
            DUK_ASSERT(func_name != NULL);
        }

        if (DUK_HOBJECT_IS_COMPFUNC(obj)) {
            duk_push_sprintf(thr, "function %s() { [ecmascript code] }", (const char *) func_name);
        } else if (DUK_HOBJECT_IS_NATFUNC(obj)) {
            duk_push_sprintf(thr, "function %s() { [native code] }", (const char *) func_name);
        } else if (DUK_HOBJECT_IS_BOUNDFUNC(obj)) {
            duk_push_sprintf(thr, "function %s() { [bound code] }", (const char *) func_name);
        } else {
            goto type_error;
        }
    } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
        duk_push_lightfunc_tostring(thr, tv);
    } else {
        goto type_error;
    }

    return 1;

 type_error:
    DUK_DCERROR_TYPE_INVALID_ARGS(thr);
}
#endif

/* Always present because the native function pointer is needed in call
 * handling.
 */
DUK_INTERNAL duk_ret_t duk_bi_function_prototype_call(duk_hthread *thr) {
    /* .call() is dealt with in call handling by simulating its
     * effects so this function is actually never called.
     */
    DUK_UNREF(thr);
    return DUK_RET_TYPE_ERROR;
}

DUK_INTERNAL duk_ret_t duk_bi_function_prototype_apply(duk_hthread *thr) {
    /* Like .call(), never actually called. */
    DUK_UNREF(thr);
    return DUK_RET_TYPE_ERROR;
}

DUK_INTERNAL duk_ret_t duk_bi_reflect_apply(duk_hthread *thr) {
    /* Like .call(), never actually called. */
    DUK_UNREF(thr);
    return DUK_RET_TYPE_ERROR;
}

DUK_INTERNAL duk_ret_t duk_bi_reflect_construct(duk_hthread *thr) {
    /* Like .call(), never actually called. */
    DUK_UNREF(thr);
    return DUK_RET_TYPE_ERROR;
}

#if defined(DUK_USE_FUNCTION_BUILTIN)
/* Create a bound function which points to a target function which may
 * be bound or non-bound.  If the target is bound, the argument lists
 * and 'this' binding of the functions are merged and the resulting
 * function points directly to the non-bound target.
 */
DUK_INTERNAL duk_ret_t duk_bi_function_prototype_bind(duk_hthread *thr) {
    duk_hboundfunc *h_bound;
    duk_idx_t nargs;  /* bound args, not counting 'this' binding */
    duk_idx_t bound_nargs;
    duk_int_t bound_len;
    duk_tval *tv_prevbound;
    duk_idx_t n_prevbound;
    duk_tval *tv_res;
    duk_tval *tv_tmp;

    /* XXX: C API call, e.g. duk_push_bound_function(thr, target_idx, nargs); */

    /* Vararg function, careful arg handling, e.g. thisArg may not
     * be present.
     */
    nargs = duk_get_top(thr) - 1;  /* actual args, not counting 'this' binding */
    if (nargs < 0) {
        nargs++;
        duk_push_undefined(thr);
    }
    DUK_ASSERT(nargs >= 0);

    /* Limit 'nargs' for bound functions to guarantee arithmetic
     * below will never wrap.
     */
    if (nargs > (duk_idx_t) DUK_HBOUNDFUNC_MAX_ARGS) {
        DUK_DCERROR_RANGE_INVALID_COUNT(thr);
    }

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

    /* [ thisArg arg1 ... argN func ]  (thisArg+args == nargs+1 total) */
    DUK_ASSERT_TOP(thr, nargs + 2);

    /* Create bound function object. */
    h_bound = duk_push_hboundfunc(thr);
    DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&h_bound->target));
    DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&h_bound->this_binding));
    DUK_ASSERT(h_bound->args == NULL);
    DUK_ASSERT(h_bound->nargs == 0);
    DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_bound) == NULL);

    /* [ thisArg arg1 ... argN func boundFunc ] */

    /* If the target is a bound function, argument lists must be
     * merged.  The 'this' binding closest to the target function
     * wins because in call handling the 'this' gets replaced over
     * and over again until we call the non-bound function.
     */
    tv_prevbound = NULL;
    n_prevbound = 0;
    tv_tmp = DUK_GET_TVAL_POSIDX(thr, 0);
    DUK_TVAL_SET_TVAL(&h_bound->this_binding, tv_tmp);
    tv_tmp = DUK_GET_TVAL_NEGIDX(thr, -2);
    DUK_TVAL_SET_TVAL(&h_bound->target, tv_tmp);

    if (DUK_TVAL_IS_OBJECT(tv_tmp)) {
        duk_hobject *h_target;
        duk_hobject *bound_proto;

        h_target = DUK_TVAL_GET_OBJECT(tv_tmp);
        DUK_ASSERT(DUK_HOBJECT_IS_CALLABLE(h_target));

        /* Internal prototype must be copied from the target.
         * For lightfuncs Function.prototype is used and is already
         * in place.
         */
        bound_proto = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_target);
        DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) h_bound, bound_proto);

        /* The 'strict' flag is copied to get the special [[Get]] of E5.1
         * Section 15.3.5.4 to apply when a 'caller' value is a strict bound
         * function.  Not sure if this is correct, because the specification
         * is a bit ambiguous on this point but it would make sense.
         */
        /* Strictness is inherited from target. */
        if (DUK_HOBJECT_HAS_STRICT(h_target)) {
            DUK_HOBJECT_SET_STRICT((duk_hobject *) h_bound);
        }

        if (DUK_HOBJECT_HAS_BOUNDFUNC(h_target)) {
            duk_hboundfunc *h_boundtarget;

            h_boundtarget = (duk_hboundfunc *) (void *) h_target;

            /* The final function should always be non-bound, unless
             * there's a bug in the internals.  Assert for it.
             */
            DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(&h_boundtarget->target) ||
                       (DUK_TVAL_IS_OBJECT(&h_boundtarget->target) &&
                        DUK_HOBJECT_IS_CALLABLE(DUK_TVAL_GET_OBJECT(&h_boundtarget->target)) &&
                        !DUK_HOBJECT_IS_BOUNDFUNC(DUK_TVAL_GET_OBJECT(&h_boundtarget->target))));

            DUK_TVAL_SET_TVAL(&h_bound->target, &h_boundtarget->target);
            DUK_TVAL_SET_TVAL(&h_bound->this_binding, &h_boundtarget->this_binding);

            tv_prevbound = h_boundtarget->args;
            n_prevbound = h_boundtarget->nargs;
        }
    } else {
        /* Lightfuncs are always strict. */
        duk_hobject *bound_proto;

        DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv_tmp));
        DUK_HOBJECT_SET_STRICT((duk_hobject *) h_bound);
        bound_proto = thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE];
        DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) h_bound, bound_proto);
    }

    DUK_TVAL_INCREF(thr, &h_bound->target);  /* old values undefined, no decref needed */
    DUK_TVAL_INCREF(thr, &h_bound->this_binding);

    bound_nargs = n_prevbound + nargs;
    if (bound_nargs > (duk_idx_t) DUK_HBOUNDFUNC_MAX_ARGS) {
        DUK_DCERROR_RANGE_INVALID_COUNT(thr);
    }
    tv_res = (duk_tval *) DUK_ALLOC_CHECKED(thr, ((duk_size_t) bound_nargs) * sizeof(duk_tval));
    DUK_ASSERT(tv_res != NULL || bound_nargs == 0);
    DUK_ASSERT(h_bound->args == NULL);
    DUK_ASSERT(h_bound->nargs == 0);
    h_bound->args = tv_res;
    h_bound->nargs = bound_nargs;

    DUK_ASSERT(n_prevbound >= 0);
    duk_copy_tvals_incref(thr, tv_res, tv_prevbound, (duk_size_t) n_prevbound);
    DUK_ASSERT(nargs >= 0);
    duk_copy_tvals_incref(thr, tv_res + n_prevbound, DUK_GET_TVAL_POSIDX(thr, 1), (duk_size_t) nargs);

    /* [ thisArg arg1 ... argN func boundFunc ] */

    /* Bound function 'length' property is interesting.
     * For lightfuncs, simply read the virtual property.
     */
    duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH);
    bound_len = duk_get_int(thr, -1);  /* ES2015: no coercion */
    if (bound_len < nargs) {
        bound_len = 0;
    } else {
        bound_len -= nargs;
    }
    if (sizeof(duk_int_t) > 4 && bound_len > (duk_int_t) DUK_UINT32_MAX) {
        bound_len = (duk_int_t) DUK_UINT32_MAX;
    }
    duk_pop(thr);
    DUK_ASSERT(bound_len >= 0);
    tv_tmp = thr->valstack_top++;
    DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv_tmp));
    DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv_tmp));
    DUK_TVAL_SET_U32(tv_tmp, (duk_uint32_t) bound_len);  /* in-place update, fastint */
    duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_C);  /* attrs in E6 Section 9.2.4 */

    /* XXX: could these be virtual? */
    /* Caller and arguments must use the same thrower, [[ThrowTypeError]]. */
    duk_xdef_prop_stridx_thrower(thr, -1, DUK_STRIDX_CALLER);
    duk_xdef_prop_stridx_thrower(thr, -1, DUK_STRIDX_LC_ARGUMENTS);

    /* Function name and fileName (non-standard). */
    duk_push_literal(thr, "bound ");  /* ES2015 19.2.3.2. */
    duk_get_prop_stridx(thr, -3, DUK_STRIDX_NAME);
    if (!duk_is_string_notsymbol(thr, -1)) {
        /* ES2015 has requirement to check that .name of target is a string
         * (also must check for Symbol); if not, targetName should be the
         * empty string.  ES2015 19.2.3.2.
         */
        duk_pop(thr);
        duk_push_hstring_empty(thr);
    }
    duk_concat(thr, 2);
    duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C);
#if defined(DUK_USE_FUNC_FILENAME_PROPERTY)
    duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_FILE_NAME);
    duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_C);
#endif

    DUK_DDD(DUK_DDDPRINT("created bound function: %!iT", (duk_tval *) duk_get_tval(thr, -1)));

    return 1;
}
#endif  /* DUK_USE_FUNCTION_BUILTIN */

/* %NativeFunctionPrototype% .length getter. */
DUK_INTERNAL duk_ret_t duk_bi_native_function_length(duk_hthread *thr) {
    duk_tval *tv;
    duk_hnatfunc *h;
    duk_int16_t func_nargs;

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

    if (DUK_TVAL_IS_OBJECT(tv)) {
        h = (duk_hnatfunc *) DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(h != NULL);
        if (!DUK_HOBJECT_IS_NATFUNC((duk_hobject *) h)) {
            goto fail_type;
        }
        func_nargs = h->nargs;
        duk_push_int(thr, func_nargs == DUK_HNATFUNC_NARGS_VARARGS ? 0 : func_nargs);
    } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
        duk_small_uint_t lf_flags;
        duk_small_uint_t lf_len;

        lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv);
        lf_len = DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags);
        duk_push_uint(thr, lf_len);
    } else {
        goto fail_type;
    }
    return 1;

 fail_type:
    DUK_DCERROR_TYPE_INVALID_ARGS(thr);
}

/* %NativeFunctionPrototype% .name getter. */
DUK_INTERNAL duk_ret_t duk_bi_native_function_name(duk_hthread *thr) {
    duk_tval *tv;
    duk_hnatfunc *h;

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

    if (DUK_TVAL_IS_OBJECT(tv)) {
        h = (duk_hnatfunc *) DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(h != NULL);
        if (!DUK_HOBJECT_IS_NATFUNC((duk_hobject *) h)) {
            goto fail_type;
        }
#if 0
        duk_push_hnatfunc_name(thr, h);
#endif
        duk_push_hstring_empty(thr);
    } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
        duk_push_lightfunc_name(thr, tv);
    } else {
        goto fail_type;
    }
    return 1;

 fail_type:
    DUK_DCERROR_TYPE_INVALID_ARGS(thr);
}

#if defined(DUK_USE_SYMBOL_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_function_prototype_hasinstance(duk_hthread *thr) {
    /* This binding: RHS, stack index 0: LHS. */
    duk_bool_t ret;

    ret = duk_js_instanceof_ordinary(thr, DUK_GET_TVAL_POSIDX(thr, 0), DUK_GET_THIS_TVAL_PTR(thr));
    duk_push_boolean(thr, ret);
    return 1;
}
#endif  /* DUK_USE_SYMBOL_BUILTIN */
//#line 1 "duk_bi_global.c"
/*
 *  Global object built-ins
 */

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

/*
 *  Encoding/decoding helpers
 */

/* XXX: Could add fast path (for each transform callback) with direct byte
 * lookups (no shifting) and no explicit check for x < 0x80 before table
 * lookup.
 */

/* Macros for creating and checking bitmasks for character encoding.
 * Bit number is a bit counterintuitive, but minimizes code size.
 */
#define DUK__MKBITS(a,b,c,d,e,f,g,h)  ((duk_uint8_t) ( \
    ((a) << 0) | ((b) << 1) | ((c) << 2) | ((d) << 3) | \
    ((e) << 4) | ((f) << 5) | ((g) << 6) | ((h) << 7) \
    ))
#define DUK__CHECK_BITMASK(table,cp)  ((table)[(cp) >> 3] & (1 << ((cp) & 0x07)))

/* E5.1 Section 15.1.3.3: uriReserved + uriUnescaped + '#' */
DUK_LOCAL const duk_uint8_t duk__encode_uriunescaped_table[16] = {
    DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0),  /* 0x00-0x0f */
    DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0),  /* 0x10-0x1f */
    DUK__MKBITS(0, 1, 0, 1, 1, 0, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1),  /* 0x20-0x2f */
    DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 0, 1, 0, 1),  /* 0x30-0x3f */
    DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1),  /* 0x40-0x4f */
    DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 0, 1),  /* 0x50-0x5f */
    DUK__MKBITS(0, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1),  /* 0x60-0x6f */
    DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 1, 0),  /* 0x70-0x7f */
};

/* E5.1 Section 15.1.3.4: uriUnescaped */
DUK_LOCAL const duk_uint8_t duk__encode_uricomponent_unescaped_table[16] = {
    DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0),  /* 0x00-0x0f */
    DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0),  /* 0x10-0x1f */
    DUK__MKBITS(0, 1, 0, 0, 0, 0, 0, 1), DUK__MKBITS(1, 1, 1, 0, 0, 1, 1, 0),  /* 0x20-0x2f */
    DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 0, 0, 0, 0, 0, 0),  /* 0x30-0x3f */
    DUK__MKBITS(0, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1),  /* 0x40-0x4f */
    DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 0, 1),  /* 0x50-0x5f */
    DUK__MKBITS(0, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1),  /* 0x60-0x6f */
    DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 1, 0),  /* 0x70-0x7f */
};

/* E5.1 Section 15.1.3.1: uriReserved + '#' */
DUK_LOCAL const duk_uint8_t duk__decode_uri_reserved_table[16] = {
    DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0),  /* 0x00-0x0f */
    DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0),  /* 0x10-0x1f */
    DUK__MKBITS(0, 0, 0, 1, 1, 0, 1, 0), DUK__MKBITS(0, 0, 0, 1, 1, 0, 0, 1),  /* 0x20-0x2f */
    DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 1, 1, 0, 1, 0, 1),  /* 0x30-0x3f */
    DUK__MKBITS(1, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0),  /* 0x40-0x4f */
    DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0),  /* 0x50-0x5f */
    DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0),  /* 0x60-0x6f */
    DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0),  /* 0x70-0x7f */
};

/* E5.1 Section 15.1.3.2: empty */
DUK_LOCAL const duk_uint8_t duk__decode_uri_component_reserved_table[16] = {
    DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0),  /* 0x00-0x0f */
    DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0),  /* 0x10-0x1f */
    DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0),  /* 0x20-0x2f */
    DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0),  /* 0x30-0x3f */
    DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0),  /* 0x40-0x4f */
    DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0),  /* 0x50-0x5f */
    DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0),  /* 0x60-0x6f */
    DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0),  /* 0x70-0x7f */
};

#if defined(DUK_USE_SECTION_B)
/* E5.1 Section B.2.2, step 7. */
DUK_LOCAL const duk_uint8_t duk__escape_unescaped_table[16] = {
    DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0),  /* 0x00-0x0f */
    DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0),  /* 0x10-0x1f */
    DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 1, 1, 0, 1, 1, 1),  /* 0x20-0x2f */
    DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 0, 0, 0, 0, 0, 0),  /* 0x30-0x3f */
    DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1),  /* 0x40-0x4f */
    DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 0, 1),  /* 0x50-0x5f */
    DUK__MKBITS(0, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1),  /* 0x60-0x6f */
    DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 0, 0)   /* 0x70-0x7f */
};
#endif  /* DUK_USE_SECTION_B */

typedef struct {
    duk_hthread *thr;
    duk_hstring *h_str;
    duk_bufwriter_ctx bw;
    const duk_uint8_t *p;
    const duk_uint8_t *p_start;
    const duk_uint8_t *p_end;
} duk__transform_context;

typedef void (*duk__transform_callback)(duk__transform_context *tfm_ctx, const void *udata, duk_codepoint_t cp);

/* XXX: refactor and share with other code */
DUK_LOCAL duk_small_int_t duk__decode_hex_escape(const duk_uint8_t *p, duk_small_int_t n) {
    duk_small_int_t ch;
    duk_small_int_t t = 0;

    while (n > 0) {
        t = t * 16;
        ch = (duk_small_int_t) duk_hex_dectab[*p++];
        if (DUK_LIKELY(ch >= 0)) {
            t += ch;
        } else {
            return -1;
        }
        n--;
    }
    return t;
}

DUK_LOCAL int duk__transform_helper(duk_hthread *thr, duk__transform_callback callback, const void *udata) {
    duk__transform_context tfm_ctx_alloc;
    duk__transform_context *tfm_ctx = &tfm_ctx_alloc;
    duk_codepoint_t cp;

    tfm_ctx->thr = thr;

    tfm_ctx->h_str = duk_to_hstring(thr, 0);
    DUK_ASSERT(tfm_ctx->h_str != NULL);

    DUK_BW_INIT_PUSHBUF(thr, &tfm_ctx->bw, DUK_HSTRING_GET_BYTELEN(tfm_ctx->h_str));  /* initial size guess */

    tfm_ctx->p_start = DUK_HSTRING_GET_DATA(tfm_ctx->h_str);
    tfm_ctx->p_end = tfm_ctx->p_start + DUK_HSTRING_GET_BYTELEN(tfm_ctx->h_str);
    tfm_ctx->p = tfm_ctx->p_start;

    while (tfm_ctx->p < tfm_ctx->p_end) {
        cp = (duk_codepoint_t) duk_unicode_decode_xutf8_checked(thr, &tfm_ctx->p, tfm_ctx->p_start, tfm_ctx->p_end);
        callback(tfm_ctx, udata, cp);
    }

    DUK_BW_COMPACT(thr, &tfm_ctx->bw);

    (void) duk_buffer_to_string(thr, -1);  /* Safe if transform is safe. */
    return 1;
}

DUK_LOCAL void duk__transform_callback_encode_uri(duk__transform_context *tfm_ctx, const void *udata, duk_codepoint_t cp) {
    duk_uint8_t xutf8_buf[DUK_UNICODE_MAX_XUTF8_LENGTH];
    duk_small_int_t len;
    duk_codepoint_t cp1, cp2;
    duk_small_int_t i, t;
    const duk_uint8_t *unescaped_table = (const duk_uint8_t *) udata;

    /* UTF-8 encoded bytes escaped as %xx%xx%xx... -> 3 * nbytes.
     * Codepoint range is restricted so this is a slightly too large
     * but doesn't matter.
     */
    DUK_BW_ENSURE(tfm_ctx->thr, &tfm_ctx->bw, 3 * DUK_UNICODE_MAX_XUTF8_LENGTH);

    if (cp < 0) {
        goto uri_error;
    } else if ((cp < 0x80L) && DUK__CHECK_BITMASK(unescaped_table, cp)) {
        DUK_BW_WRITE_RAW_U8(tfm_ctx->thr, &tfm_ctx->bw, (duk_uint8_t) cp);
        return;
    } else if (cp >= 0xdc00L && cp <= 0xdfffL) {
        goto uri_error;
    } else if (cp >= 0xd800L && cp <= 0xdbffL) {
        /* Needs lookahead */
        if (duk_unicode_decode_xutf8(tfm_ctx->thr, &tfm_ctx->p, tfm_ctx->p_start, tfm_ctx->p_end, (duk_ucodepoint_t *) &cp2) == 0) {
            goto uri_error;
        }
        if (!(cp2 >= 0xdc00L && cp2 <= 0xdfffL)) {
            goto uri_error;
        }
        cp1 = cp;
        cp = (duk_codepoint_t) (((cp1 - 0xd800L) << 10) + (cp2 - 0xdc00L) + 0x10000L);
    } else if (cp > 0x10ffffL) {
        /* Although we can allow non-BMP characters (they'll decode
         * back into surrogate pairs), we don't allow extended UTF-8
         * characters; they would encode to URIs which won't decode
         * back because of strict UTF-8 checks in URI decoding.
         * (However, we could just as well allow them here.)
         */
        goto uri_error;
    } else {
        /* Non-BMP characters within valid UTF-8 range: encode as is.
         * They'll decode back into surrogate pairs if the escaped
         * output is decoded.
         */
        ;
    }

    len = duk_unicode_encode_xutf8((duk_ucodepoint_t) cp, xutf8_buf);
    for (i = 0; i < len; i++) {
        t = (duk_small_int_t) xutf8_buf[i];
        DUK_BW_WRITE_RAW_U8_3(tfm_ctx->thr,
                              &tfm_ctx->bw,
                              DUK_ASC_PERCENT,
                              (duk_uint8_t) duk_uc_nybbles[t >> 4],
                                      (duk_uint8_t) duk_uc_nybbles[t & 0x0f]);
    }

    return;

 uri_error:
    DUK_ERROR_URI(tfm_ctx->thr, DUK_STR_INVALID_INPUT);
    DUK_WO_NORETURN(return;);
}

DUK_LOCAL void duk__transform_callback_decode_uri(duk__transform_context *tfm_ctx, const void *udata, duk_codepoint_t cp) {
    const duk_uint8_t *reserved_table = (const duk_uint8_t *) udata;
    duk_small_uint_t utf8_blen;
    duk_codepoint_t min_cp;
    duk_small_int_t t;  /* must be signed */
    duk_small_uint_t i;

    /* Maximum write size: XUTF8 path writes max DUK_UNICODE_MAX_XUTF8_LENGTH,
     * percent escape path writes max two times CESU-8 encoded BMP length.
     */
    DUK_BW_ENSURE(tfm_ctx->thr,
                  &tfm_ctx->bw,
                  (DUK_UNICODE_MAX_XUTF8_LENGTH >= 2 * DUK_UNICODE_MAX_CESU8_BMP_LENGTH ?
                  DUK_UNICODE_MAX_XUTF8_LENGTH : DUK_UNICODE_MAX_CESU8_BMP_LENGTH));

    if (cp == (duk_codepoint_t) '%') {
        const duk_uint8_t *p = tfm_ctx->p;
        duk_size_t left = (duk_size_t) (tfm_ctx->p_end - p);  /* bytes left */

        DUK_DDD(DUK_DDDPRINT("percent encoding, left=%ld", (long) left));

        if (left < 2) {
            goto uri_error;
        }

        t = duk__decode_hex_escape(p, 2);
        DUK_DDD(DUK_DDDPRINT("first byte: %ld", (long) t));
        if (t < 0) {
            goto uri_error;
        }

        if (t < 0x80) {
            if (DUK__CHECK_BITMASK(reserved_table, t)) {
                /* decode '%xx' to '%xx' if decoded char in reserved set */
                DUK_ASSERT(tfm_ctx->p - 1 >= tfm_ctx->p_start);
                DUK_BW_WRITE_RAW_U8_3(tfm_ctx->thr,
                                      &tfm_ctx->bw,
                                      DUK_ASC_PERCENT,
                                      p[0],
                                      p[1]);
            } else {
                DUK_BW_WRITE_RAW_U8(tfm_ctx->thr, &tfm_ctx->bw, (duk_uint8_t) t);
            }
            tfm_ctx->p += 2;
            return;
        }

        /* Decode UTF-8 codepoint from a sequence of hex escapes.  The
         * first byte of the sequence has been decoded to 't'.
         *
         * Note that UTF-8 validation must be strict according to the
         * specification: E5.1 Section 15.1.3, decode algorithm step
         * 4.d.vii.8.  URIError from non-shortest encodings is also
         * specifically noted in the spec.
         */

        DUK_ASSERT(t >= 0x80);
        if (t < 0xc0) {
            /* continuation byte */
            goto uri_error;
        } else if (t < 0xe0) {
            /* 110x xxxx; 2 bytes */
            utf8_blen = 2;
            min_cp = 0x80L;
            cp = t & 0x1f;
        } else if (t < 0xf0) {
            /* 1110 xxxx; 3 bytes */
            utf8_blen = 3;
            min_cp = 0x800L;
            cp = t & 0x0f;
        } else if (t < 0xf8) {
            /* 1111 0xxx; 4 bytes */
            utf8_blen = 4;
            min_cp = 0x10000L;
            cp = t & 0x07;
        } else {
            /* extended utf-8 not allowed for URIs */
            goto uri_error;
        }

        if (left < utf8_blen * 3 - 1) {
            /* '%xx%xx...%xx', p points to char after first '%' */
            goto uri_error;
        }

        p += 3;
        for (i = 1; i < utf8_blen; i++) {
            /* p points to digit part ('%xy', p points to 'x') */
            t = duk__decode_hex_escape(p, 2);
            DUK_DDD(DUK_DDDPRINT("i=%ld utf8_blen=%ld cp=%ld t=0x%02lx",
                                 (long) i, (long) utf8_blen, (long) cp, (unsigned long) t));
            if (t < 0) {
                goto uri_error;
            }
            if ((t & 0xc0) != 0x80) {
                goto uri_error;
            }
            cp = (cp << 6) + (t & 0x3f);
            p += 3;
        }
        p--;  /* p overshoots */
        tfm_ctx->p = p;

        DUK_DDD(DUK_DDDPRINT("final cp=%ld, min_cp=%ld", (long) cp, (long) min_cp));

        if (cp < min_cp || cp > 0x10ffffL || (cp >= 0xd800L && cp <= 0xdfffL)) {
            goto uri_error;
        }

        /* The E5.1 algorithm checks whether or not a decoded codepoint
         * is below 0x80 and perhaps may be in the "reserved" set.
         * This seems pointless because the single byte UTF-8 case is
         * handled separately, and non-shortest encodings are rejected.
         * So, 'cp' cannot be below 0x80 here, and thus cannot be in
         * the reserved set.
         */

        /* utf-8 validation ensures these */
        DUK_ASSERT(cp >= 0x80L && cp <= 0x10ffffL);

        if (cp >= 0x10000L) {
            cp -= 0x10000L;
            DUK_ASSERT(cp < 0x100000L);

            DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, ((cp >> 10) + 0xd800L));
            DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, ((cp & 0x03ffL) + 0xdc00L));
        } else {
            DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, cp);
        }
    } else {
        DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, cp);
    }
    return;

 uri_error:
    DUK_ERROR_URI(tfm_ctx->thr, DUK_STR_INVALID_INPUT);
    DUK_WO_NORETURN(return;);
}

#if defined(DUK_USE_SECTION_B)
DUK_LOCAL void duk__transform_callback_escape(duk__transform_context *tfm_ctx, const void *udata, duk_codepoint_t cp) {
    DUK_UNREF(udata);

    DUK_BW_ENSURE(tfm_ctx->thr, &tfm_ctx->bw, 6);

    if (cp < 0) {
        goto esc_error;
    } else if ((cp < 0x80L) && DUK__CHECK_BITMASK(duk__escape_unescaped_table, cp)) {
        DUK_BW_WRITE_RAW_U8(tfm_ctx->thr, &tfm_ctx->bw, (duk_uint8_t) cp);
    } else if (cp < 0x100L) {
        DUK_BW_WRITE_RAW_U8_3(tfm_ctx->thr,
                              &tfm_ctx->bw,
                              (duk_uint8_t) DUK_ASC_PERCENT,
                              (duk_uint8_t) duk_uc_nybbles[cp >> 4],
                              (duk_uint8_t) duk_uc_nybbles[cp & 0x0f]);
    } else if (cp < 0x10000L) {
        DUK_BW_WRITE_RAW_U8_6(tfm_ctx->thr,
                              &tfm_ctx->bw,
                              (duk_uint8_t) DUK_ASC_PERCENT,
                              (duk_uint8_t) DUK_ASC_LC_U,
                              (duk_uint8_t) duk_uc_nybbles[cp >> 12],
                              (duk_uint8_t) duk_uc_nybbles[(cp >> 8) & 0x0f],
                              (duk_uint8_t) duk_uc_nybbles[(cp >> 4) & 0x0f],
                              (duk_uint8_t) duk_uc_nybbles[cp & 0x0f]);
    } else {
        /* Characters outside BMP cannot be escape()'d.  We could
         * encode them as surrogate pairs (for codepoints inside
         * valid UTF-8 range, but not extended UTF-8).  Because
         * escape() and unescape() are legacy functions, we don't.
         */
        goto esc_error;
    }

    return;

 esc_error:
    DUK_ERROR_TYPE(tfm_ctx->thr, DUK_STR_INVALID_INPUT);
    DUK_WO_NORETURN(return;);
}

DUK_LOCAL void duk__transform_callback_unescape(duk__transform_context *tfm_ctx, const void *udata, duk_codepoint_t cp) {
    duk_small_int_t t;

    DUK_UNREF(udata);

    if (cp == (duk_codepoint_t) '%') {
        const duk_uint8_t *p = tfm_ctx->p;
        duk_size_t left = (duk_size_t) (tfm_ctx->p_end - p);  /* bytes left */

        if (left >= 5 && p[0] == 'u' &&
            ((t = duk__decode_hex_escape(p + 1, 4)) >= 0)) {
            cp = (duk_codepoint_t) t;
            tfm_ctx->p += 5;
        } else if (left >= 2 &&
                   ((t = duk__decode_hex_escape(p, 2)) >= 0)) {
            cp = (duk_codepoint_t) t;
            tfm_ctx->p += 2;
        }
    }

    DUK_BW_WRITE_ENSURE_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, cp);
}
#endif  /* DUK_USE_SECTION_B */

/*
 *  Eval
 *
 *  Eval needs to handle both a "direct eval" and an "indirect eval".
 *  Direct eval handling needs access to the caller's activation so that its
 *  lexical environment can be accessed.  A direct eval is only possible from
 *  ECMAScript code; an indirect eval call is possible also from C code.
 *  When an indirect eval call is made from C code, there may not be a
 *  calling activation at all which needs careful handling.
 */

DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_hthread *thr) {
    duk_hstring *h;
    duk_activation *act_caller;
    duk_activation *act_eval;
    duk_hcompfunc *func;
    duk_hobject *outer_lex_env;
    duk_hobject *outer_var_env;
    duk_bool_t this_to_global = 1;
    duk_small_uint_t comp_flags;
    duk_int_t level = -2;
    duk_small_uint_t call_flags;

    DUK_ASSERT(duk_get_top(thr) == 1 || duk_get_top(thr) == 2);  /* 2 when called by debugger */
    DUK_ASSERT(thr->callstack_top >= 1);  /* at least this function exists */
    DUK_ASSERT(thr->callstack_curr != NULL);
    DUK_ASSERT((thr->callstack_curr->flags & DUK_ACT_FLAG_DIRECT_EVAL) == 0 || /* indirect eval */
               (thr->callstack_top >= 2));  /* if direct eval, calling activation must exist */

    /*
     *  callstack_top - 1 --> this function
     *  callstack_top - 2 --> caller (may not exist)
     *
     *  If called directly from C, callstack_top might be 1.  If calling
     *  activation doesn't exist, call must be indirect.
     */

    h = duk_get_hstring_notsymbol(thr, 0);
    if (!h) {
        /* Symbol must be returned as is, like any non-string values. */
        return 1;  /* return arg as-is */
    }

#if defined(DUK_USE_DEBUGGER_SUPPORT)
    /* NOTE: level is used only by the debugger and should never be present
     * for an ECMAScript eval().
     */
    DUK_ASSERT(level == -2);  /* by default, use caller's environment */
    if (duk_get_top(thr) >= 2 && duk_is_number(thr, 1)) {
        level = duk_get_int(thr, 1);
    }
    DUK_ASSERT(level <= -2);  /* This is guaranteed by debugger code. */
#endif

    /* [ source ] */

    comp_flags = DUK_COMPILE_EVAL;
    act_eval = thr->callstack_curr;  /* this function */
    DUK_ASSERT(act_eval != NULL);
    act_caller = duk_hthread_get_activation_for_level(thr, level);
    if (act_caller != NULL) {
        /* Have a calling activation, check for direct eval (otherwise
         * assume indirect eval.
         */
        if ((act_caller->flags & DUK_ACT_FLAG_STRICT) &&
            (act_eval->flags & DUK_ACT_FLAG_DIRECT_EVAL)) {
            /* Only direct eval inherits strictness from calling code
             * (E5.1 Section 10.1.1).
             */
            comp_flags |= DUK_COMPILE_STRICT;
        }
    } else {
        DUK_ASSERT((act_eval->flags & DUK_ACT_FLAG_DIRECT_EVAL) == 0);
    }

    duk_push_hstring_stridx(thr, DUK_STRIDX_INPUT);  /* XXX: copy from caller? */
    duk_js_compile(thr,
                   (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h),
                   (duk_size_t) DUK_HSTRING_GET_BYTELEN(h),
                   comp_flags);
    func = (duk_hcompfunc *) duk_known_hobject(thr, -1);
    DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) func));

    /* [ source template ] */

    /* E5 Section 10.4.2 */

    if (act_eval->flags & DUK_ACT_FLAG_DIRECT_EVAL) {
        DUK_ASSERT(thr->callstack_top >= 2);
        DUK_ASSERT(act_caller != NULL);
        if (act_caller->lex_env == NULL) {
            DUK_ASSERT(act_caller->var_env == NULL);
            DUK_DDD(DUK_DDDPRINT("delayed environment initialization"));

            /* this may have side effects, so re-lookup act */
            duk_js_init_activation_environment_records_delayed(thr, act_caller);
        }
        DUK_ASSERT(act_caller->lex_env != NULL);
        DUK_ASSERT(act_caller->var_env != NULL);

        this_to_global = 0;

        if (DUK_HOBJECT_HAS_STRICT((duk_hobject *) func)) {
            duk_hdecenv *new_env;
            duk_hobject *act_lex_env;

            DUK_DDD(DUK_DDDPRINT("direct eval call to a strict function -> "
                                 "var_env and lex_env to a fresh env, "
                                 "this_binding to caller's this_binding"));

            act_lex_env = act_caller->lex_env;

            new_env = duk_hdecenv_alloc(thr,
                                        DUK_HOBJECT_FLAG_EXTENSIBLE |
                                        DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV));
            DUK_ASSERT(new_env != NULL);
            duk_push_hobject(thr, (duk_hobject *) new_env);

            DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) new_env) == NULL);
            DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) new_env, act_lex_env);
            DUK_HOBJECT_INCREF_ALLOWNULL(thr, act_lex_env);
            DUK_DDD(DUK_DDDPRINT("new_env allocated: %!iO", (duk_heaphdr *) new_env));

            outer_lex_env = (duk_hobject *) new_env;
            outer_var_env = (duk_hobject *) new_env;

            duk_insert(thr, 0);  /* stash to bottom of value stack to keep new_env reachable for duration of eval */

            /* compiler's responsibility */
            DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV((duk_hobject *) func));
        } else {
            DUK_DDD(DUK_DDDPRINT("direct eval call to a non-strict function -> "
                                 "var_env and lex_env to caller's envs, "
                                 "this_binding to caller's this_binding"));

            outer_lex_env = act_caller->lex_env;
            outer_var_env = act_caller->var_env;

            /* compiler's responsibility */
            DUK_ASSERT(!DUK_HOBJECT_HAS_NEWENV((duk_hobject *) func));
        }
    } else {
        DUK_DDD(DUK_DDDPRINT("indirect eval call -> var_env and lex_env to "
                             "global object, this_binding to global object"));

        this_to_global = 1;
        outer_lex_env = thr->builtins[DUK_BIDX_GLOBAL_ENV];
        outer_var_env = thr->builtins[DUK_BIDX_GLOBAL_ENV];
    }

    /* Eval code doesn't need an automatic .prototype object. */
    duk_js_push_closure(thr, func, outer_var_env, outer_lex_env, 0 /*add_auto_proto*/);

    /* [ env? source template closure ] */

    if (this_to_global) {
        DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL);
        duk_push_hobject_bidx(thr, DUK_BIDX_GLOBAL);
    } else {
        duk_tval *tv;
        DUK_ASSERT(thr->callstack_top >= 2);
        DUK_ASSERT(act_caller != NULL);
        tv = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + act_caller->bottom_byteoff - sizeof(duk_tval));  /* this is just beneath bottom */
        DUK_ASSERT(tv >= thr->valstack);
        duk_push_tval(thr, tv);
    }

    DUK_DDD(DUK_DDDPRINT("eval -> lex_env=%!iO, var_env=%!iO, this_binding=%!T",
                         (duk_heaphdr *) outer_lex_env,
                         (duk_heaphdr *) outer_var_env,
                         duk_get_tval(thr, -1)));

    /* [ env? source template closure this ] */

    call_flags = 0;
    if (act_eval->flags & DUK_ACT_FLAG_DIRECT_EVAL) {
        /* Set DIRECT_EVAL flag for the call; it's not strictly
         * needed for the 'inner' eval call (the eval body) but
         * current new.target implementation expects to find it
         * so it can traverse direct eval chains up to the real
         * calling function.
         */
        call_flags |= DUK_CALL_FLAG_DIRECT_EVAL;
    }
    duk_handle_call_unprotected_nargs(thr, 0, call_flags);

    /* [ env? source template result ] */

    return 1;
}

/*
 *  Parsing of ints and floats
 */

#if defined(DUK_USE_GLOBAL_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_global_object_parse_int(duk_hthread *thr) {
    duk_int32_t radix;
    duk_small_uint_t s2n_flags;

    DUK_ASSERT_TOP(thr, 2);
    duk_to_string(thr, 0);  /* Reject symbols. */

    radix = duk_to_int32(thr, 1);

    /* While parseInt() recognizes 0xdeadbeef, it doesn't recognize
     * ES2015 0o123 or 0b10001.
     */
    s2n_flags = DUK_S2N_FLAG_TRIM_WHITE |
                DUK_S2N_FLAG_ALLOW_GARBAGE |
                DUK_S2N_FLAG_ALLOW_PLUS |
                DUK_S2N_FLAG_ALLOW_MINUS |
                DUK_S2N_FLAG_ALLOW_LEADING_ZERO |
                DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT;

    /* Specification stripPrefix maps to DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT.
     *
     * Don't autodetect octals (from leading zeroes), require user code to
     * provide an explicit radix 8 for parsing octal.  See write-up from Mozilla:
     * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt#ECMAScript_5_Removes_Octal_Interpretation
     */

    if (radix != 0) {
        if (radix < 2 || radix > 36) {
            goto ret_nan;
        }
        if (radix != 16) {
            s2n_flags &= ~DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT;
        }
    } else {
        radix = 10;
    }

    duk_dup_0(thr);
    duk_numconv_parse(thr, (duk_small_int_t) radix, s2n_flags);
    return 1;

 ret_nan:
    duk_push_nan(thr);
    return 1;
}
#endif  /* DUK_USE_GLOBAL_BUILTIN */

#if defined(DUK_USE_GLOBAL_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_global_object_parse_float(duk_hthread *thr) {
    duk_small_uint_t s2n_flags;

    DUK_ASSERT_TOP(thr, 1);
    duk_to_string(thr, 0);  /* Reject symbols. */

    /* XXX: check flags */
    s2n_flags = DUK_S2N_FLAG_TRIM_WHITE |
                DUK_S2N_FLAG_ALLOW_EXP |
                DUK_S2N_FLAG_ALLOW_GARBAGE |
                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_LEADING_ZERO;

    duk_numconv_parse(thr, 10 /*radix*/, s2n_flags);
    return 1;
}
#endif  /* DUK_USE_GLOBAL_BUILTIN */

/*
 *  Number checkers
 */

#if defined(DUK_USE_GLOBAL_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_global_object_is_nan(duk_hthread *thr) {
    duk_double_t d = duk_to_number(thr, 0);
    duk_push_boolean(thr, (duk_bool_t) DUK_ISNAN(d));
    return 1;
}
#endif  /* DUK_USE_GLOBAL_BUILTIN */

#if defined(DUK_USE_GLOBAL_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_global_object_is_finite(duk_hthread *thr) {
    duk_double_t d = duk_to_number(thr, 0);
    duk_push_boolean(thr, (duk_bool_t) DUK_ISFINITE(d));
    return 1;
}
#endif  /* DUK_USE_GLOBAL_BUILTIN */

/*
 *  URI handling
 */

#if defined(DUK_USE_GLOBAL_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_global_object_decode_uri(duk_hthread *thr) {
    return duk__transform_helper(thr, duk__transform_callback_decode_uri, (const void *) duk__decode_uri_reserved_table);
}

DUK_INTERNAL duk_ret_t duk_bi_global_object_decode_uri_component(duk_hthread *thr) {
    return duk__transform_helper(thr, duk__transform_callback_decode_uri, (const void *) duk__decode_uri_component_reserved_table);
}

DUK_INTERNAL duk_ret_t duk_bi_global_object_encode_uri(duk_hthread *thr) {
    return duk__transform_helper(thr, duk__transform_callback_encode_uri, (const void *) duk__encode_uriunescaped_table);
}

DUK_INTERNAL duk_ret_t duk_bi_global_object_encode_uri_component(duk_hthread *thr) {
    return duk__transform_helper(thr, duk__transform_callback_encode_uri, (const void *) duk__encode_uricomponent_unescaped_table);
}

#if defined(DUK_USE_SECTION_B)
DUK_INTERNAL duk_ret_t duk_bi_global_object_escape(duk_hthread *thr) {
    return duk__transform_helper(thr, duk__transform_callback_escape, (const void *) NULL);
}

DUK_INTERNAL duk_ret_t duk_bi_global_object_unescape(duk_hthread *thr) {
    return duk__transform_helper(thr, duk__transform_callback_unescape, (const void *) NULL);
}
#endif  /* DUK_USE_SECTION_B */
#endif  /* DUK_USE_GLOBAL_BUILTIN */

/* automatic undefs */
#undef DUK__CHECK_BITMASK
#undef DUK__MKBITS
//#line 1 "duk_bi_json.c"
/*
 *  JSON built-ins.
 *
 *  See doc/json.rst.
 *
 *  Codepoints are handled as duk_uint_fast32_t to ensure that the full
 *  unsigned 32-bit range is supported.  This matters to e.g. JX.
 *
 *  Input parsing doesn't do an explicit end-of-input check at all.  This is
 *  safe: input string data is always NUL-terminated (0x00) and valid JSON
 *  inputs never contain plain NUL characters, so that as long as syntax checks
 *  are correct, we'll never read past the NUL.  This approach reduces code size
 *  and improves parsing performance, but it's critical that syntax checks are
 *  indeed correct!
 */

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

#if defined(DUK_USE_JSON_SUPPORT)

/*
 *  Local defines and forward declarations.
 */

#define DUK__JSON_DECSTR_BUFSIZE 128
#define DUK__JSON_DECSTR_CHUNKSIZE 64
#define DUK__JSON_ENCSTR_CHUNKSIZE 64
#define DUK__JSON_STRINGIFY_BUFSIZE 128
#define DUK__JSON_MAX_ESC_LEN 10  /* '\Udeadbeef' */

DUK_LOCAL_DECL void duk__json_dec_syntax_error(duk_json_dec_ctx *js_ctx);
DUK_LOCAL_DECL void duk__json_dec_eat_white(duk_json_dec_ctx *js_ctx);
#if defined(DUK_USE_JX)
DUK_LOCAL_DECL duk_uint8_t duk__json_dec_peek(duk_json_dec_ctx *js_ctx);
#endif
DUK_LOCAL_DECL duk_uint8_t duk__json_dec_get(duk_json_dec_ctx *js_ctx);
DUK_LOCAL_DECL duk_uint8_t duk__json_dec_get_nonwhite(duk_json_dec_ctx *js_ctx);
DUK_LOCAL_DECL duk_uint_fast32_t duk__json_dec_decode_hex_escape(duk_json_dec_ctx *js_ctx, duk_small_uint_t n);
DUK_LOCAL_DECL void duk__json_dec_req_stridx(duk_json_dec_ctx *js_ctx, duk_small_uint_t stridx);
DUK_LOCAL_DECL void duk__json_dec_string(duk_json_dec_ctx *js_ctx);
#if defined(DUK_USE_JX)
DUK_LOCAL_DECL void duk__json_dec_plain_string(duk_json_dec_ctx *js_ctx);
DUK_LOCAL_DECL void duk__json_dec_pointer(duk_json_dec_ctx *js_ctx);
DUK_LOCAL_DECL void duk__json_dec_buffer(duk_json_dec_ctx *js_ctx);
#endif
DUK_LOCAL_DECL void duk__json_dec_number(duk_json_dec_ctx *js_ctx);
DUK_LOCAL_DECL void duk__json_dec_objarr_entry(duk_json_dec_ctx *js_ctx);
DUK_LOCAL_DECL void duk__json_dec_objarr_exit(duk_json_dec_ctx *js_ctx);
DUK_LOCAL_DECL void duk__json_dec_object(duk_json_dec_ctx *js_ctx);
DUK_LOCAL_DECL void duk__json_dec_array(duk_json_dec_ctx *js_ctx);
DUK_LOCAL_DECL void duk__json_dec_value(duk_json_dec_ctx *js_ctx);
DUK_LOCAL_DECL void duk__json_dec_reviver_walk(duk_json_dec_ctx *js_ctx);

DUK_LOCAL_DECL void duk__emit_1(duk_json_enc_ctx *js_ctx, duk_uint_fast8_t ch);
DUK_LOCAL_DECL void duk__emit_2(duk_json_enc_ctx *js_ctx, duk_uint_fast8_t ch1, duk_uint_fast8_t ch2);
DUK_LOCAL_DECL void duk__unemit_1(duk_json_enc_ctx *js_ctx);
DUK_LOCAL_DECL void duk__emit_hstring(duk_json_enc_ctx *js_ctx, duk_hstring *h);
#if defined(DUK_USE_FASTINT)
DUK_LOCAL_DECL void duk__emit_cstring(duk_json_enc_ctx *js_ctx, const char *p);
#endif
DUK_LOCAL_DECL void duk__emit_stridx(duk_json_enc_ctx *js_ctx, duk_small_uint_t stridx);
DUK_LOCAL_DECL duk_uint8_t *duk__emit_esc_auto_fast(duk_json_enc_ctx *js_ctx, duk_uint_fast32_t cp, duk_uint8_t *q);
DUK_LOCAL_DECL void duk__json_enc_key_autoquote(duk_json_enc_ctx *js_ctx, duk_hstring *k);
DUK_LOCAL_DECL void duk__json_enc_quote_string(duk_json_enc_ctx *js_ctx, duk_hstring *h_str);
DUK_LOCAL_DECL void duk__json_enc_objarr_entry(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_top);
DUK_LOCAL_DECL void duk__json_enc_objarr_exit(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_top);
DUK_LOCAL_DECL void duk__json_enc_object(duk_json_enc_ctx *js_ctx);
DUK_LOCAL_DECL void duk__json_enc_array(duk_json_enc_ctx *js_ctx);
DUK_LOCAL_DECL duk_bool_t duk__json_enc_value(duk_json_enc_ctx *js_ctx, duk_idx_t idx_holder);
DUK_LOCAL_DECL duk_bool_t duk__json_enc_allow_into_proplist(duk_tval *tv);
DUK_LOCAL_DECL void duk__json_enc_double(duk_json_enc_ctx *js_ctx);
#if defined(DUK_USE_FASTINT)
DUK_LOCAL_DECL void duk__json_enc_fastint_tval(duk_json_enc_ctx *js_ctx, duk_tval *tv);
#endif
#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
DUK_LOCAL_DECL void duk__json_enc_buffer_jx_jc(duk_json_enc_ctx *js_ctx, duk_hbuffer *h);
DUK_LOCAL_DECL void duk__json_enc_pointer(duk_json_enc_ctx *js_ctx, void *ptr);
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_LOCAL_DECL void duk__json_enc_bufobj(duk_json_enc_ctx *js_ctx, duk_hbufobj *h_bufobj);
#endif
#endif
#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH)
DUK_LOCAL_DECL void duk__json_enc_buffer_json_fastpath(duk_json_enc_ctx *js_ctx, duk_hbuffer *h);
#endif
DUK_LOCAL_DECL void duk__json_enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_uint_t depth);

/*
 *  Helper tables
 */

#if defined(DUK_USE_JSON_QUOTESTRING_FASTPATH)
DUK_LOCAL const duk_uint8_t duk__json_quotestr_lookup[256] = {
    /* 0x00 ... 0x7f: as is
     * 0x80: escape generically
     * 0x81: slow path
     * 0xa0 ... 0xff: backslash + one char
     */

    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xe2, 0xf4, 0xee, 0x80, 0xe6, 0xf2, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x20, 0x21, 0xa2, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0xdc, 0x5d, 0x5e, 0x5f,
    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x81,
    0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
    0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
    0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
    0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
    0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
    0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
    0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
    0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81
};
#else  /* DUK_USE_JSON_QUOTESTRING_FASTPATH */
DUK_LOCAL const duk_uint8_t duk__json_quotestr_esc[14] = {
    DUK_ASC_NUL, DUK_ASC_NUL, DUK_ASC_NUL, DUK_ASC_NUL,
    DUK_ASC_NUL, DUK_ASC_NUL, DUK_ASC_NUL, DUK_ASC_NUL,
    DUK_ASC_LC_B, DUK_ASC_LC_T, DUK_ASC_LC_N, DUK_ASC_NUL,
    DUK_ASC_LC_F, DUK_ASC_LC_R
};
#endif  /* DUK_USE_JSON_QUOTESTRING_FASTPATH */

#if defined(DUK_USE_JSON_DECSTRING_FASTPATH)
DUK_LOCAL const duk_uint8_t duk__json_decstr_lookup[256] = {
    /* 0x00: slow path
     * other: as is
     */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x20, 0x21, 0x00, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x00, 0x5d, 0x5e, 0x5f,
    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
};
#endif  /* DUK_USE_JSON_DECSTRING_FASTPATH */

#if defined(DUK_USE_JSON_EATWHITE_FASTPATH)
DUK_LOCAL const duk_uint8_t duk__json_eatwhite_lookup[256] = {
    /* 0x00: finish (non-white)
     * 0x01: continue
     */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
#endif  /* DUK_USE_JSON_EATWHITE_FASTPATH */

#if defined(DUK_USE_JSON_DECNUMBER_FASTPATH)
DUK_LOCAL const duk_uint8_t duk__json_decnumber_lookup[256] = {
    /* 0x00: finish (not part of number)
     * 0x01: continue
     */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
#endif  /* DUK_USE_JSON_DECNUMBER_FASTPATH */

/*
 *  Parsing implementation.
 *
 *  JSON lexer is now separate from duk_lexer.c because there are numerous
 *  small differences making it difficult to share the lexer.
 *
 *  The parser here works with raw bytes directly; this works because all
 *  JSON delimiters are ASCII characters.  Invalid xUTF-8 encoded values
 *  inside strings will be passed on without normalization; this is not a
 *  compliance concern because compliant inputs will always be valid
 *  CESU-8 encodings.
 */

DUK_LOCAL void duk__json_dec_syntax_error(duk_json_dec_ctx *js_ctx) {
    /* Shared handler to minimize parser size.  Cause will be
     * hidden, unfortunately, but we'll have an offset which
     * is often quite enough.
     */
    DUK_ERROR_FMT1(js_ctx->thr, DUK_ERR_SYNTAX_ERROR, DUK_STR_FMT_INVALID_JSON,
                   (long) (js_ctx->p - js_ctx->p_start));
    DUK_WO_NORETURN(return;);
}

DUK_LOCAL void duk__json_dec_eat_white(duk_json_dec_ctx *js_ctx) {
    const duk_uint8_t *p;
    duk_uint8_t t;

    p = js_ctx->p;
    for (;;) {
        DUK_ASSERT(p <= js_ctx->p_end);
        t = *p;

#if defined(DUK_USE_JSON_EATWHITE_FASTPATH)
        /* This fast path is pretty marginal in practice.
         * XXX: candidate for removal.
         */
        DUK_ASSERT(duk__json_eatwhite_lookup[0x00] == 0x00);  /* end-of-input breaks */
        if (duk__json_eatwhite_lookup[t] == 0) {
            break;
        }
#else  /* DUK_USE_JSON_EATWHITE_FASTPATH */
        if (!(t == 0x20 || t == 0x0a || t == 0x0d || t == 0x09)) {
            /* NUL also comes here.  Comparison order matters, 0x20
             * is most common whitespace.
             */
            break;
        }
#endif  /* DUK_USE_JSON_EATWHITE_FASTPATH */
        p++;
    }
    js_ctx->p = p;
}

#if defined(DUK_USE_JX)
DUK_LOCAL duk_uint8_t duk__json_dec_peek(duk_json_dec_ctx *js_ctx) {
    DUK_ASSERT(js_ctx->p <= js_ctx->p_end);
    return *js_ctx->p;
}
#endif

DUK_LOCAL duk_uint8_t duk__json_dec_get(duk_json_dec_ctx *js_ctx) {
    DUK_ASSERT(js_ctx->p <= js_ctx->p_end);
    return *js_ctx->p++;
}

DUK_LOCAL duk_uint8_t duk__json_dec_get_nonwhite(duk_json_dec_ctx *js_ctx) {
    duk__json_dec_eat_white(js_ctx);
    return duk__json_dec_get(js_ctx);
}

/* For JX, expressing the whole unsigned 32-bit range matters. */
DUK_LOCAL duk_uint_fast32_t duk__json_dec_decode_hex_escape(duk_json_dec_ctx *js_ctx, duk_small_uint_t n) {
    duk_small_uint_t i;
    duk_uint_fast32_t res = 0;
    duk_uint8_t x;
    duk_small_int_t t;

    for (i = 0; i < n; i++) {
        /* XXX: share helper from lexer; duk_lexer.c / hexval(). */

        x = duk__json_dec_get(js_ctx);
        DUK_DDD(DUK_DDDPRINT("decode_hex_escape: i=%ld, n=%ld, res=%ld, x=%ld",
                             (long) i, (long) n, (long) res, (long) x));

        /* x == 0x00 (EOF) causes syntax_error */
        DUK_ASSERT(duk_hex_dectab[0] == -1);
        t = duk_hex_dectab[x & 0xff];
        if (DUK_LIKELY(t >= 0)) {
            res = (res * 16) + (duk_uint_fast32_t) t;
        } else {
            /* catches EOF and invalid digits */
            goto syntax_error;
        }
    }

    DUK_DDD(DUK_DDDPRINT("final hex decoded value: %ld", (long) res));
    return res;

 syntax_error:
    duk__json_dec_syntax_error(js_ctx);
    DUK_UNREACHABLE();
    return 0;
}

DUK_LOCAL void duk__json_dec_req_stridx(duk_json_dec_ctx *js_ctx, duk_small_uint_t stridx) {
    duk_hstring *h;
    const duk_uint8_t *p;
    duk_uint8_t x, y;

    /* First character has already been eaten and checked by the caller.
     * We can scan until a NUL in stridx string because no built-in strings
     * have internal NULs.
     */

    DUK_ASSERT_STRIDX_VALID(stridx);
    h = DUK_HTHREAD_GET_STRING(js_ctx->thr, stridx);
    DUK_ASSERT(h != NULL);

    p = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h) + 1;
    DUK_ASSERT(*(js_ctx->p - 1) == *(p - 1));  /* first character has been matched */

    for (;;) {
        x = *p;
        if (x == 0) {
            break;
        }
        y = duk__json_dec_get(js_ctx);
        if (x != y) {
            /* Catches EOF of JSON input. */
            goto syntax_error;
        }
        p++;
    }

    return;

 syntax_error:
    duk__json_dec_syntax_error(js_ctx);
    DUK_UNREACHABLE();
}

DUK_LOCAL duk_small_int_t duk__json_dec_string_escape(duk_json_dec_ctx *js_ctx, duk_uint8_t **ext_p) {
    duk_uint_fast32_t cp;

    /* EOF (-1) will be cast to an unsigned value first
     * and then re-cast for the switch.  In any case, it
     * will match the default case (syntax error).
     */
    cp = (duk_uint_fast32_t) duk__json_dec_get(js_ctx);
    switch (cp) {
    case DUK_ASC_BACKSLASH: break;
    case DUK_ASC_DOUBLEQUOTE: break;
    case DUK_ASC_SLASH: break;
    case DUK_ASC_LC_T: cp = 0x09; break;
    case DUK_ASC_LC_N: cp = 0x0a; break;
    case DUK_ASC_LC_R: cp = 0x0d; break;
    case DUK_ASC_LC_F: cp = 0x0c; break;
    case DUK_ASC_LC_B: cp = 0x08; break;
    case DUK_ASC_LC_U: {
        cp = duk__json_dec_decode_hex_escape(js_ctx, 4);
        break;
    }
#if defined(DUK_USE_JX)
    case DUK_ASC_UC_U: {
        if (js_ctx->flag_ext_custom) {
            cp = duk__json_dec_decode_hex_escape(js_ctx, 8);
        } else {
            return 1;  /* syntax error */
        }
        break;
    }
    case DUK_ASC_LC_X: {
        if (js_ctx->flag_ext_custom) {
            cp = duk__json_dec_decode_hex_escape(js_ctx, 2);
        } else {
            return 1;  /* syntax error */
        }
        break;
    }
#endif  /* DUK_USE_JX */
    default:
        /* catches EOF (0x00) */
        return 1;  /* syntax error */
    }

    DUK_RAW_WRITEINC_XUTF8(*ext_p, cp);

    return 0;
}

DUK_LOCAL void duk__json_dec_string(duk_json_dec_ctx *js_ctx) {
    duk_hthread *thr = js_ctx->thr;
    duk_bufwriter_ctx bw_alloc;
    duk_bufwriter_ctx *bw;
    duk_uint8_t *q;

    /* '"' was eaten by caller */

    /* Note that we currently parse -bytes-, not codepoints.
     * All non-ASCII extended UTF-8 will encode to bytes >= 0x80,
     * so they'll simply pass through (valid UTF-8 or not).
     */

    bw = &bw_alloc;
    DUK_BW_INIT_PUSHBUF(js_ctx->thr, bw, DUK__JSON_DECSTR_BUFSIZE);
    q = DUK_BW_GET_PTR(js_ctx->thr, bw);

#if defined(DUK_USE_JSON_DECSTRING_FASTPATH)
    for (;;) {
        duk_small_uint_t safe;
        duk_uint8_t b, x;
        const duk_uint8_t *p;

        /* Select a safe loop count where no output checks are
         * needed assuming we won't encounter escapes.  Input
         * bound checks are not necessary as a NUL (guaranteed)
         * will cause a SyntaxError before we read out of bounds.
         */

        safe = DUK__JSON_DECSTR_CHUNKSIZE;

        /* Ensure space for 1:1 output plus one escape. */
        q = DUK_BW_ENSURE_RAW(js_ctx->thr, bw, safe + DUK_UNICODE_MAX_XUTF8_LENGTH, q);

        p = js_ctx->p;  /* temp copy, write back for next loop */
        for (;;) {
            if (safe == 0) {
                js_ctx->p = p;
                break;
            }
            safe--;

            /* End of input (NUL) goes through slow path and causes SyntaxError. */
            DUK_ASSERT(duk__json_decstr_lookup[0] == 0x00);

            b = *p++;
            x = (duk_small_int_t) duk__json_decstr_lookup[b];
            if (DUK_LIKELY(x != 0)) {
                /* Fast path, decode as is. */
                *q++ = b;
            } else if (b == DUK_ASC_DOUBLEQUOTE) {
                js_ctx->p = p;
                goto found_quote;
            } else if (b == DUK_ASC_BACKSLASH) {
                /* We've ensured space for one escaped input; then
                 * bail out and recheck (this makes escape handling
                 * quite slow but it's uncommon).
                 */
                js_ctx->p = p;
                if (duk__json_dec_string_escape(js_ctx, &q) != 0) {
                    goto syntax_error;
                }
                break;
            } else {
                js_ctx->p = p;
                goto syntax_error;
            }
        }
    }
 found_quote:
#else  /* DUK_USE_JSON_DECSTRING_FASTPATH */
    for (;;) {
        duk_uint8_t x;

        q = DUK_BW_ENSURE_RAW(js_ctx->thr, bw, DUK_UNICODE_MAX_XUTF8_LENGTH, q);

        x = duk__json_dec_get(js_ctx);

        if (x == DUK_ASC_DOUBLEQUOTE) {
            break;
        } else if (x == DUK_ASC_BACKSLASH) {
            if (duk__json_dec_string_escape(js_ctx, &q) != 0) {
                goto syntax_error;
            }
        } else if (x < 0x20) {
            /* catches EOF (NUL) */
            goto syntax_error;
        } else {
            *q++ = (duk_uint8_t) x;
        }
    }
#endif  /* DUK_USE_JSON_DECSTRING_FASTPATH */

    DUK_BW_SETPTR_AND_COMPACT(js_ctx->thr, bw, q);
    (void) duk_buffer_to_string(thr, -1);  /* Safe if input string is safe. */

    /* [ ... str ] */

    return;

 syntax_error:
    duk__json_dec_syntax_error(js_ctx);
    DUK_UNREACHABLE();
}

#if defined(DUK_USE_JX)
/* Decode a plain string consisting entirely of identifier characters.
 * Used to parse plain keys (e.g. "foo: 123").
 */
DUK_LOCAL void duk__json_dec_plain_string(duk_json_dec_ctx *js_ctx) {
    duk_hthread *thr = js_ctx->thr;
    const duk_uint8_t *p;
    duk_small_int_t x;

    /* Caller has already eaten the first char so backtrack one byte. */

    js_ctx->p--;  /* safe */
    p = js_ctx->p;

    /* Here again we parse bytes, and non-ASCII UTF-8 will cause end of
     * parsing (which is correct except if there are non-shortest encodings).
     * There is also no need to check explicitly for end of input buffer as
     * the input is NUL padded and NUL will exit the parsing loop.
     *
     * Because no unescaping takes place, we can just scan to the end of the
     * plain string and intern from the input buffer.
     */

    for (;;) {
        x = *p;

        /* There is no need to check the first character specially here
         * (i.e. reject digits): the caller only accepts valid initial
         * characters and won't call us if the first character is a digit.
         * This also ensures that the plain string won't be empty.
         */

        if (!duk_unicode_is_identifier_part((duk_codepoint_t) x)) {
            break;
        }
        p++;
    }

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

    /* [ ... str ] */
}
#endif  /* DUK_USE_JX */

#if defined(DUK_USE_JX)
DUK_LOCAL void duk__json_dec_pointer(duk_json_dec_ctx *js_ctx) {
    duk_hthread *thr = js_ctx->thr;
    const duk_uint8_t *p;
    duk_small_int_t x;
    void *voidptr;

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

    p = js_ctx->p;

    for (;;) {
        x = *p;

        /* Assume that the native representation never contains a closing
         * parenthesis.
         */

        if (x == DUK_ASC_RPAREN) {
            break;
        } else if (x <= 0) {
            /* NUL term or -1 (EOF), NUL check would suffice */
            goto syntax_error;
        }
        p++;
    }

    /* There is no need to NUL delimit the sscanf() call: trailing garbage is
     * ignored and there is always a NUL terminator which will force an error
     * if no error is encountered before it.  It's possible that the scan
     * would scan further than between [js_ctx->p,p[ though and we'd advance
     * by less than the scanned value.
     *
     * Because pointers are platform specific, a failure to scan a pointer
     * results in a null pointer which is a better placeholder than a missing
     * value or an error.
     */

    voidptr = NULL;
    (void) DUK_SSCANF((const char *) js_ctx->p, DUK_STR_FMT_PTR, &voidptr);
    duk_push_pointer(thr, voidptr);
    js_ctx->p = p + 1;  /* skip ')' */

    /* [ ... ptr ] */

    return;

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

#if defined(DUK_USE_JX)
DUK_LOCAL void duk__json_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__json_dec_syntax_error(js_ctx);
    DUK_UNREACHABLE();
}
#endif  /* DUK_USE_JX */

/* Parse a number, other than NaN or +/- Infinity */
DUK_LOCAL void duk__json_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__json_dec_syntax_error(js_ctx);
    }
    DUK_ASSERT(duk_is_number(thr, -1));
    DUK_DDD(DUK_DDDPRINT("parse_number: final number: %!T",
                         (duk_tval *) duk_get_tval(thr, -1)));

    /* [ ... num ] */
}

DUK_LOCAL void duk__json_dec_objarr_entry(duk_json_dec_ctx *js_ctx) {
    duk_hthread *thr = js_ctx->thr;
    duk_require_stack(thr, DUK_JSON_DEC_REQSTACK);

    /* c recursion check */

    duk_native_stack_check(thr);

    DUK_ASSERT_DISABLE(js_ctx->recursion_depth >= 0);  /* unsigned */
    DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit);
    if (js_ctx->recursion_depth >= js_ctx->recursion_limit) {
        DUK_ERROR_RANGE(thr, DUK_STR_DEC_RECLIMIT);
        DUK_WO_NORETURN(return;);
    }
    js_ctx->recursion_depth++;
}

DUK_LOCAL void duk__json_dec_objarr_exit(duk_json_dec_ctx *js_ctx) {
    /* c recursion check */

    DUK_ASSERT(js_ctx->recursion_depth > 0);
    DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit);
    js_ctx->recursion_depth--;
}

DUK_LOCAL void duk__json_dec_object(duk_json_dec_ctx *js_ctx) {
    duk_hthread *thr = js_ctx->thr;
    duk_int_t key_count;  /* XXX: a "first" flag would suffice */
    duk_uint8_t x;

    DUK_DDD(DUK_DDDPRINT("parse_object"));

    duk__json_dec_objarr_entry(js_ctx);

    duk_push_object(thr);

    /* Initial '{' has been checked and eaten by caller. */

    key_count = 0;
    for (;;) {
        x = duk__json_dec_get_nonwhite(js_ctx);

        DUK_DDD(DUK_DDDPRINT("parse_object: obj=%!T, x=%ld, key_count=%ld",
                             (duk_tval *) duk_get_tval(thr, -1),
                             (long) x, (long) key_count));

        /* handle comma and closing brace */

        if (x == DUK_ASC_COMMA && key_count > 0) {
            /* accept comma, expect new value */
            x = duk__json_dec_get_nonwhite(js_ctx);
        } else if (x == DUK_ASC_RCURLY) {
            /* eat closing brace */
            break;
        } else if (key_count == 0) {
            /* accept anything, expect first value (EOF will be
             * caught by key parsing below.
             */
            ;
        } else {
            /* catches EOF (NUL) and initial comma */
            goto syntax_error;
        }

        /* parse key and value */

        if (x == DUK_ASC_DOUBLEQUOTE) {
            duk__json_dec_string(js_ctx);
#if defined(DUK_USE_JX)
        } else if (js_ctx->flag_ext_custom &&
                   duk_unicode_is_identifier_start((duk_codepoint_t) x)) {
            duk__json_dec_plain_string(js_ctx);
#endif
        } else {
            goto syntax_error;
        }

        /* [ ... obj key ] */

        x = duk__json_dec_get_nonwhite(js_ctx);
        if (x != DUK_ASC_COLON) {
            goto syntax_error;
        }

        duk__json_dec_value(js_ctx);

        /* [ ... obj key val ] */

        duk_xdef_prop_wec(thr, -3);

        /* [ ... obj ] */

        key_count++;
    }

    /* [ ... obj ] */

    DUK_DDD(DUK_DDDPRINT("parse_object: final object is %!T",
                         (duk_tval *) duk_get_tval(thr, -1)));

    duk__json_dec_objarr_exit(js_ctx);
    return;

 syntax_error:
    duk__json_dec_syntax_error(js_ctx);
    DUK_UNREACHABLE();
}

DUK_LOCAL void duk__json_dec_array(duk_json_dec_ctx *js_ctx) {
    duk_hthread *thr = js_ctx->thr;
    duk_uarridx_t arr_idx;
    duk_uint8_t x;

    DUK_DDD(DUK_DDDPRINT("parse_array"));

    duk__json_dec_objarr_entry(js_ctx);

    duk_push_array(thr);

    /* Initial '[' has been checked and eaten by caller. */

    arr_idx = 0;
    for (;;) {
        x = duk__json_dec_get_nonwhite(js_ctx);

        DUK_DDD(DUK_DDDPRINT("parse_array: arr=%!T, x=%ld, arr_idx=%ld",
                             (duk_tval *) duk_get_tval(thr, -1),
                             (long) x, (long) arr_idx));

        /* handle comma and closing bracket */

        if ((x == DUK_ASC_COMMA) && (arr_idx != 0)) {
            /* accept comma, expect new value */
            ;
        } else if (x == DUK_ASC_RBRACKET) {
            /* eat closing bracket */
            break;
        } else if (arr_idx == 0) {
            /* accept anything, expect first value (EOF will be
             * caught by duk__json_dec_value() below.
             */
            js_ctx->p--;  /* backtrack (safe) */
        } else {
            /* catches EOF (NUL) and initial comma */
            goto syntax_error;
        }

        /* parse value */

        duk__json_dec_value(js_ctx);

        /* [ ... arr val ] */

        duk_xdef_prop_index_wec(thr, -2, arr_idx);
        arr_idx++;
    }

    /* Must set 'length' explicitly when using duk_xdef_prop_xxx() to
     * set the values.
     */

    duk_set_length(thr, -1, arr_idx);

    /* [ ... arr ] */

    DUK_DDD(DUK_DDDPRINT("parse_array: final array is %!T",
                         (duk_tval *) duk_get_tval(thr, -1)));

    duk__json_dec_objarr_exit(js_ctx);
    return;

 syntax_error:
    duk__json_dec_syntax_error(js_ctx);
    DUK_UNREACHABLE();
}

DUK_LOCAL void duk__json_dec_value(duk_json_dec_ctx *js_ctx) {
    duk_hthread *thr = js_ctx->thr;
    duk_uint8_t x;

    x = duk__json_dec_get_nonwhite(js_ctx);

    DUK_DDD(DUK_DDDPRINT("parse_value: initial x=%ld", (long) x));

    /* Note: duk__json_dec_req_stridx() backtracks one char */

    if (x == DUK_ASC_DOUBLEQUOTE) {
        duk__json_dec_string(js_ctx);
    } else if ((x >= DUK_ASC_0 && x <= DUK_ASC_9) || (x == DUK_ASC_MINUS)) {
#if defined(DUK_USE_JX)
        if (js_ctx->flag_ext_custom && x == DUK_ASC_MINUS && duk__json_dec_peek(js_ctx) == DUK_ASC_UC_I) {
            duk__json_dec_req_stridx(js_ctx, DUK_STRIDX_MINUS_INFINITY);  /* "-Infinity", '-' has been eaten */
            duk_push_number(thr, -DUK_DOUBLE_INFINITY);
        } else {
#else
        {  /* unconditional block */
#endif
            /* We already ate 'x', so backup one byte. */
            js_ctx->p--;  /* safe */
            duk__json_dec_number(js_ctx);
        }
    } else if (x == DUK_ASC_LC_T) {
        duk__json_dec_req_stridx(js_ctx, DUK_STRIDX_TRUE);
        duk_push_true(thr);
    } else if (x == DUK_ASC_LC_F) {
        duk__json_dec_req_stridx(js_ctx, DUK_STRIDX_FALSE);
        duk_push_false(thr);
    } else if (x == DUK_ASC_LC_N) {
        duk__json_dec_req_stridx(js_ctx, DUK_STRIDX_LC_NULL);
        duk_push_null(thr);
#if defined(DUK_USE_JX)
    } else if (js_ctx->flag_ext_custom && x == DUK_ASC_LC_U) {
        duk__json_dec_req_stridx(js_ctx, DUK_STRIDX_LC_UNDEFINED);
        duk_push_undefined(thr);
    } else if (js_ctx->flag_ext_custom && x == DUK_ASC_UC_N) {
        duk__json_dec_req_stridx(js_ctx, DUK_STRIDX_NAN);
        duk_push_nan(thr);
    } else if (js_ctx->flag_ext_custom && x == DUK_ASC_UC_I) {
        duk__json_dec_req_stridx(js_ctx, DUK_STRIDX_INFINITY);
        duk_push_number(thr, DUK_DOUBLE_INFINITY);
    } else if (js_ctx->flag_ext_custom && x == DUK_ASC_LPAREN) {
        duk__json_dec_pointer(js_ctx);
    } else if (js_ctx->flag_ext_custom && x == DUK_ASC_PIPE) {
        duk__json_dec_buffer(js_ctx);
#endif
    } else if (x == DUK_ASC_LCURLY) {
        duk__json_dec_object(js_ctx);
    } else if (x == DUK_ASC_LBRACKET) {
        duk__json_dec_array(js_ctx);
    } else {
        /* catches EOF (NUL) */
        goto syntax_error;
    }

    duk__json_dec_eat_white(js_ctx);

    /* [ ... val ] */
    return;

 syntax_error:
    duk__json_dec_syntax_error(js_ctx);
    DUK_UNREACHABLE();
}

/* Recursive value reviver, implements the Walk() algorithm.  The parsing
 * step ensures there is a reasonable depth limit to the input.  However,
 * the reviver may create more depth by editing object or array entries, so
 * we have both C recursion limit and native stack checks here.
 */
DUK_LOCAL void duk__json_dec_reviver_walk(duk_json_dec_ctx *js_ctx) {
    duk_hthread *thr = js_ctx->thr;
    duk_hobject *h;
    duk_uarridx_t i, arr_len;

    duk__json_dec_objarr_entry(js_ctx);

    DUK_DDD(DUK_DDDPRINT("walk: top=%ld, holder=%!T, name=%!T",
                         (long) duk_get_top(thr),
                         (duk_tval *) duk_get_tval(thr, -2),
                         (duk_tval *) duk_get_tval(thr, -1)));

    duk_dup_top(thr);
    duk_get_prop(thr, -3);  /* -> [ ... holder name val ] */

    h = duk_get_hobject(thr, -1);
    if (h != NULL) {
        if (duk_js_isarray_hobject(h)) {
            arr_len = (duk_uarridx_t) duk_get_length(thr, -1);
            for (i = 0; i < arr_len; i++) {
                /* [ ... holder name val ] */

                DUK_DDD(DUK_DDDPRINT("walk: array, top=%ld, i=%ld, arr_len=%ld, holder=%!T, name=%!T, val=%!T",
                                     (long) duk_get_top(thr), (long) i, (long) arr_len,
                                     (duk_tval *) duk_get_tval(thr, -3), (duk_tval *) duk_get_tval(thr, -2),
                                     (duk_tval *) duk_get_tval(thr, -1)));

                duk_dup_top(thr);
                (void) duk_push_uint_to_hstring(thr, (duk_uint_t) i);  /* -> [ ... holder name val val ToString(i) ] */
                duk__json_dec_reviver_walk(js_ctx);  /* -> [ ... holder name val new_elem ] */

                if (duk_is_undefined(thr, -1)) {
                    duk_pop(thr);
                    duk_del_prop_index(thr, -1, i);
                } else {
                    /* XXX: duk_xdef_prop_index_wec() would be more appropriate
                     * here but it currently makes some assumptions that might
                     * not hold (e.g. that previous property is not an accessor).
                     */
                    duk_put_prop_index(thr, -2, i);
                }
            }
        } else {
            /* [ ... holder name val ] */
            duk_enum(thr, -1, DUK_ENUM_OWN_PROPERTIES_ONLY /*flags*/);
            while (duk_next(thr, -1 /*enum_index*/, 0 /*get_value*/)) {
                DUK_DDD(DUK_DDDPRINT("walk: object, top=%ld, holder=%!T, name=%!T, val=%!T, enum=%!iT, obj_key=%!T",
                                     (long) duk_get_top(thr), (duk_tval *) duk_get_tval(thr, -5),
                                     (duk_tval *) duk_get_tval(thr, -4), (duk_tval *) duk_get_tval(thr, -3),
                                     (duk_tval *) duk_get_tval(thr, -2), (duk_tval *) duk_get_tval(thr, -1)));

                /* [ ... holder name val enum obj_key ] */
                duk_dup_m3(thr);
                duk_dup_m2(thr);

                /* [ ... holder name val enum obj_key val obj_key ] */
                duk__json_dec_reviver_walk(js_ctx);

                /* [ ... holder name val enum obj_key new_elem ] */
                if (duk_is_undefined(thr, -1)) {
                    duk_pop(thr);
                    duk_del_prop(thr, -3);
                } else {
                    /* XXX: duk_xdef_prop_index_wec() would be more appropriate
                     * here but it currently makes some assumptions that might
                     * not hold (e.g. that previous property is not an accessor).
                     *
                     * Using duk_put_prop() works incorrectly with '__proto__'
                     * if the own property with that name has been deleted.  This
                     * does not happen normally, but a clever reviver can trigger
                     * that, see complex reviver case in: test-bug-json-parse-__proto__.js.
                     */
                    duk_put_prop(thr, -4);
                }
            }
            duk_pop(thr);  /* pop enum */
        }
    }

    /* [ ... holder name val ] */

    duk_dup(thr, js_ctx->idx_reviver);
    duk_insert(thr, -4);  /* -> [ ... reviver holder name val ] */
    duk_call_method(thr, 2);  /* -> [ ... res ] */

    duk__json_dec_objarr_exit(js_ctx);

    DUK_DDD(DUK_DDDPRINT("walk: top=%ld, result=%!T",
                         (long) duk_get_top(thr), (duk_tval *) duk_get_tval(thr, -1)));
}

/*
 *  Stringify implementation.
 */

#define DUK__EMIT_1(js_ctx,ch)          duk__emit_1((js_ctx), (duk_uint_fast8_t) (ch))
#define DUK__EMIT_2(js_ctx,ch1,ch2)     duk__emit_2((js_ctx), (duk_uint_fast8_t) (ch1), (duk_uint_fast8_t) (ch2))
#define DUK__EMIT_HSTR(js_ctx,h)        duk__emit_hstring((js_ctx), (h))
#if defined(DUK_USE_FASTINT) || defined(DUK_USE_JX) || defined(DUK_USE_JC)
#define DUK__EMIT_CSTR(js_ctx,p)        duk__emit_cstring((js_ctx), (p))
#endif
#define DUK__EMIT_STRIDX(js_ctx,i)      duk__emit_stridx((js_ctx), (i))
#define DUK__UNEMIT_1(js_ctx)           duk__unemit_1((js_ctx))

DUK_LOCAL void duk__emit_1(duk_json_enc_ctx *js_ctx, duk_uint_fast8_t ch) {
    DUK_BW_WRITE_ENSURE_U8(js_ctx->thr, &js_ctx->bw, ch);
}

DUK_LOCAL void duk__emit_2(duk_json_enc_ctx *js_ctx, duk_uint_fast8_t ch1, duk_uint_fast8_t ch2) {
    DUK_BW_WRITE_ENSURE_U8_2(js_ctx->thr, &js_ctx->bw, ch1, ch2);
}

DUK_LOCAL void duk__emit_hstring(duk_json_enc_ctx *js_ctx, duk_hstring *h) {
    DUK_BW_WRITE_ENSURE_HSTRING(js_ctx->thr, &js_ctx->bw, h);
}

#if defined(DUK_USE_FASTINT) || defined(DUK_USE_JX) || defined(DUK_USE_JC)
DUK_LOCAL void duk__emit_cstring(duk_json_enc_ctx *js_ctx, const char *str) {
    DUK_BW_WRITE_ENSURE_CSTRING(js_ctx->thr, &js_ctx->bw, str);
}
#endif

DUK_LOCAL void duk__emit_stridx(duk_json_enc_ctx *js_ctx, duk_small_uint_t stridx) {
    duk_hstring *h;

    DUK_ASSERT_STRIDX_VALID(stridx);
    h = DUK_HTHREAD_GET_STRING(js_ctx->thr, stridx);
    DUK_ASSERT(h != NULL);

    DUK_BW_WRITE_ENSURE_HSTRING(js_ctx->thr, &js_ctx->bw, h);
}

DUK_LOCAL void duk__unemit_1(duk_json_enc_ctx *js_ctx) {
    DUK_ASSERT(DUK_BW_GET_SIZE(js_ctx->thr, &js_ctx->bw) >= 1);
    DUK_BW_ADD_PTR(js_ctx->thr, &js_ctx->bw, -1);
}

#define DUK__MKESC(nybbles,esc1,esc2)  \
    (((duk_uint_fast32_t) (nybbles)) << 16) | \
    (((duk_uint_fast32_t) (esc1)) << 8) | \
    ((duk_uint_fast32_t) (esc2))

DUK_LOCAL duk_uint8_t *duk__emit_esc_auto_fast(duk_json_enc_ctx *js_ctx, duk_uint_fast32_t cp, duk_uint8_t *q) {
    duk_uint_fast32_t tmp;
    duk_small_uint_t dig;

    DUK_UNREF(js_ctx);

    /* Caller ensures space for at least DUK__JSON_MAX_ESC_LEN. */

    /* Select appropriate escape format automatically, and set 'tmp' to a
     * value encoding both the escape format character and the nybble count:
     *
     *   (nybble_count << 16) | (escape_char1) | (escape_char2)
     */

#if defined(DUK_USE_JX)
    if (DUK_LIKELY(cp < 0x100UL)) {
        if (DUK_UNLIKELY(js_ctx->flag_ext_custom != 0U)) {
            tmp = DUK__MKESC(2, DUK_ASC_BACKSLASH, DUK_ASC_LC_X);
        } else {
            tmp = DUK__MKESC(4, DUK_ASC_BACKSLASH, DUK_ASC_LC_U);
        }
    } else
#endif
    if (DUK_LIKELY(cp < 0x10000UL)) {
        tmp = DUK__MKESC(4, DUK_ASC_BACKSLASH, DUK_ASC_LC_U);
    } else {
#if defined(DUK_USE_JX)
        if (DUK_LIKELY(js_ctx->flag_ext_custom != 0U)) {
            tmp = DUK__MKESC(8, DUK_ASC_BACKSLASH, DUK_ASC_UC_U);
        } else
#endif
        {
            /* In compatible mode and standard JSON mode, output
             * something useful for non-BMP characters.  This won't
             * roundtrip but will still be more or less readable and
             * more useful than an error.
             */
            tmp = DUK__MKESC(8, DUK_ASC_UC_U, DUK_ASC_PLUS);
        }
    }

    *q++ = (duk_uint8_t) ((tmp >> 8) & 0xff);
    *q++ = (duk_uint8_t) (tmp & 0xff);

    tmp = tmp >> 16;
    while (tmp > 0) {
        tmp--;
        dig = (duk_small_uint_t) ((cp >> (4 * tmp)) & 0x0f);
        *q++ = duk_lc_digits[dig];
    }

    return q;
}

DUK_LOCAL void duk__json_enc_key_autoquote(duk_json_enc_ctx *js_ctx, duk_hstring *k) {
    const duk_int8_t *p, *p_start, *p_end;  /* Note: intentionally signed. */
    duk_size_t k_len;
    duk_codepoint_t cp;

    DUK_ASSERT(k != NULL);

    /* Accept ASCII strings which conform to identifier requirements
     * as being emitted without key quotes.  Since we only accept ASCII
     * there's no need for actual decoding: 'p' is intentionally signed
     * so that bytes >= 0x80 extend to negative values and are rejected
     * as invalid identifier codepoints.
     */

    if (js_ctx->flag_avoid_key_quotes) {
        k_len = DUK_HSTRING_GET_BYTELEN(k);
        p_start = (const duk_int8_t *) DUK_HSTRING_GET_DATA(k);
        p_end = p_start + k_len;
        p = p_start;

        if (p == p_end) {
            /* Zero length string is not accepted without quotes */
            goto quote_normally;
        }
        cp = (duk_codepoint_t) (*p++);
        if (DUK_UNLIKELY(!duk_unicode_is_identifier_start(cp))) {
            goto quote_normally;
        }
        while (p < p_end) {
            cp = (duk_codepoint_t) (*p++);
            if (DUK_UNLIKELY(!duk_unicode_is_identifier_part(cp))) {
                goto quote_normally;
            }
        }

        /* This seems faster than emitting bytes one at a time and
         * then potentially rewinding.
         */
        DUK__EMIT_HSTR(js_ctx, k);
        return;
    }

 quote_normally:
    duk__json_enc_quote_string(js_ctx, k);
}

/* The Quote(value) operation: quote a string.
 *
 * Stack policy: [ ] -> [ ].
 */

DUK_LOCAL void duk__json_enc_quote_string(duk_json_enc_ctx *js_ctx, duk_hstring *h_str) {
    duk_hthread *thr = js_ctx->thr;
    const duk_uint8_t *p, *p_start, *p_end, *p_now, *p_tmp;
    duk_uint8_t *q;
    duk_ucodepoint_t cp;  /* typed for duk_unicode_decode_xutf8() */

    DUK_DDD(DUK_DDDPRINT("duk__json_enc_quote_string: h_str=%!O", (duk_heaphdr *) h_str));

    DUK_ASSERT(h_str != NULL);
    p_start = DUK_HSTRING_GET_DATA(h_str);
    p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_str);
    p = p_start;

    DUK__EMIT_1(js_ctx, DUK_ASC_DOUBLEQUOTE);

    /* Encode string in small chunks, estimating the maximum expansion so that
     * there's no need to ensure space while processing the chunk.
     */

    while (p < p_end) {
        duk_size_t left, now, space;

        left = (duk_size_t) (p_end - p);
        now = (left > DUK__JSON_ENCSTR_CHUNKSIZE ?
               DUK__JSON_ENCSTR_CHUNKSIZE : left);

        /* Maximum expansion per input byte is 6:
         *   - invalid UTF-8 byte causes "\uXXXX" to be emitted (6/1 = 6).
         *   - 2-byte UTF-8 encodes as "\uXXXX" (6/2 = 3).
         *   - 4-byte UTF-8 encodes as "\Uxxxxxxxx" (10/4 = 2.5).
         */
        space = now * 6;
        q = DUK_BW_ENSURE_GETPTR(thr, &js_ctx->bw, space);

        p_now = p + now;

        while (p < p_now) {
#if defined(DUK_USE_JSON_QUOTESTRING_FASTPATH)
            duk_uint8_t b;

            b = duk__json_quotestr_lookup[*p++];
            if (DUK_LIKELY(b < 0x80)) {
                /* Most input bytes go through here. */
                *q++ = b;
            } else if (b >= 0xa0) {
                *q++ = DUK_ASC_BACKSLASH;
                *q++ = (duk_uint8_t) (b - 0x80);
            } else if (b == 0x80) {
                cp = (duk_ucodepoint_t) (*(p - 1));
                q = duk__emit_esc_auto_fast(js_ctx, cp, q);
            } else if (b == 0x7f && js_ctx->flag_ascii_only) {
                /* 0x7F is special */
                DUK_ASSERT(b == 0x81);
                cp = (duk_ucodepoint_t) 0x7f;
                q = duk__emit_esc_auto_fast(js_ctx, cp, q);
            } else {
                DUK_ASSERT(b == 0x81);
                p--;

                /* slow path is shared */
#else  /* DUK_USE_JSON_QUOTESTRING_FASTPATH */
            cp = *p;

            if (DUK_LIKELY(cp <= 0x7f)) {
                /* ascii fast path: avoid decoding utf-8 */
                p++;
                if (cp == 0x22 || cp == 0x5c) {
                    /* double quote or backslash */
                    *q++ = DUK_ASC_BACKSLASH;
                    *q++ = (duk_uint8_t) cp;
                } else if (cp < 0x20) {
                    duk_uint_fast8_t esc_char;

                    /* This approach is a bit shorter than a straight
                     * if-else-ladder and also a bit faster.
                     */
                    if (cp < (sizeof(duk__json_quotestr_esc) / sizeof(duk_uint8_t)) &&
                        (esc_char = duk__json_quotestr_esc[cp]) != 0) {
                        *q++ = DUK_ASC_BACKSLASH;
                        *q++ = (duk_uint8_t) esc_char;
                    } else {
                        q = duk__emit_esc_auto_fast(js_ctx, cp, q);
                    }
                } else if (cp == 0x7f && js_ctx->flag_ascii_only) {
                    q = duk__emit_esc_auto_fast(js_ctx, cp, q);
                } else {
                    /* any other printable -> as is */
                    *q++ = (duk_uint8_t) cp;
                }
            } else {
                /* slow path is shared */
#endif  /* DUK_USE_JSON_QUOTESTRING_FASTPATH */

                /* slow path decode */

                /* If XUTF-8 decoding fails, treat the offending byte as a codepoint directly
                 * and go forward one byte.  This is of course very lossy, but allows some kind
                 * of output to be produced even for internal strings which don't conform to
                 * XUTF-8.  All standard ECMAScript strings are always CESU-8, so this behavior
                 * does not violate the ECMAScript specification.  The behavior is applied to
                 * all modes, including ECMAScript standard JSON.  Because the current XUTF-8
                 * decoding is not very strict, this behavior only really affects initial bytes
                 * and truncated codepoints.
                 *
                 * Another alternative would be to scan forwards to start of next codepoint
                 * (or end of input) and emit just one replacement codepoint.
                 */

                p_tmp = p;
                if (!duk_unicode_decode_xutf8(thr, &p, p_start, p_end, &cp)) {
                    /* Decode failed. */
                    cp = *p_tmp;
                    p = p_tmp + 1;
                }

#if defined(DUK_USE_NONSTD_JSON_ESC_U2028_U2029)
                if (js_ctx->flag_ascii_only || cp == 0x2028 || cp == 0x2029) {
#else
                if (js_ctx->flag_ascii_only) {
#endif
                    q = duk__emit_esc_auto_fast(js_ctx, cp, q);
                } else {
                    /* as is */
                    DUK_RAW_WRITEINC_XUTF8(q, cp);
                }
            }
        }

        DUK_BW_SET_PTR(thr, &js_ctx->bw, q);
    }

    DUK__EMIT_1(js_ctx, DUK_ASC_DOUBLEQUOTE);
}

/* Encode a double (checked by caller) from stack top.  Stack top may be
 * replaced by serialized string but is not popped (caller does that).
 */
DUK_LOCAL void duk__json_enc_double(duk_json_enc_ctx *js_ctx) {
    duk_hthread *thr;
    duk_tval *tv;
    duk_double_t d;
    duk_small_int_t c;
    duk_small_int_t s;
    duk_small_uint_t stridx;
    duk_small_uint_t n2s_flags;
    duk_hstring *h_str;

    DUK_ASSERT(js_ctx != NULL);
    thr = js_ctx->thr;
    DUK_ASSERT(thr != NULL);

    /* Caller must ensure 'tv' is indeed a double and not a fastint! */
    tv = DUK_GET_TVAL_NEGIDX(thr, -1);
    DUK_ASSERT(DUK_TVAL_IS_DOUBLE(tv));
    d = DUK_TVAL_GET_DOUBLE(tv);

    c = (duk_small_int_t) DUK_FPCLASSIFY(d);
    s = (duk_small_int_t) DUK_SIGNBIT(d);
    DUK_UNREF(s);

    if (DUK_LIKELY(!(c == DUK_FP_INFINITE || c == DUK_FP_NAN))) {
        DUK_ASSERT(DUK_ISFINITE(d));

#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
        /* Negative zero needs special handling in JX/JC because
         * it would otherwise serialize to '0', not '-0'.
         */
        if (DUK_UNLIKELY(c == DUK_FP_ZERO && s != 0 &&
                         (js_ctx->flag_ext_custom_or_compatible))) {
            duk_push_hstring_stridx(thr, DUK_STRIDX_MINUS_ZERO);  /* '-0' */
        } else
#endif  /* DUK_USE_JX || DUK_USE_JC */
        {
            n2s_flags = 0;
            /* [ ... number ] -> [ ... string ] */
            duk_numconv_stringify(thr, 10 /*radix*/, 0 /*digits*/, n2s_flags);
        }
        h_str = duk_known_hstring(thr, -1);
        DUK__EMIT_HSTR(js_ctx, h_str);
        return;
    }

#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
    if (!(js_ctx->flags & (DUK_JSON_FLAG_EXT_CUSTOM |
                           DUK_JSON_FLAG_EXT_COMPATIBLE))) {
        stridx = DUK_STRIDX_LC_NULL;
    } else if (c == DUK_FP_NAN) {
        stridx = js_ctx->stridx_custom_nan;
    } else if (s == 0) {
        stridx = js_ctx->stridx_custom_posinf;
    } else {
        stridx = js_ctx->stridx_custom_neginf;
    }
#else
    stridx = DUK_STRIDX_LC_NULL;
#endif
    DUK__EMIT_STRIDX(js_ctx, stridx);
}

#if defined(DUK_USE_FASTINT)
/* Encode a fastint from duk_tval ptr, no value stack effects. */
DUK_LOCAL void duk__json_enc_fastint_tval(duk_json_enc_ctx *js_ctx, duk_tval *tv) {
    duk_int64_t v;

    /* Fastint range is signed 48-bit so longest value is -2^47 = -140737488355328
     * (16 chars long), longest signed 64-bit value is -2^63 = -9223372036854775808
     * (20 chars long).  Alloc space for 64-bit range to be safe.
     */
    duk_uint8_t buf[20 + 1];

    /* Caller must ensure 'tv' is indeed a fastint! */
    DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv));
    v = DUK_TVAL_GET_FASTINT(tv);

    /* XXX: There are no format strings in duk_config.h yet, could add
     * one for formatting duk_int64_t.  For now, assumes "%lld" and that
     * "long long" type exists.  Could also rely on C99 directly but that
     * won't work for older MSVC.
     */
    DUK_SPRINTF((char *) buf, "%lld", (long long) v);
    DUK__EMIT_CSTR(js_ctx, (const char *) buf);
}
#endif

#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
#if defined(DUK_USE_HEX_FASTPATH)
DUK_LOCAL duk_uint8_t *duk__json_enc_buffer_data_hex(const duk_uint8_t *src, duk_size_t src_len, duk_uint8_t *dst) {
    duk_uint8_t *q;
    duk_uint16_t *q16;
    duk_small_uint_t x;
    duk_size_t i, len_safe;
#if !defined(DUK_USE_UNALIGNED_ACCESSES_POSSIBLE)
    duk_bool_t shift_dst;
#endif

    /* Unlike in duk_hex_encode() 'dst' is not necessarily aligned by 2.
     * For platforms where unaligned accesses are not allowed, shift 'dst'
     * ahead by 1 byte to get alignment and then duk_memmove() the result
     * in place.  The faster encoding loop makes up the difference.
     * There's always space for one extra byte because a terminator always
     * follows the hex data and that's been accounted for by the caller.
     */

#if defined(DUK_USE_UNALIGNED_ACCESSES_POSSIBLE)
    q16 = (duk_uint16_t *) (void *) dst;
#else
    shift_dst = (duk_bool_t) (((duk_size_t) dst) & 0x01U);
    if (shift_dst) {
        DUK_DD(DUK_DDPRINT("unaligned accesses not possible, dst not aligned -> step to dst + 1"));
        q16 = (duk_uint16_t *) (void *) (dst + 1);
    } else {
        DUK_DD(DUK_DDPRINT("unaligned accesses not possible, dst is aligned"));
        q16 = (duk_uint16_t *) (void *) dst;
    }
    DUK_ASSERT((((duk_size_t) q16) & 0x01U) == 0);
#endif

    len_safe = src_len & ~0x03U;
    for (i = 0; i < len_safe; i += 4) {
        q16[0] = duk_hex_enctab[src[i]];
        q16[1] = duk_hex_enctab[src[i + 1]];
        q16[2] = duk_hex_enctab[src[i + 2]];
        q16[3] = duk_hex_enctab[src[i + 3]];
        q16 += 4;
    }
    q = (duk_uint8_t *) q16;

#if !defined(DUK_USE_UNALIGNED_ACCESSES_POSSIBLE)
    if (shift_dst) {
        q--;
        duk_memmove((void *) dst, (const void *) (dst + 1), 2 * len_safe);
        DUK_ASSERT(dst + 2 * len_safe == q);
    }
#endif

    for (; i < src_len; i++) {
        x = src[i];
        *q++ = duk_lc_digits[x >> 4];
        *q++ = duk_lc_digits[x & 0x0f];
    }

    return q;
}
#else  /* DUK_USE_HEX_FASTPATH */
DUK_LOCAL duk_uint8_t *duk__json_enc_buffer_data_hex(const duk_uint8_t *src, duk_size_t src_len, duk_uint8_t *dst) {
    const duk_uint8_t *p;
    const duk_uint8_t *p_end;
    duk_uint8_t *q;
    duk_small_uint_t x;

    p = src;
    p_end = src + src_len;
    q = dst;
    while (p != p_end) {
        x = *p++;
        *q++ = duk_lc_digits[x >> 4];
        *q++ = duk_lc_digits[x & 0x0f];
    }

    return q;
}
#endif  /* DUK_USE_HEX_FASTPATH */

DUK_LOCAL void duk__json_enc_buffer_data(duk_json_enc_ctx *js_ctx, duk_uint8_t *buf_data, duk_size_t buf_len) {
    duk_hthread *thr;
    duk_uint8_t *q;
    duk_size_t space;

    thr = js_ctx->thr;

    DUK_ASSERT(js_ctx->flag_ext_custom || js_ctx->flag_ext_compatible);  /* caller checks */
    DUK_ASSERT(js_ctx->flag_ext_custom_or_compatible);

    /* Buffer values are encoded in (lowercase) hex to make the
     * binary data readable.  Base64 or similar would be more
     * compact but less readable, and the point of JX/JC
     * variants is to be as useful to a programmer as possible.
     */

    /* The #if defined() clutter here needs to handle the three
     * cases: (1) JX+JC, (2) JX only, (3) JC only.
     */

    /* Note: space must cater for both JX and JC. */
    space = 9 + buf_len * 2 + 2;
    DUK_ASSERT(DUK_HBUFFER_MAX_BYTELEN <= 0x7ffffffeUL);
    DUK_ASSERT((space - 2) / 2 >= buf_len);  /* overflow not possible, buffer limits */
    q = DUK_BW_ENSURE_GETPTR(thr, &js_ctx->bw, space);

#if defined(DUK_USE_JX) && defined(DUK_USE_JC)
    if (js_ctx->flag_ext_custom)
#endif
#if defined(DUK_USE_JX)
    {
        *q++ = DUK_ASC_PIPE;
        q = duk__json_enc_buffer_data_hex(buf_data, buf_len, q);
        *q++ = DUK_ASC_PIPE;

    }
#endif
#if defined(DUK_USE_JX) && defined(DUK_USE_JC)
    else
#endif
#if defined(DUK_USE_JC)
    {
        DUK_ASSERT(js_ctx->flag_ext_compatible);
        duk_memcpy((void *) q, (const void *) "{\"_buf\":\"", 9);  /* len: 9 */
        q += 9;
        q = duk__json_enc_buffer_data_hex(buf_data, buf_len, q);
        *q++ = DUK_ASC_DOUBLEQUOTE;
        *q++ = DUK_ASC_RCURLY;
    }
#endif

    DUK_BW_SET_PTR(thr, &js_ctx->bw, q);
}

DUK_LOCAL void duk__json_enc_buffer_jx_jc(duk_json_enc_ctx *js_ctx, duk_hbuffer *h) {
    duk__json_enc_buffer_data(js_ctx,
                         (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(js_ctx->thr->heap, h),
                         (duk_size_t) DUK_HBUFFER_GET_SIZE(h));
}
#endif  /* DUK_USE_JX || DUK_USE_JC */

#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH)
DUK_LOCAL void duk__json_enc_buffer_json_fastpath(duk_json_enc_ctx *js_ctx, duk_hbuffer *h) {
    duk_size_t i, n;
    const duk_uint8_t *buf;
    duk_uint8_t *q;

    n = DUK_HBUFFER_GET_SIZE(h);
    if (n == 0) {
        DUK__EMIT_2(js_ctx, DUK_ASC_LCURLY, DUK_ASC_RCURLY);
        return;
    }

    DUK__EMIT_1(js_ctx, DUK_ASC_LCURLY);

    /* Maximum encoded length with 32-bit index: 1 + 10 + 2 + 3 + 1 + 1 = 18,
     * with 64-bit index: 1 + 20 + 2 + 3 + 1 + 1 = 28.  32 has some slack.
     *
     * Note that because the output buffer is reallocated from time to time,
     * side effects (such as finalizers) affecting the buffer 'h' must be
     * disabled.  This is the case in the JSON.stringify() fast path.
     */

    buf = (const duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(js_ctx->thr->heap, h);
    if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) {
        for (i = 0; i < n; i++) {
            duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth + 1);
            q = DUK_BW_ENSURE_GETPTR(js_ctx->thr, &js_ctx->bw, 32);
            q += DUK_SPRINTF((char *) q, "\"%lu\": %u,", (unsigned long) i, (unsigned int) buf[i]);
            DUK_BW_SET_PTR(js_ctx->thr, &js_ctx->bw, q);
        }
    } else {
        q = DUK_BW_GET_PTR(js_ctx->thr, &js_ctx->bw);
        for (i = 0; i < n; i++) {
            q = DUK_BW_ENSURE_RAW(js_ctx->thr, &js_ctx->bw, 32, q);
            q += DUK_SPRINTF((char *) q, "\"%lu\":%u,", (unsigned long) i, (unsigned int) buf[i]);
        }
        DUK_BW_SET_PTR(js_ctx->thr, &js_ctx->bw, q);
    }
    DUK__UNEMIT_1(js_ctx);  /* eat trailing comma */

    if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) {
        duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth);
    }
    DUK__EMIT_1(js_ctx, DUK_ASC_RCURLY);
}
#endif  /* DUK_USE_JSON_STRINGIFY_FASTPATH */

#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
DUK_LOCAL void duk__json_enc_pointer(duk_json_enc_ctx *js_ctx, void *ptr) {
    char buf[64];  /* XXX: how to figure correct size? */
    const char *fmt;

    DUK_ASSERT(js_ctx->flag_ext_custom || js_ctx->flag_ext_compatible);  /* caller checks */
    DUK_ASSERT(js_ctx->flag_ext_custom_or_compatible);

    duk_memzero(buf, sizeof(buf));

    /* The #if defined() clutter here needs to handle the three
     * cases: (1) JX+JC, (2) JX only, (3) JC only.
     */
#if defined(DUK_USE_JX) && defined(DUK_USE_JC)
    if (js_ctx->flag_ext_custom)
#endif
#if defined(DUK_USE_JX)
    {
        fmt = ptr ? "(%p)" : "(null)";
    }
#endif
#if defined(DUK_USE_JX) && defined(DUK_USE_JC)
    else
#endif
#if defined(DUK_USE_JC)
    {
        DUK_ASSERT(js_ctx->flag_ext_compatible);
        fmt = ptr ? "{\"_ptr\":\"%p\"}" : "{\"_ptr\":\"null\"}";
    }
#endif

    /* When ptr == NULL, the format argument is unused. */
    DUK_SNPRINTF(buf, sizeof(buf) - 1, fmt, ptr);  /* must not truncate */
    DUK__EMIT_CSTR(js_ctx, buf);
}
#endif  /* DUK_USE_JX || DUK_USE_JC */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
DUK_LOCAL void duk__json_enc_bufobj(duk_json_enc_ctx *js_ctx, duk_hbufobj *h_bufobj) {
    DUK_HBUFOBJ_ASSERT_VALID(h_bufobj);

    if (h_bufobj->buf == NULL || !DUK_HBUFOBJ_VALID_SLICE(h_bufobj)) {
        DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL);
    } else {
        /* Handle both full and partial slice (as long as covered). */
        duk__json_enc_buffer_data(js_ctx,
                             (duk_uint8_t *) DUK_HBUFOBJ_GET_SLICE_BASE(js_ctx->thr->heap, h_bufobj),
                             (duk_size_t) h_bufobj->length);
    }
}
#endif  /* DUK_USE_JX || DUK_USE_JC */
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/* Indent helper.  Calling code relies on js_ctx->recursion_depth also being
 * directly related to indent depth.
 */
#if defined(DUK_USE_PREFER_SIZE)
DUK_LOCAL void duk__json_enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_uint_t depth) {
    DUK_ASSERT(js_ctx->h_gap != NULL);
    DUK_ASSERT(DUK_HSTRING_GET_BYTELEN(js_ctx->h_gap) > 0);  /* caller guarantees */

    DUK__EMIT_1(js_ctx, 0x0a);
    while (depth-- > 0) {
        DUK__EMIT_HSTR(js_ctx, js_ctx->h_gap);
    }
}
#else  /* DUK_USE_PREFER_SIZE */
DUK_LOCAL void duk__json_enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_uint_t depth) {
    const duk_uint8_t *gap_data;
    duk_size_t gap_len;
    duk_size_t avail_bytes;   /* bytes of indent available for copying */
    duk_size_t need_bytes;    /* bytes of indent still needed */
    duk_uint8_t *p_start;
    duk_uint8_t *p;

    DUK_ASSERT(js_ctx->h_gap != NULL);
    DUK_ASSERT(DUK_HSTRING_GET_BYTELEN(js_ctx->h_gap) > 0);  /* caller guarantees */

    DUK__EMIT_1(js_ctx, 0x0a);
    if (DUK_UNLIKELY(depth == 0)) {
        return;
    }

    /* To handle deeper indents efficiently, make use of copies we've
     * already emitted.  In effect we can emit a sequence of 1, 2, 4,
     * 8, etc copies, and then finish the last run.  Byte counters
     * avoid multiply with gap_len on every loop.
     */

    gap_data = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(js_ctx->h_gap);
    gap_len = (duk_size_t) DUK_HSTRING_GET_BYTELEN(js_ctx->h_gap);
    DUK_ASSERT(gap_len > 0);

    need_bytes = gap_len * depth;
    p = DUK_BW_ENSURE_GETPTR(js_ctx->thr, &js_ctx->bw, need_bytes);
    p_start = p;

    duk_memcpy((void *) p, (const void *) gap_data, (size_t) gap_len);
    p += gap_len;
    avail_bytes = gap_len;
    DUK_ASSERT(need_bytes >= gap_len);
    need_bytes -= gap_len;

    while (need_bytes >= avail_bytes) {
        duk_memcpy((void *) p, (const void *) p_start, (size_t) avail_bytes);
        p += avail_bytes;
        need_bytes -= avail_bytes;
        avail_bytes <<= 1;
    }

    DUK_ASSERT(need_bytes < avail_bytes);  /* need_bytes may be zero */
    duk_memcpy((void *) p, (const void *) p_start, (size_t) need_bytes);
    p += need_bytes;
    /*avail_bytes += need_bytes*/

    DUK_BW_SET_PTR(js_ctx->thr, &js_ctx->bw, p);
}
#endif  /* DUK_USE_PREFER_SIZE */

/* Shared entry handling for object/array serialization. */
DUK_LOCAL void duk__json_enc_objarr_entry(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_top) {
    duk_hthread *thr = js_ctx->thr;
    duk_hobject *h_target;
    duk_uint_fast32_t i, n;

    *entry_top = duk_get_top(thr);

    duk_native_stack_check(thr);
    duk_require_stack(thr, DUK_JSON_ENC_REQSTACK);

    /* Loop check using a hybrid approach: a fixed-size visited[] array
     * with overflow in a loop check object.
     */

    h_target = duk_known_hobject(thr, -1);  /* object or array */

    n = js_ctx->recursion_depth;
    if (DUK_UNLIKELY(n > DUK_JSON_ENC_LOOPARRAY)) {
        n = DUK_JSON_ENC_LOOPARRAY;
    }
    for (i = 0; i < n; i++) {
        if (DUK_UNLIKELY(js_ctx->visiting[i] == h_target)) {
            DUK_DD(DUK_DDPRINT("slow path loop detect"));
            DUK_ERROR_TYPE(thr, DUK_STR_CYCLIC_INPUT);
            DUK_WO_NORETURN(return;);
        }
    }
    if (js_ctx->recursion_depth < DUK_JSON_ENC_LOOPARRAY) {
        js_ctx->visiting[js_ctx->recursion_depth] = h_target;
    } else {
        duk_push_sprintf(thr, DUK_STR_FMT_PTR, (void *) h_target);
        duk_dup_top(thr);  /* -> [ ... voidp voidp ] */
        if (duk_has_prop(thr, js_ctx->idx_loop)) {
            DUK_ERROR_TYPE(thr, DUK_STR_CYCLIC_INPUT);
            DUK_WO_NORETURN(return;);
        }
        duk_push_true(thr);  /* -> [ ... voidp true ] */
        duk_put_prop(thr, js_ctx->idx_loop);  /* -> [ ... ] */
    }

    /* C recursion check. */

    DUK_ASSERT_DISABLE(js_ctx->recursion_depth >= 0);  /* unsigned */
    DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit);
    if (js_ctx->recursion_depth >= js_ctx->recursion_limit) {
        DUK_ERROR_RANGE(thr, DUK_STR_ENC_RECLIMIT);
        DUK_WO_NORETURN(return;);
    }
    js_ctx->recursion_depth++;

    DUK_DDD(DUK_DDDPRINT("shared entry finished: top=%ld, loop=%!T",
                         (long) duk_get_top(thr), (duk_tval *) duk_get_tval(thr, js_ctx->idx_loop)));
}

/* Shared exit handling for object/array serialization. */
DUK_LOCAL void duk__json_enc_objarr_exit(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_top) {
    duk_hthread *thr = js_ctx->thr;
    duk_hobject *h_target;

    /* C recursion check. */

    DUK_ASSERT(js_ctx->recursion_depth > 0);
    DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit);
    js_ctx->recursion_depth--;

    /* Loop check. */

    h_target = duk_known_hobject(thr, *entry_top - 1);  /* original target at entry_top - 1 */

    if (js_ctx->recursion_depth < DUK_JSON_ENC_LOOPARRAY) {
        /* Previous entry was inside visited[], nothing to do. */
    } else {
        duk_push_sprintf(thr, DUK_STR_FMT_PTR, (void *) h_target);
        duk_del_prop(thr, js_ctx->idx_loop);  /* -> [ ... ] */
    }

    /* Restore stack top after unbalanced code paths. */
    duk_set_top(thr, *entry_top);

    DUK_DDD(DUK_DDDPRINT("shared entry finished: top=%ld, loop=%!T",
                         (long) duk_get_top(thr), (duk_tval *) duk_get_tval(thr, js_ctx->idx_loop)));
}

/* The JO(value) operation: encode object.
 *
 * Stack policy: [ object ] -> [ object ].
 */
DUK_LOCAL void duk__json_enc_object(duk_json_enc_ctx *js_ctx) {
    duk_hthread *thr = js_ctx->thr;
    duk_hstring *h_key;
    duk_idx_t entry_top;
    duk_idx_t idx_obj;
    duk_idx_t idx_keys;
    duk_bool_t emitted;
    duk_uarridx_t arr_len, i;
    duk_size_t prev_size;

    DUK_DDD(DUK_DDDPRINT("duk__json_enc_object: obj=%!T", (duk_tval *) duk_get_tval(thr, -1)));

    duk__json_enc_objarr_entry(js_ctx, &entry_top);

    idx_obj = entry_top - 1;

    if (js_ctx->idx_proplist >= 0) {
        idx_keys = js_ctx->idx_proplist;
    } else {
        /* XXX: would be nice to enumerate an object at specified index */
        duk_dup(thr, idx_obj);
        (void) duk_hobject_get_enumerated_keys(thr, DUK_ENUM_OWN_PROPERTIES_ONLY /*flags*/);  /* [ ... target ] -> [ ... target keys ] */
        idx_keys = duk_require_normalize_index(thr, -1);
        /* leave stack unbalanced on purpose */
    }

    DUK_DDD(DUK_DDDPRINT("idx_keys=%ld, h_keys=%!T",
                         (long) idx_keys, (duk_tval *) duk_get_tval(thr, idx_keys)));

    /* Steps 8-10 have been merged to avoid a "partial" variable. */

    DUK__EMIT_1(js_ctx, DUK_ASC_LCURLY);

    /* XXX: keys is an internal object with all keys to be processed
     * in its (gapless) array part.  Because nobody can touch the keys
     * object, we could iterate its array part directly (keeping in mind
     * that it can be reallocated).
     */

    arr_len = (duk_uarridx_t) duk_get_length(thr, idx_keys);
    emitted = 0;
    for (i = 0; i < arr_len; i++) {
        duk_get_prop_index(thr, idx_keys, i);  /* -> [ ... key ] */

        DUK_DDD(DUK_DDDPRINT("object property loop: holder=%!T, key=%!T",
                             (duk_tval *) duk_get_tval(thr, idx_obj),
                             (duk_tval *) duk_get_tval(thr, -1)));

        h_key = duk_known_hstring(thr, -1);
        DUK_ASSERT(h_key != NULL);
        DUK_ASSERT(!DUK_HSTRING_HAS_SYMBOL(h_key));  /* proplist filtering; enum options */

        prev_size = DUK_BW_GET_SIZE(js_ctx->thr, &js_ctx->bw);
        if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) {
            duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth);
            duk__json_enc_key_autoquote(js_ctx, h_key);
            DUK__EMIT_2(js_ctx, DUK_ASC_COLON, DUK_ASC_SPACE);
        } else {
            duk__json_enc_key_autoquote(js_ctx, h_key);
            DUK__EMIT_1(js_ctx, DUK_ASC_COLON);
        }

        /* [ ... key ] */

        if (DUK_UNLIKELY(duk__json_enc_value(js_ctx, idx_obj) == 0)) {
            /* Value would yield 'undefined', so skip key altogether.
             * Side effects have already happened.
             */
            DUK_BW_SET_SIZE(js_ctx->thr, &js_ctx->bw, prev_size);
        } else {
            DUK__EMIT_1(js_ctx, DUK_ASC_COMMA);
            emitted = 1;
        }

        /* [ ... ] */
    }

    if (emitted) {
        DUK_ASSERT(*((duk_uint8_t *) DUK_BW_GET_PTR(js_ctx->thr, &js_ctx->bw) - 1) == DUK_ASC_COMMA);
        DUK__UNEMIT_1(js_ctx);  /* eat trailing comma */
        if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) {
            DUK_ASSERT(js_ctx->recursion_depth >= 1);
            duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1U);
        }
    }
    DUK__EMIT_1(js_ctx, DUK_ASC_RCURLY);

    duk__json_enc_objarr_exit(js_ctx, &entry_top);

    DUK_ASSERT_TOP(thr, entry_top);
}

/* The JA(value) operation: encode array.
 *
 * Stack policy: [ array ] -> [ array ].
 */
DUK_LOCAL void duk__json_enc_array(duk_json_enc_ctx *js_ctx) {
    duk_hthread *thr = js_ctx->thr;
    duk_idx_t entry_top;
    duk_idx_t idx_arr;
    duk_bool_t emitted;
    duk_uarridx_t i, arr_len;

    DUK_DDD(DUK_DDDPRINT("duk__json_enc_array: array=%!T",
                         (duk_tval *) duk_get_tval(thr, -1)));

    duk__json_enc_objarr_entry(js_ctx, &entry_top);

    idx_arr = entry_top - 1;

    /* Steps 8-10 have been merged to avoid a "partial" variable. */

    DUK__EMIT_1(js_ctx, DUK_ASC_LBRACKET);

    arr_len = (duk_uarridx_t) duk_get_length(thr, idx_arr);
    emitted = 0;
    for (i = 0; i < arr_len; i++) {
        DUK_DDD(DUK_DDDPRINT("array entry loop: array=%!T, index=%ld, arr_len=%ld",
                             (duk_tval *) duk_get_tval(thr, idx_arr),
                             (long) i, (long) arr_len));

        if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) {
            DUK_ASSERT(js_ctx->recursion_depth >= 1);
            duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth);
        }

        (void) duk_push_uint_to_hstring(thr, (duk_uint_t) i);  /* -> [ ... key ] */

        /* [ ... key ] */

        if (DUK_UNLIKELY(duk__json_enc_value(js_ctx, idx_arr) == 0)) {
            /* Value would normally be omitted, replace with 'null'. */
            DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL);
        } else {
            ;
        }

        /* [ ... ] */

        DUK__EMIT_1(js_ctx, DUK_ASC_COMMA);
        emitted = 1;
    }

    if (emitted) {
        DUK_ASSERT(*((duk_uint8_t *) DUK_BW_GET_PTR(js_ctx->thr, &js_ctx->bw) - 1) == DUK_ASC_COMMA);
        DUK__UNEMIT_1(js_ctx);  /* eat trailing comma */
        if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) {
            DUK_ASSERT(js_ctx->recursion_depth >= 1);
            duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1U);
        }
    }
    DUK__EMIT_1(js_ctx, DUK_ASC_RBRACKET);

    duk__json_enc_objarr_exit(js_ctx, &entry_top);

    DUK_ASSERT_TOP(thr, entry_top);
}

/* The Str(key, holder) operation.
 *
 * Stack policy: [ ... key ] -> [ ... ]
 */
DUK_LOCAL duk_bool_t duk__json_enc_value(duk_json_enc_ctx *js_ctx, duk_idx_t idx_holder) {
    duk_hthread *thr = js_ctx->thr;
    duk_tval *tv;
    duk_tval *tv_holder;
    duk_tval *tv_key;
    duk_small_int_t c;

    DUK_DDD(DUK_DDDPRINT("duk__json_enc_value: idx_holder=%ld, holder=%!T, key=%!T",
                         (long) idx_holder, (duk_tval *) duk_get_tval(thr, idx_holder),
                         (duk_tval *) duk_get_tval(thr, -1)));

    tv_holder = DUK_GET_TVAL_POSIDX(thr, idx_holder);
    DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv_holder));
    tv_key = DUK_GET_TVAL_NEGIDX(thr, -1);
    DUK_ASSERT(DUK_TVAL_IS_STRING(tv_key));
    DUK_ASSERT(!DUK_HSTRING_HAS_SYMBOL(DUK_TVAL_GET_STRING(tv_key)));  /* Caller responsible. */
    (void) duk_hobject_getprop(thr, tv_holder, tv_key);

    /* -> [ ... key val ] */

    DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(thr, -1)));

    /* Standard JSON checks for .toJSON() only for actual objects; for
     * example, setting Number.prototype.toJSON and then serializing a
     * number won't invoke the .toJSON() method.  However, lightfuncs and
     * plain buffers mimic objects so we check for their .toJSON() method.
     */
    if (duk_check_type_mask(thr, -1, DUK_TYPE_MASK_OBJECT |
                                     DUK_TYPE_MASK_LIGHTFUNC |
                                     DUK_TYPE_MASK_BUFFER)) {
        duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_TO_JSON);
        if (duk_is_callable(thr, -1)) {  /* toJSON() can also be a lightfunc */
            DUK_DDD(DUK_DDDPRINT("value is object, has callable toJSON() -> call it"));
            /* XXX: duk_dup_unvalidated(thr, -2) etc. */
            duk_dup_m2(thr);          /* -> [ ... key val toJSON val ] */
            duk_dup_m4(thr);          /* -> [ ... key val toJSON val key ] */
            duk_call_method(thr, 1);  /* -> [ ... key val val' ] */
            duk_remove_m2(thr);       /* -> [ ... key val' ] */
        } else {
            duk_pop(thr);             /* -> [ ... key val ] */
        }
    }

    /* [ ... key val ] */

    DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(thr, -1)));

    if (js_ctx->h_replacer) {
        /* XXX: Here a "slice copy" would be useful. */
        DUK_DDD(DUK_DDDPRINT("replacer is set, call replacer"));
        duk_push_hobject(thr, js_ctx->h_replacer);  /* -> [ ... key val replacer ] */
        duk_dup(thr, idx_holder);                   /* -> [ ... key val replacer holder ] */
        duk_dup_m4(thr);                            /* -> [ ... key val replacer holder key ] */
        duk_dup_m4(thr);                            /* -> [ ... key val replacer holder key val ] */
        duk_call_method(thr, 2);                    /* -> [ ... key val val' ] */
        duk_remove_m2(thr);                         /* -> [ ... key val' ] */
    }

    /* [ ... key val ] */

    DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(thr, -1)));

    tv = DUK_GET_TVAL_NEGIDX(thr, -1);
    if (DUK_TVAL_IS_OBJECT(tv)) {
        duk_hobject *h;

        h = DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(h != NULL);

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
        if (DUK_HOBJECT_IS_BUFOBJ(h) &&
            js_ctx->flags & (DUK_JSON_FLAG_EXT_CUSTOM | DUK_JSON_FLAG_EXT_COMPATIBLE)) {
            /* With JX/JC a bufferobject gets serialized specially. */
            duk_hbufobj *h_bufobj;
            h_bufobj = (duk_hbufobj *) h;
            DUK_HBUFOBJ_ASSERT_VALID(h_bufobj);
            duk__json_enc_bufobj(js_ctx, h_bufobj);
            goto pop2_emitted;
        }
        /* Otherwise bufferobjects get serialized as normal objects. */
#endif  /* JX || JC */
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
        c = (duk_small_int_t) DUK_HOBJECT_GET_CLASS_NUMBER(h);
        switch (c) {
        case DUK_HOBJECT_CLASS_NUMBER: {
            DUK_DDD(DUK_DDDPRINT("value is a Number object -> coerce with ToNumber()"));
            duk_to_number_m1(thr);
            /* The coercion potentially invokes user .valueOf() and .toString()
             * but can't result in a function value because ToPrimitive() would
             * reject such a result: test-dev-json-stringify-coercion-1.js.
             */
            DUK_ASSERT(!duk_is_callable(thr, -1));
            break;
        }
        case DUK_HOBJECT_CLASS_STRING: {
            DUK_DDD(DUK_DDDPRINT("value is a String object -> coerce with ToString()"));
            duk_to_string(thr, -1);
            /* Same coercion behavior as for Number. */
            DUK_ASSERT(!duk_is_callable(thr, -1));
            break;
        }
#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
        case DUK_HOBJECT_CLASS_POINTER:
#endif
        case DUK_HOBJECT_CLASS_BOOLEAN: {
            DUK_DDD(DUK_DDDPRINT("value is a Boolean/Buffer/Pointer object -> get internal value"));
            duk_xget_owndataprop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE);
            duk_remove_m2(thr);
            break;
        }
        default: {
            /* Normal object which doesn't get automatically coerced to a
             * primitive value.  Functions are checked for specially.  The
             * primitive value coercions for Number, String, Pointer, and
             * Boolean can't result in functions so suffices to check here.
             * Symbol objects are handled like plain objects (their primitive
             * value is NOT looked up like for e.g. String objects).
             */
            DUK_ASSERT(h != NULL);
            if (DUK_HOBJECT_IS_CALLABLE(h)) {
#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
                if (js_ctx->flags & (DUK_JSON_FLAG_EXT_CUSTOM |
                                     DUK_JSON_FLAG_EXT_COMPATIBLE)) {
                    /* We only get here when doing non-standard JSON encoding */
                    DUK_DDD(DUK_DDDPRINT("-> function allowed, serialize to custom format"));
                    DUK_ASSERT(js_ctx->flag_ext_custom || js_ctx->flag_ext_compatible);
                    DUK__EMIT_STRIDX(js_ctx, js_ctx->stridx_custom_function);
                    goto pop2_emitted;
                } else {
                    DUK_DDD(DUK_DDDPRINT("-> will result in undefined (function)"));
                    goto pop2_undef;
                }
#else  /* DUK_USE_JX || DUK_USE_JC */
                DUK_DDD(DUK_DDDPRINT("-> will result in undefined (function)"));
                goto pop2_undef;
#endif  /* DUK_USE_JX || DUK_USE_JC */
            }
        }
        }  /* end switch */
    }

    /* [ ... key val ] */

    DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(thr, -1)));

    if (duk_check_type_mask(thr, -1, js_ctx->mask_for_undefined)) {
        /* will result in undefined */
        DUK_DDD(DUK_DDDPRINT("-> will result in undefined (type mask check)"));
        goto pop2_undef;
    }
    tv = DUK_GET_TVAL_NEGIDX(thr, -1);

    switch (DUK_TVAL_GET_TAG(tv)) {
#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
    /* When JX/JC not in use, the type mask above will avoid this case if needed. */
    case DUK_TAG_UNDEFINED: {
        DUK__EMIT_STRIDX(js_ctx, js_ctx->stridx_custom_undefined);
        break;
    }
#endif
    case DUK_TAG_NULL: {
        DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL);
        break;
    }
    case DUK_TAG_BOOLEAN: {
        DUK__EMIT_STRIDX(js_ctx, DUK_TVAL_GET_BOOLEAN(tv) ?
                         DUK_STRIDX_TRUE : DUK_STRIDX_FALSE);
        break;
    }
#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
    /* When JX/JC not in use, the type mask above will avoid this case if needed. */
    case DUK_TAG_POINTER: {
        duk__json_enc_pointer(js_ctx, DUK_TVAL_GET_POINTER(tv));
        break;
    }
#endif  /* DUK_USE_JX || DUK_USE_JC */
    case DUK_TAG_STRING: {
        duk_hstring *h = DUK_TVAL_GET_STRING(tv);
        DUK_ASSERT(h != NULL);
        if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) {
            goto pop2_undef;
        }
        duk__json_enc_quote_string(js_ctx, h);
        break;
    }
    case DUK_TAG_OBJECT: {
        duk_hobject *h = DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(h != NULL);

        /* Function values are handled completely above (including
         * coercion results):
         */
        DUK_ASSERT(!DUK_HOBJECT_IS_CALLABLE(h));

        if (duk_js_isarray_hobject(h)) {
            duk__json_enc_array(js_ctx);
        } else {
            duk__json_enc_object(js_ctx);
        }
        break;
    }
    /* Because plain buffers mimics Uint8Array, they have enumerable
     * index properties [0,byteLength[.  Because JSON only serializes
     * enumerable own properties, no properties can be serialized for
     * plain buffers (all virtual properties are non-enumerable).  However,
     * there may be a .toJSON() method which was already handled above.
     */
    case DUK_TAG_BUFFER: {
#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
        if (js_ctx->flag_ext_custom_or_compatible) {
            duk__json_enc_buffer_jx_jc(js_ctx, DUK_TVAL_GET_BUFFER(tv));
            break;
        }
#endif

        /* Could implement a fastpath, but the fast path would need
         * to handle realloc side effects correctly.
         */
        duk_to_object(thr, -1);
        duk__json_enc_object(js_ctx);
        break;
    }
    case DUK_TAG_LIGHTFUNC: {
#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
        /* We only get here when doing non-standard JSON encoding */
        DUK_ASSERT(js_ctx->flag_ext_custom || js_ctx->flag_ext_compatible);
        DUK__EMIT_STRIDX(js_ctx, js_ctx->stridx_custom_function);
#else
        /* Standard JSON omits functions */
        DUK_UNREACHABLE();
#endif
        break;
    }
#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__json_enc_fastint_tval(js_ctx, tv);
        break;
#endif
    default: {
        /* number */
        DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
        DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
        /* XXX: A fast path for usual integers would be useful when
         * fastint support is not enabled.
         */
        duk__json_enc_double(js_ctx);
        break;
    }
    }

#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
 pop2_emitted:
#endif
    duk_pop_2(thr); /* [ ... key val ] -> [ ... ] */
    return 1;  /* emitted */

 pop2_undef:
    duk_pop_2(thr);  /* [ ... key val ] -> [ ... ] */
    return 0;  /* not emitted */
}

/* E5 Section 15.12.3, main algorithm, step 4.b.ii steps 1-4. */
DUK_LOCAL duk_bool_t duk__json_enc_allow_into_proplist(duk_tval *tv) {
    duk_small_int_t c;

    /* XXX: some kind of external internal type checker?
     * - type mask; symbol flag; class mask
     */
    DUK_ASSERT(tv != NULL);
    if (DUK_TVAL_IS_STRING(tv)) {
        duk_hstring *h;
        h = DUK_TVAL_GET_STRING(tv);
        DUK_ASSERT(h != NULL);
        if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) {
            return 0;
        }
        return 1;
    } else if (DUK_TVAL_IS_NUMBER(tv)) {
        return 1;
    } else if (DUK_TVAL_IS_OBJECT(tv)) {
        duk_hobject *h;
        h = DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(h != NULL);
        c = (duk_small_int_t) DUK_HOBJECT_GET_CLASS_NUMBER(h);
        if (c == DUK_HOBJECT_CLASS_STRING || c == DUK_HOBJECT_CLASS_NUMBER) {
            return 1;
        }
    }

    return 0;
}

/*
 *  JSON.stringify() fast path
 *
 *  Otherwise supports full JSON, JX, and JC features, but bails out on any
 *  possible side effect which might change the value being serialized.  The
 *  fast path can take advantage of the fact that the value being serialized
 *  is unchanged so that we can walk directly through property tables etc.
 */

#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH)
DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, duk_tval *tv) {
    duk_uint_fast32_t i, n;

    DUK_DDD(DUK_DDDPRINT("stringify fast: %!T", tv));

    DUK_ASSERT(js_ctx != NULL);
    DUK_ASSERT(js_ctx->thr != NULL);

#if 0 /* disabled for now */
 restart_match:
#endif

    DUK_ASSERT(tv != NULL);

    switch (DUK_TVAL_GET_TAG(tv)) {
    case DUK_TAG_UNDEFINED: {
#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
        if (js_ctx->flag_ext_custom || js_ctx->flag_ext_compatible) {
            DUK__EMIT_STRIDX(js_ctx, js_ctx->stridx_custom_undefined);
            break;
        } else {
            goto emit_undefined;
        }
#else
        goto emit_undefined;
#endif
    }
    case DUK_TAG_NULL: {
        DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL);
        break;
    }
    case DUK_TAG_BOOLEAN: {
        DUK__EMIT_STRIDX(js_ctx, DUK_TVAL_GET_BOOLEAN(tv) ?
                         DUK_STRIDX_TRUE : DUK_STRIDX_FALSE);
        break;
    }
    case DUK_TAG_STRING: {
        duk_hstring *h;
        h = DUK_TVAL_GET_STRING(tv);
        DUK_ASSERT(h != NULL);
        if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) {
            goto emit_undefined;
        }
        duk__json_enc_quote_string(js_ctx, h);
        break;
    }
    case DUK_TAG_OBJECT: {
        duk_hobject *obj;
        duk_tval *tv_val;
        duk_bool_t emitted = 0;
        duk_uint32_t c_bit, c_all, c_array, c_unbox, c_undef,
                     c_func, c_bufobj, c_object, c_abort;

        /* For objects JSON.stringify() only looks for own, enumerable
         * properties which is nice for the fast path here.
         *
         * For arrays JSON.stringify() uses [[Get]] so it will actually
         * inherit properties during serialization!  This fast path
         * supports gappy arrays as long as there's no actual inherited
         * property (which might be a getter etc).
         *
         * Since recursion only happens for objects, we can have both
         * recursion and loop checks here.  We use a simple, depth-limited
         * loop check in the fast path because the object-based tracking
         * is very slow (when tested, it accounted for 50% of fast path
         * execution time for input data with a lot of small objects!).
         */

        /* XXX: for real world code, could just ignore array inheritance
         * and only look at array own properties.
         */

        /* We rely on a few object flag / class number relationships here,
         * assert for them.
         */

        obj = DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(obj != NULL);
        DUK_HOBJECT_ASSERT_VALID(obj);

        /* Once recursion depth is increased, exit path must decrease
         * it (though it's OK to abort the fast path).
         */

        DUK_ASSERT_DISABLE(js_ctx->recursion_depth >= 0);  /* unsigned */
        DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit);
        if (js_ctx->recursion_depth >= js_ctx->recursion_limit) {
            DUK_DD(DUK_DDPRINT("fast path recursion limit"));
            DUK_ERROR_RANGE(js_ctx->thr, DUK_STR_DEC_RECLIMIT);
            DUK_WO_NORETURN(return 0;);
        }

        for (i = 0, n = (duk_uint_fast32_t) js_ctx->recursion_depth; i < n; i++) {
            if (DUK_UNLIKELY(js_ctx->visiting[i] == obj)) {
                DUK_DD(DUK_DDPRINT("fast path loop detect"));
                DUK_ERROR_TYPE(js_ctx->thr, DUK_STR_CYCLIC_INPUT);
                DUK_WO_NORETURN(return 0;);
            }
        }

        /* Guaranteed by recursion_limit setup so we don't have to
         * check twice.
         */
        DUK_ASSERT(js_ctx->recursion_depth < DUK_JSON_ENC_LOOPARRAY);
        js_ctx->visiting[js_ctx->recursion_depth] = obj;
        js_ctx->recursion_depth++;

        /* If object has a .toJSON() property, we can't be certain
         * that it wouldn't mutate any value arbitrarily, so bail
         * out of the fast path.
         *
         * If an object is a Proxy we also can't avoid side effects
         * so abandon.
         */
        /* XXX: non-callable .toJSON() doesn't need to cause an abort
         * but does at the moment, probably not worth fixing.
         */
        if (duk_hobject_hasprop_raw(js_ctx->thr, obj, DUK_HTHREAD_STRING_TO_JSON(js_ctx->thr)) ||
            DUK_HOBJECT_IS_PROXY(obj)) {
            DUK_DD(DUK_DDPRINT("object has a .toJSON property or object is a Proxy, abort fast path"));
            goto abort_fastpath;
        }

        /* We could use a switch-case for the class number but it turns out
         * a small if-else ladder on class masks is better.  The if-ladder
         * should be in order of relevancy.
         */

        /* XXX: move masks to js_ctx? they don't change during one
         * fast path invocation.
         */
        DUK_ASSERT(DUK_HOBJECT_CLASS_MAX <= 31);
#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
        if (js_ctx->flag_ext_custom_or_compatible) {
            c_all = DUK_HOBJECT_CMASK_ALL;
            c_array = DUK_HOBJECT_CMASK_ARRAY;
            c_unbox = DUK_HOBJECT_CMASK_NUMBER |
                      DUK_HOBJECT_CMASK_STRING |
                      DUK_HOBJECT_CMASK_BOOLEAN |
                      DUK_HOBJECT_CMASK_POINTER;  /* Symbols are not unboxed. */
            c_func = DUK_HOBJECT_CMASK_FUNCTION;
            c_bufobj = DUK_HOBJECT_CMASK_ALL_BUFOBJS;
            c_undef = 0;
            c_abort = 0;
            c_object = c_all & ~(c_array | c_unbox | c_func | c_bufobj | c_undef | c_abort);
        }
        else
#endif
        {
            c_all = DUK_HOBJECT_CMASK_ALL;
            c_array = DUK_HOBJECT_CMASK_ARRAY;
            c_unbox = DUK_HOBJECT_CMASK_NUMBER |
                      DUK_HOBJECT_CMASK_STRING |
                      DUK_HOBJECT_CMASK_BOOLEAN;  /* Symbols are not unboxed. */
            c_func = 0;
            c_bufobj = 0;
            c_undef = DUK_HOBJECT_CMASK_FUNCTION |
                      DUK_HOBJECT_CMASK_POINTER;
            /* As the fast path doesn't currently properly support
             * duk_hbufobj virtual properties, abort fast path if
             * we encounter them in plain JSON mode.
             */
            c_abort = DUK_HOBJECT_CMASK_ALL_BUFOBJS;
            c_object = c_all & ~(c_array | c_unbox | c_func | c_bufobj | c_undef | c_abort);
        }

        c_bit = (duk_uint32_t) DUK_HOBJECT_GET_CLASS_MASK(obj);
        if (c_bit & c_object) {
            /* All other object types. */
            DUK__EMIT_1(js_ctx, DUK_ASC_LCURLY);

            /* A non-Array object should not have an array part in practice.
             * But since it is supported internally (and perhaps used at some
             * point), check and abandon if that's the case.
             */
            if (DUK_HOBJECT_HAS_ARRAY_PART(obj)) {
                DUK_DD(DUK_DDPRINT("non-Array object has array part, abort fast path"));
                goto abort_fastpath;
            }

            for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(obj); i++) {
                duk_hstring *k;
                duk_size_t prev_size;

                k = DUK_HOBJECT_E_GET_KEY(js_ctx->thr->heap, obj, i);
                if (!k) {
                    continue;
                }
                if (DUK_HSTRING_HAS_ARRIDX(k)) {
                    /* If an object has array index keys we would need
                     * to sort them into the ES2015 enumeration order to
                     * be consistent with the slow path.  Abort the fast
                     * path and handle in the slow path for now.
                     */
                    DUK_DD(DUK_DDPRINT("property key is an array index, abort fast path"));
                    goto abort_fastpath;
                }
                if (!DUK_HOBJECT_E_SLOT_IS_ENUMERABLE(js_ctx->thr->heap, obj, i)) {
                    continue;
                }
                if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(js_ctx->thr->heap, obj, i)) {
                    /* Getter might have arbitrary side effects,
                     * so bail out.
                     */
                    DUK_DD(DUK_DDPRINT("property is an accessor, abort fast path"));
                    goto abort_fastpath;
                }
                if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(k))) {
                    continue;
                }

                tv_val = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(js_ctx->thr->heap, obj, i);

                prev_size = DUK_BW_GET_SIZE(js_ctx->thr, &js_ctx->bw);
                if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) {
                    duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth);
                    duk__json_enc_key_autoquote(js_ctx, k);
                    DUK__EMIT_2(js_ctx, DUK_ASC_COLON, DUK_ASC_SPACE);
                } else {
                    duk__json_enc_key_autoquote(js_ctx, k);
                    DUK__EMIT_1(js_ctx, DUK_ASC_COLON);
                }

                if (duk__json_stringify_fast_value(js_ctx, tv_val) == 0) {
                    DUK_DD(DUK_DDPRINT("prop value not supported, rewind key and colon"));
                    DUK_BW_SET_SIZE(js_ctx->thr, &js_ctx->bw, prev_size);
                } else {
                    DUK__EMIT_1(js_ctx, DUK_ASC_COMMA);
                    emitted = 1;
                }
            }

            /* If any non-Array value had enumerable virtual own
             * properties, they should be serialized here (actually,
             * before the explicit properties).  Standard types don't.
             */

            if (emitted) {
                DUK_ASSERT(*((duk_uint8_t *) DUK_BW_GET_PTR(js_ctx->thr, &js_ctx->bw) - 1) == DUK_ASC_COMMA);
                DUK__UNEMIT_1(js_ctx);  /* eat trailing comma */
                if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) {
                    DUK_ASSERT(js_ctx->recursion_depth >= 1);
                    duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1U);
                }
            }
            DUK__EMIT_1(js_ctx, DUK_ASC_RCURLY);
        } else if (c_bit & c_array) {
            duk_uint_fast32_t arr_len;
            duk_uint_fast32_t asize;

            DUK__EMIT_1(js_ctx, DUK_ASC_LBRACKET);

            /* Assume arrays are dense in the fast path. */
            if (!DUK_HOBJECT_HAS_ARRAY_PART(obj)) {
                DUK_DD(DUK_DDPRINT("Array object is sparse, abort fast path"));
                goto abort_fastpath;
            }

            arr_len = (duk_uint_fast32_t) ((duk_harray *) obj)->length;
            asize = (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(obj);
            /* Array part may be larger than 'length'; if so, iterate
             * only up to array 'length'.  Array part may also be smaller
             * than 'length' in some cases.
             */
            for (i = 0; i < arr_len; i++) {
                duk_tval *tv_arrval;
                duk_hstring *h_tmp;
                duk_bool_t has_inherited;

                if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) {
                    duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth);
                }

                if (DUK_LIKELY(i < asize)) {
                    tv_arrval = DUK_HOBJECT_A_GET_VALUE_PTR(js_ctx->thr->heap, obj, i);
                    if (DUK_LIKELY(!DUK_TVAL_IS_UNUSED(tv_arrval))) {
                        /* Expected case: element is present. */
                        if (duk__json_stringify_fast_value(js_ctx, tv_arrval) == 0) {
                            DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL);
                        }
                        goto elem_done;
                    }
                }

                /* Gap in array; check for inherited property,
                 * bail out if one exists.  This should be enough
                 * to support gappy arrays for all practical code.
                 */

                h_tmp = duk_push_uint_to_hstring(js_ctx->thr, (duk_uint_t) i);
                has_inherited = duk_hobject_hasprop_raw(js_ctx->thr, obj, h_tmp);
                duk_pop(js_ctx->thr);
                if (has_inherited) {
                    DUK_D(DUK_DPRINT("gap in array, conflicting inherited property, abort fast path"));
                    goto abort_fastpath;
                }

                /* Ordinary gap, undefined encodes to 'null' in
                 * standard JSON, but JX/JC use their form for
                 * undefined to better preserve the typing.
                 */
                DUK_D(DUK_DPRINT("gap in array, no conflicting inherited property, remain on fast path"));
#if defined(DUK_USE_JX)
                DUK__EMIT_STRIDX(js_ctx, js_ctx->stridx_custom_undefined);
#else
                DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL);
#endif
                /* fall through */

             elem_done:
                DUK__EMIT_1(js_ctx, DUK_ASC_COMMA);
                emitted = 1;
            }

            if (emitted) {
                DUK_ASSERT(*((duk_uint8_t *) DUK_BW_GET_PTR(js_ctx->thr, &js_ctx->bw) - 1) == DUK_ASC_COMMA);
                DUK__UNEMIT_1(js_ctx);  /* eat trailing comma */
                if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) {
                    DUK_ASSERT(js_ctx->recursion_depth >= 1);
                    duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1U);
                }
            }
            DUK__EMIT_1(js_ctx, DUK_ASC_RBRACKET);
        } else if (c_bit & c_unbox) {
            /* Certain boxed types are required to go through
             * automatic unboxing.  Rely on internal value being
             * sane (to avoid infinite recursion).
             */
            DUK_ASSERT((c_bit & DUK_HOBJECT_CMASK_SYMBOL) == 0);  /* Symbols are not unboxed. */

#if 1
            /* The code below is incorrect if .toString() or .valueOf() have
             * have been overridden.  The correct approach would be to look up
             * the method(s) and if they resolve to the built-in function we
             * can safely bypass it and look up the internal value directly.
             * Unimplemented for now, abort fast path for boxed values.
             */
            goto abort_fastpath;
#else  /* disabled */
            /* Disabled until fixed, see above. */
            duk_tval *tv_internal;

            DUK_DD(DUK_DDPRINT("auto unboxing in fast path"));

            tv_internal = duk_hobject_get_internal_value_tval_ptr(js_ctx->thr->heap, obj);
            DUK_ASSERT(tv_internal != NULL);
            DUK_ASSERT(DUK_TVAL_IS_STRING(tv_internal) ||
                       DUK_TVAL_IS_NUMBER(tv_internal) ||
                       DUK_TVAL_IS_BOOLEAN(tv_internal) ||
                       DUK_TVAL_IS_POINTER(tv_internal));

            tv = tv_internal;
            DUK_ASSERT(js_ctx->recursion_depth > 0);
            js_ctx->recursion_depth--;  /* required to keep recursion depth correct */
            goto restart_match;
#endif  /* disabled */
#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
        } else if (c_bit & c_func) {
            DUK__EMIT_STRIDX(js_ctx, js_ctx->stridx_custom_function);
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
        } else if (c_bit & c_bufobj) {
            duk__json_enc_bufobj(js_ctx, (duk_hbufobj *) obj);
#endif
#endif
        } else if (c_bit & c_abort) {
            DUK_DD(DUK_DDPRINT("abort fast path for unsupported type"));
            goto abort_fastpath;
        } else {
            DUK_ASSERT((c_bit & c_undef) != 0);

            /* Must decrease recursion depth before returning. */
            DUK_ASSERT(js_ctx->recursion_depth > 0);
            DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit);
            js_ctx->recursion_depth--;
            goto emit_undefined;
        }

        DUK_ASSERT(js_ctx->recursion_depth > 0);
        DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit);
        js_ctx->recursion_depth--;
        break;
    }
    case DUK_TAG_BUFFER: {
        /* Plain buffers are treated like Uint8Arrays: they have
         * enumerable indices.  Other virtual properties are not
         * enumerable, and inherited properties are not serialized.
         * However, there can be a replacer (not relevant here) or
         * a .toJSON() method (which we need to check for explicitly).
         */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
        if (duk_hobject_hasprop_raw(js_ctx->thr,
                                    js_ctx->thr->builtins[DUK_BIDX_UINT8ARRAY_PROTOTYPE],
                                    DUK_HTHREAD_STRING_TO_JSON(js_ctx->thr))) {
            DUK_DD(DUK_DDPRINT("value is a plain buffer and there's an inherited .toJSON, abort fast path"));
            goto abort_fastpath;
        }
#endif

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

        /* Plain buffers mimic Uint8Arrays, and have enumerable index
         * properties.
         */
        duk__json_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__json_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__json_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__json_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

    /* negative top-relative indices not allowed now */
    DUK_ASSERT(idx_value == DUK_INVALID_INDEX || idx_value >= 0);
    DUK_ASSERT(idx_reviver == DUK_INVALID_INDEX || idx_reviver >= 0);

    DUK_DDD(DUK_DDDPRINT("JSON parse start: text=%!T, reviver=%!T, flags=0x%08lx, stack_top=%ld",
                         (duk_tval *) duk_get_tval(thr, idx_value),
                         (duk_tval *) duk_get_tval(thr, idx_reviver),
                         (unsigned long) flags,
                         (long) duk_get_top(thr)));

    duk_memzero(&js_ctx_alloc, sizeof(js_ctx_alloc));
    js_ctx->thr = thr;
#if defined(DUK_USE_EXPLICIT_NULL_INIT)
    /* nothing now */
#endif
    js_ctx->recursion_limit = DUK_USE_JSON_DEC_RECLIMIT;
    DUK_ASSERT(js_ctx->recursion_depth == 0);

    /* Flag handling currently assumes that flags are consistent.  This is OK
     * because the call sites are now strictly controlled.
     */

    js_ctx->flags = flags;
#if defined(DUK_USE_JX)
    js_ctx->flag_ext_custom = flags & DUK_JSON_FLAG_EXT_CUSTOM;
#endif
#if defined(DUK_USE_JC)
    js_ctx->flag_ext_compatible = flags & DUK_JSON_FLAG_EXT_COMPATIBLE;
#endif
#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
    js_ctx->flag_ext_custom_or_compatible = flags & (DUK_JSON_FLAG_EXT_CUSTOM | DUK_JSON_FLAG_EXT_COMPATIBLE);
#endif

    h_text = duk_to_hstring(thr, idx_value);  /* coerce in-place; rejects Symbols */
    DUK_ASSERT(h_text != NULL);

    /* JSON parsing code is allowed to read [p_start,p_end]: p_end is
     * valid and points to the string NUL terminator (which is always
     * guaranteed for duk_hstrings.
     */
    js_ctx->p_start = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_text);
    js_ctx->p = js_ctx->p_start;
    js_ctx->p_end = ((const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_text)) +
                    DUK_HSTRING_GET_BYTELEN(h_text);
    DUK_ASSERT(*(js_ctx->p_end) == 0x00);

    duk__json_dec_value(js_ctx);  /* -> [ ... value ] */
    DUK_ASSERT(js_ctx->recursion_depth == 0);

    /* Trailing whitespace has been eaten by duk__json_dec_value(), so if
     * we're not at end of input here, it's a SyntaxError.
     */

    if (js_ctx->p != js_ctx->p_end) {
        duk__json_dec_syntax_error(js_ctx);
    }

    if (duk_is_callable(thr, idx_reviver)) {
        DUK_DDD(DUK_DDDPRINT("applying reviver: %!T",
                             (duk_tval *) duk_get_tval(thr, idx_reviver)));

        js_ctx->idx_reviver = idx_reviver;

        duk_push_object(thr);
        duk_dup_m2(thr);  /* -> [ ... val root val ] */
        duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_EMPTY_STRING);  /* default attrs ok */
        duk_push_hstring_stridx(thr, DUK_STRIDX_EMPTY_STRING);  /* -> [ ... val root "" ] */

        DUK_DDD(DUK_DDDPRINT("start reviver walk, root=%!T, name=%!T",
                             (duk_tval *) duk_get_tval(thr, -2),
                             (duk_tval *) duk_get_tval(thr, -1)));

        DUK_ASSERT(js_ctx->recursion_depth == 0);
        duk__json_dec_reviver_walk(js_ctx);  /* [ ... val root "" ] -> [ ... val val' ] */
        DUK_ASSERT(js_ctx->recursion_depth == 0);
        duk_remove_m2(thr);             /* -> [ ... val' ] */
    } else {
        DUK_DDD(DUK_DDDPRINT("reviver does not exist or is not callable: %!T",
                             (duk_tval *) duk_get_tval(thr, idx_reviver)));
    }

    /* Final result is at stack top. */

    DUK_DDD(DUK_DDDPRINT("JSON parse end: text=%!T, reviver=%!T, flags=0x%08lx, result=%!T, stack_top=%ld",
                         (duk_tval *) duk_get_tval(thr, idx_value),
                         (duk_tval *) duk_get_tval(thr, idx_reviver),
                         (unsigned long) flags,
                         (duk_tval *) duk_get_tval(thr, -1),
                         (long) duk_get_top(thr)));

    DUK_ASSERT(duk_get_top(thr) == entry_top + 1);
}

DUK_INTERNAL
void duk_bi_json_stringify_helper(duk_hthread *thr,
                                  duk_idx_t idx_value,
                                  duk_idx_t idx_replacer,
                                  duk_idx_t idx_space,
                                  duk_small_uint_t flags) {
    duk_json_enc_ctx js_ctx_alloc;
    duk_json_enc_ctx *js_ctx = &js_ctx_alloc;
    duk_hobject *h;
    duk_idx_t idx_holder;
    duk_idx_t entry_top;

    /* negative top-relative indices not allowed now */
    DUK_ASSERT(idx_value == DUK_INVALID_INDEX || idx_value >= 0);
    DUK_ASSERT(idx_replacer == DUK_INVALID_INDEX || idx_replacer >= 0);
    DUK_ASSERT(idx_space == DUK_INVALID_INDEX || idx_space >= 0);

    DUK_DDD(DUK_DDDPRINT("JSON stringify start: value=%!T, replacer=%!T, space=%!T, flags=0x%08lx, stack_top=%ld",
                         (duk_tval *) duk_get_tval(thr, idx_value),
                         (duk_tval *) duk_get_tval(thr, idx_replacer),
                         (duk_tval *) duk_get_tval(thr, idx_space),
                         (unsigned long) flags,
                         (long) duk_get_top(thr)));

    entry_top = duk_get_top(thr);

    /*
     *  Context init
     */

    duk_memzero(&js_ctx_alloc, sizeof(js_ctx_alloc));
    js_ctx->thr = thr;
#if defined(DUK_USE_EXPLICIT_NULL_INIT)
    js_ctx->h_replacer = NULL;
    js_ctx->h_gap = NULL;
#endif
    js_ctx->idx_proplist = -1;

    /* Flag handling currently assumes that flags are consistent.  This is OK
     * because the call sites are now strictly controlled.
     */

    js_ctx->flags = flags;
    js_ctx->flag_ascii_only = flags & DUK_JSON_FLAG_ASCII_ONLY;
    js_ctx->flag_avoid_key_quotes = flags & DUK_JSON_FLAG_AVOID_KEY_QUOTES;
#if defined(DUK_USE_JX)
    js_ctx->flag_ext_custom = flags & DUK_JSON_FLAG_EXT_CUSTOM;
#endif
#if defined(DUK_USE_JC)
    js_ctx->flag_ext_compatible = flags & DUK_JSON_FLAG_EXT_COMPATIBLE;
#endif
#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
    js_ctx->flag_ext_custom_or_compatible = flags & (DUK_JSON_FLAG_EXT_CUSTOM | DUK_JSON_FLAG_EXT_COMPATIBLE);
#endif

    /* The #if defined() clutter here handles the JX/JC enable/disable
     * combinations properly.
     */
#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
    js_ctx->stridx_custom_undefined = DUK_STRIDX_LC_NULL;  /* standard JSON; array gaps */
#if defined(DUK_USE_JX)
    if (flags & DUK_JSON_FLAG_EXT_CUSTOM) {
        js_ctx->stridx_custom_undefined = DUK_STRIDX_LC_UNDEFINED;
        js_ctx->stridx_custom_nan = DUK_STRIDX_NAN;
        js_ctx->stridx_custom_neginf = DUK_STRIDX_MINUS_INFINITY;
        js_ctx->stridx_custom_posinf = DUK_STRIDX_INFINITY;
        js_ctx->stridx_custom_function =
                (flags & DUK_JSON_FLAG_AVOID_KEY_QUOTES) ?
                        DUK_STRIDX_JSON_EXT_FUNCTION2 :
                        DUK_STRIDX_JSON_EXT_FUNCTION1;
    }
#endif  /* DUK_USE_JX */
#if defined(DUK_USE_JX) && defined(DUK_USE_JC)
    else
#endif  /* DUK_USE_JX && DUK_USE_JC */
#if defined(DUK_USE_JC)
    if (js_ctx->flags & DUK_JSON_FLAG_EXT_COMPATIBLE) {
        js_ctx->stridx_custom_undefined = DUK_STRIDX_JSON_EXT_UNDEFINED;
        js_ctx->stridx_custom_nan = DUK_STRIDX_JSON_EXT_NAN;
        js_ctx->stridx_custom_neginf = DUK_STRIDX_JSON_EXT_NEGINF;
        js_ctx->stridx_custom_posinf = DUK_STRIDX_JSON_EXT_POSINF;
        js_ctx->stridx_custom_function = DUK_STRIDX_JSON_EXT_FUNCTION1;
    }
#endif  /* DUK_USE_JC */
#endif  /* DUK_USE_JX || DUK_USE_JC */

#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
    if (js_ctx->flags & (DUK_JSON_FLAG_EXT_CUSTOM |
                         DUK_JSON_FLAG_EXT_COMPATIBLE)) {
        DUK_ASSERT(js_ctx->mask_for_undefined == 0);  /* already zero */
    }
    else
#endif  /* DUK_USE_JX || DUK_USE_JC */
    {
        /* Plain buffer is treated like ArrayBuffer and serialized.
         * Lightfuncs are treated like objects, but JSON explicitly
         * skips serializing Function objects so we can just reject
         * lightfuncs here.
         */
        js_ctx->mask_for_undefined = DUK_TYPE_MASK_UNDEFINED |
                                     DUK_TYPE_MASK_POINTER |
                                     DUK_TYPE_MASK_LIGHTFUNC;
    }

    DUK_BW_INIT_PUSHBUF(thr, &js_ctx->bw, DUK__JSON_STRINGIFY_BUFSIZE);

    js_ctx->idx_loop = duk_push_bare_object(thr);
    DUK_ASSERT(js_ctx->idx_loop >= 0);

    /* [ ... buf loop ] */

    /*
     *  Process replacer/proplist (2nd argument to JSON.stringify)
     */

    h = duk_get_hobject(thr, idx_replacer);
    if (h != NULL) {
        if (DUK_HOBJECT_IS_CALLABLE(h)) {
            js_ctx->h_replacer = h;
        } else if (duk_js_isarray_hobject(h)) {
            /* Here the specification requires correct array index enumeration
             * which is a bit tricky for sparse arrays (it is handled by the
             * enum setup code).  We now enumerate ancestors too, although the
             * 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_bare_array(thr);

            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__json_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. */
        thr->heap->pf_prevent_count++;                 /* Prevent finalizers. */
        DUK_ASSERT(thr->heap->pf_prevent_count != 0);  /* Wrap. */

        pcall_rc = duk_safe_call(thr, duk__json_stringify_fast, (void *) js_ctx /*udata*/, 1 /*nargs*/, 0 /*nret*/);

        DUK_ASSERT(thr->heap->pf_prevent_count > 0);
        thr->heap->pf_prevent_count--;
        thr->heap->ms_base_flags = prev_ms_base_flags;

        if (pcall_rc == DUK_EXEC_SUCCESS) {
            DUK_DD(DUK_DDPRINT("fast path successful"));
            DUK_BW_PUSH_AS_STRING(thr, &js_ctx->bw);
            goto replace_finished;
        }

        /* We come here for actual aborts (like encountering .toJSON())
         * but also for recursion/loop errors.  Bufwriter size can be
         * kept because we'll probably need at least as much as we've
         * allocated so far.
         */
        DUK_D(DUK_DPRINT("fast path failed, serialize using slow path instead"));
        DUK_BW_RESET_SIZE(thr, &js_ctx->bw);
        js_ctx->recursion_depth = 0;
    }
#endif

    /*
     *  Create wrapper object and serialize
     */

    idx_holder = duk_push_object(thr);
    duk_dup(thr, idx_value);
    duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_EMPTY_STRING);

    DUK_DDD(DUK_DDDPRINT("before: flags=0x%08lx, loop=%!T, replacer=%!O, "
                         "proplist=%!T, gap=%!O, holder=%!T",
                         (unsigned long) js_ctx->flags,
                         (duk_tval *) duk_get_tval(thr, js_ctx->idx_loop),
                         (duk_heaphdr *) js_ctx->h_replacer,
                         (duk_tval *) (js_ctx->idx_proplist >= 0 ? duk_get_tval(thr, js_ctx->idx_proplist) : NULL),
                         (duk_heaphdr *) js_ctx->h_gap,
                         (duk_tval *) duk_get_tval(thr, -1)));

    /* serialize the wrapper with empty string key */

    duk_push_hstring_empty(thr);

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

    js_ctx->recursion_limit = DUK_USE_JSON_ENC_RECLIMIT;
    DUK_ASSERT(js_ctx->recursion_depth == 0);

    if (DUK_UNLIKELY(duk__json_enc_value(js_ctx, idx_holder) == 0)) {  /* [ ... holder key ] -> [ ... holder ] */
        /* Result is undefined. */
        duk_push_undefined(thr);
    } else {
        /* Convert buffer to result string. */
        DUK_BW_PUSH_AS_STRING(thr, &js_ctx->bw);
    }

    DUK_DDD(DUK_DDDPRINT("after: flags=0x%08lx, loop=%!T, replacer=%!O, "
                         "proplist=%!T, gap=%!O, holder=%!T",
                         (unsigned long) js_ctx->flags,
                         (duk_tval *) duk_get_tval(thr, js_ctx->idx_loop),
                         (duk_heaphdr *) js_ctx->h_replacer,
                         (duk_tval *) (js_ctx->idx_proplist >= 0 ? duk_get_tval(thr, js_ctx->idx_proplist) : NULL),
                         (duk_heaphdr *) js_ctx->h_gap,
                         (duk_tval *) duk_get_tval(thr, idx_holder)));

    /* The stack has a variable shape here, so force it to the
     * desired one explicitly.
     */

#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH)
 replace_finished:
#endif
    duk_replace(thr, entry_top);
    duk_set_top(thr, entry_top + 1);

    DUK_DDD(DUK_DDDPRINT("JSON stringify end: value=%!T, replacer=%!T, space=%!T, "
                         "flags=0x%08lx, result=%!T, stack_top=%ld",
                         (duk_tval *) duk_get_tval(thr, idx_value),
                         (duk_tval *) duk_get_tval(thr, idx_replacer),
                         (duk_tval *) duk_get_tval(thr, idx_space),
                         (unsigned long) flags,
                         (duk_tval *) duk_get_tval(thr, -1),
                         (long) duk_get_top(thr)));

    DUK_ASSERT(duk_get_top(thr) == entry_top + 1);
}

#if defined(DUK_USE_JSON_BUILTIN)

/*
 *  Entry points
 */

DUK_INTERNAL duk_ret_t duk_bi_json_object_parse(duk_hthread *thr) {
    duk_bi_json_parse_helper(thr,
                             0 /*idx_value*/,
                             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 (duk_double_equals(x, 0.0) && duk_double_equals(y, 0.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 (duk_double_equals(x, 0.0) && duk_double_equals(y, 0.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) {
    /* cbrt() is C99.  To avoid hassling embedders with the need to provide a
     * cube root function, we can get by with pow().  The result is not
     * identical, but that's OK: ES2015 says it's implementation-dependent.
     */

#if defined(DUK_CBRT)
    /* cbrt() matches ES2015 requirements. */
    return DUK_CBRT(x);
#else
    duk_small_int_t c = (duk_small_int_t) DUK_FPCLASSIFY(x);

    /* pow() does not, however. */
    if (c == DUK_FP_NAN || c == DUK_FP_INFINITE || c == DUK_FP_ZERO) {
        return x;
    }
    if (DUK_SIGNBIT(x)) {
        return -DUK_POW(-x, 1.0 / 3.0);
    } else {
        return DUK_POW(x, 1.0 / 3.0);
    }
#endif
}

DUK_LOCAL double duk__log2(double x) {
#if defined(DUK_LOG2)
    return DUK_LOG2(x);
#else
    return DUK_LOG(x) * DUK_DOUBLE_LOG2E;
#endif
}

DUK_LOCAL double duk__log10(double x) {
#if defined(DUK_LOG10)
    return DUK_LOG10(x);
#else
    return DUK_LOG(x) * DUK_DOUBLE_LOG10E;
#endif
}

DUK_LOCAL double duk__trunc(double x) {
#if defined(DUK_TRUNC)
    return DUK_TRUNC(x);
#else
    /* Handles -0 correctly: -0.0 matches 'x >= 0.0' but floor()
     * is required to return -0 when the argument is -0.
     */
    return x >= 0.0 ? DUK_FLOOR(x) : DUK_CEIL(x);
#endif
}
#endif  /* DUK_USE_ES6 */

DUK_LOCAL double duk__round_fixed(double x) {
    /* Numbers half-way between integers must be rounded towards +Infinity,
     * e.g. -3.5 must be rounded to -3 (not -4).  When rounded to zero, zero
     * sign must be set appropriately.  E5.1 Section 15.8.2.15.
     *
     * Note that ANSI C round() is "round to nearest integer, away from zero",
     * which is incorrect for negative values.  Here we make do with floor().
     */

    duk_small_int_t c = (duk_small_int_t) DUK_FPCLASSIFY(x);
    if (c == DUK_FP_NAN || c == DUK_FP_INFINITE || c == DUK_FP_ZERO) {
        return x;
    }

    /*
     *  x is finite and non-zero
     *
     *  -1.6 -> floor(-1.1) -> -2
     *  -1.5 -> floor(-1.0) -> -1  (towards +Inf)
     *  -1.4 -> floor(-0.9) -> -1
     *  -0.5 -> -0.0               (special case)
     *  -0.1 -> -0.0               (special case)
     *  +0.1 -> +0.0               (special case)
     *  +0.5 -> floor(+1.0) -> 1   (towards +Inf)
     *  +1.4 -> floor(+1.9) -> 1
     *  +1.5 -> floor(+2.0) -> 2   (towards +Inf)
     *  +1.6 -> floor(+2.1) -> 2
     */

    if (x >= -0.5 && x < 0.5) {
        /* +0.5 is handled by floor, this is on purpose */
        if (x < 0.0) {
            return -0.0;
        } else {
            return +0.0;
        }
    }

    return DUK_FLOOR(x + 0.5);
}

/* Wrappers for calling standard math library methods.  These may be required
 * on platforms where one or more of the math built-ins are defined as macros
 * or inline functions and are thus not suitable to be used as function pointers.
 */
#if defined(DUK_USE_AVOID_PLATFORM_FUNCPTRS)
DUK_LOCAL double duk__fabs(double x) {
    return DUK_FABS(x);
}
DUK_LOCAL double duk__acos(double x) {
    return DUK_ACOS(x);
}
DUK_LOCAL double duk__asin(double x) {
    return DUK_ASIN(x);
}
DUK_LOCAL double duk__atan(double x) {
    return DUK_ATAN(x);
}
DUK_LOCAL double duk__ceil(double x) {
    return DUK_CEIL(x);
}
DUK_LOCAL double duk__cos(double x) {
    return DUK_COS(x);
}
DUK_LOCAL double duk__exp(double x) {
    return DUK_EXP(x);
}
DUK_LOCAL double duk__floor(double x) {
    return DUK_FLOOR(x);
}
DUK_LOCAL double duk__log(double x) {
    return DUK_LOG(x);
}
DUK_LOCAL double duk__sin(double x) {
    return DUK_SIN(x);
}
DUK_LOCAL double duk__sqrt(double x) {
    return DUK_SQRT(x);
}
DUK_LOCAL double duk__tan(double x) {
    return DUK_TAN(x);
}
DUK_LOCAL double duk__atan2_fixed(double x, double y) {
#if defined(DUK_USE_ATAN2_WORKAROUNDS)
    /* Specific fixes to common atan2() implementation issues:
     * - test-bug-mingw-math-issues.js
     */
    if (DUK_ISINF(x) && DUK_ISINF(y)) {
        if (DUK_SIGNBIT(x)) {
            if (DUK_SIGNBIT(y)) {
                return -2.356194490192345;
            } else {
                return -0.7853981633974483;
            }
        } else {
            if (DUK_SIGNBIT(y)) {
                return 2.356194490192345;
            } else {
                return 0.7853981633974483;
            }
        }
    }
#else
    /* Some ISO C assumptions. */

    DUK_ASSERT(duk_double_equals(DUK_ATAN2(DUK_DOUBLE_INFINITY, DUK_DOUBLE_INFINITY), 0.7853981633974483));
    DUK_ASSERT(duk_double_equals(DUK_ATAN2(-DUK_DOUBLE_INFINITY, DUK_DOUBLE_INFINITY), -0.7853981633974483));
    DUK_ASSERT(duk_double_equals(DUK_ATAN2(DUK_DOUBLE_INFINITY, -DUK_DOUBLE_INFINITY), 2.356194490192345));
    DUK_ASSERT(duk_double_equals(DUK_ATAN2(-DUK_DOUBLE_INFINITY, -DUK_DOUBLE_INFINITY), -2.356194490192345));
#endif

    return DUK_ATAN2(x, y);
}
#endif  /* DUK_USE_AVOID_PLATFORM_FUNCPTRS */

/* order must match constants in genbuiltins.py */
DUK_LOCAL const duk__one_arg_func duk__one_arg_funcs[] = {
#if defined(DUK_USE_AVOID_PLATFORM_FUNCPTRS)
    duk__fabs,
    duk__acos,
    duk__asin,
    duk__atan,
    duk__ceil,
    duk__cos,
    duk__exp,
    duk__floor,
    duk__log,
    duk__round_fixed,
    duk__sin,
    duk__sqrt,
    duk__tan,
#if defined(DUK_USE_ES6)
    duk__cbrt,
    duk__log2,
    duk__log10,
    duk__trunc
#endif
#else  /* DUK_USE_AVOID_PLATFORM_FUNCPTRS */
    DUK_FABS,
    DUK_ACOS,
    DUK_ASIN,
    DUK_ATAN,
    DUK_CEIL,
    DUK_COS,
    DUK_EXP,
    DUK_FLOOR,
    DUK_LOG,
    duk__round_fixed,
    DUK_SIN,
    DUK_SQRT,
    DUK_TAN,
#if defined(DUK_USE_ES6)
    duk__cbrt,
    duk__log2,
    duk__log10,
    duk__trunc
#endif
#endif  /* DUK_USE_AVOID_PLATFORM_FUNCPTRS */
};

/* 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 (duk_double_equals(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 (duk_double_equals(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 (duk_double_equals(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;
    }
    DUK_ASSERT(i <= 32);
    duk_push_uint(thr, i);
    return 1;
#endif  /* DUK_USE_PREFER_SIZE */
}
#endif  /* DUK_USE_ES6 */

#if defined(DUK_USE_ES6)
DUK_INTERNAL duk_ret_t duk_bi_math_object_imul(duk_hthread *thr) {
    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_xget_owndataprop_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;
    duk_small_uint_t n2s_flags;

    /* In ES5.1 frac_digits is coerced first; in ES2015 the 'this number
     * value' check is done first.
     */
    d = duk__push_this_number_plain(thr);
    frac_digits = (duk_small_int_t) duk_to_int_check_range(thr, 0, 0, 20);

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

    if (d >= 1.0e21 || d <= -1.0e21) {
        goto use_to_string;
    }

    n2s_flags = DUK_N2S_FLAG_FIXED_FORMAT |
                DUK_N2S_FLAG_FRACTION_DIGITS;

    duk_numconv_stringify(thr,
                          10 /*radix*/,
                          frac_digits /*digits*/,
                          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_exponential(duk_hthread *thr) {
    duk_bool_t frac_undefined;
    duk_small_int_t frac_digits;
    duk_double_t d;
    duk_small_int_t c;
    duk_small_uint_t n2s_flags;

    d = duk__push_this_number_plain(thr);

    frac_undefined = duk_is_undefined(thr, 0);
    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;
    }

    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;
    }

    (void) duk_push_object_helper(thr,
                                  DUK_HOBJECT_FLAG_EXTENSIBLE |
                                  DUK_HOBJECT_FLAG_FASTREFS |
                                  DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
                                  DUK_BIDX_OBJECT_PROTOTYPE);
    return 1;
}
#endif  /* DUK_USE_OBJECT_BUILTIN */

#if defined(DUK_USE_OBJECT_BUILTIN) && defined(DUK_USE_ES6)
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_assign(duk_hthread *thr) {
    duk_idx_t nargs;
    duk_int_t idx;

    nargs = duk_get_top_require_min(thr, 1 /*min_top*/);

    duk_to_object(thr, 0);
    for (idx = 1; idx < nargs; idx++) {
        /* E7 19.1.2.1 (step 4a) */
        if (duk_is_null_or_undefined(thr, idx)) {
            continue;
        }

        /* duk_enum() respects ES2015+ [[OwnPropertyKeys]] ordering, which is
         * convenient here.
         */
        duk_to_object(thr, idx);
        duk_enum(thr, idx, DUK_ENUM_OWN_PROPERTIES_ONLY);
        while (duk_next(thr, -1, 1 /*get_value*/)) {
            /* [ target ... enum key value ] */
            duk_put_prop(thr, 0);
            /* [ target ... enum ] */
        }
        /* Could pop enumerator, but unnecessary because of duk_set_top()
         * below.
         */
    }

    duk_set_top(thr, 1);
    return 1;
}
#endif

#if defined(DUK_USE_OBJECT_BUILTIN) && defined(DUK_USE_ES6)
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_is(duk_hthread *thr) {
    DUK_ASSERT_TOP(thr, 2);
    duk_push_boolean(thr, duk_samevalue(thr, 0, 1));
    return 1;
}
#endif

#if defined(DUK_USE_OBJECT_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_create(duk_hthread *thr) {
    duk_hobject *proto;

    DUK_ASSERT_TOP(thr, 2);

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
    duk_hbufobj_promote_plain(thr, 0);
#endif
    proto = duk_require_hobject_accept_mask(thr, 0, DUK_TYPE_MASK_NULL);
    DUK_ASSERT(proto != NULL || duk_is_null(thr, 0));

    (void) duk_push_object_helper_proto(thr,
                                        DUK_HOBJECT_FLAG_EXTENSIBLE |
                                        DUK_HOBJECT_FLAG_FASTREFS |
                                        DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
                                        proto);

    if (!duk_is_undefined(thr, 1)) {
        /* [ O Properties obj ] */

        duk_replace(thr, 0);

        /* [ obj Properties ] */

        /* Just call the "original" Object.defineProperties() to
         * finish up.
         */

        return duk_bi_object_constructor_define_properties(thr);
    }

    /* [ O Properties obj ] */

    return 1;
}
#endif  /* DUK_USE_OBJECT_BUILTIN */

#if defined(DUK_USE_OBJECT_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_properties(duk_hthread *thr) {
    duk_small_uint_t pass;
    duk_uint_t defprop_flags;
    duk_hobject *obj;
    duk_idx_t idx_value;
    duk_hobject *get;
    duk_hobject *set;

    /* Lightfunc and plain buffer handling by ToObject() coercion. */
    obj = duk_require_hobject_promote_mask(thr, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
    DUK_ASSERT(obj != NULL);

    duk_to_object(thr, 1);        /* properties object */

    DUK_DDD(DUK_DDDPRINT("target=%!iT, properties=%!iT",
                         (duk_tval *) duk_get_tval(thr, 0),
                         (duk_tval *) duk_get_tval(thr, 1)));

    /*
     *  Two pass approach to processing the property descriptors.
     *  On first pass validate and normalize all descriptors before
     *  any changes are made to the target object.  On second pass
     *  make the actual modifications to the target object.
     *
     *  Right now we'll just use the same normalize/validate helper
     *  on both passes, ignoring its outputs on the first pass.
     */

    for (pass = 0; pass < 2; pass++) {
        duk_set_top(thr, 2);  /* -> [ hobject props ] */
        duk_enum(thr, 1, DUK_ENUM_OWN_PROPERTIES_ONLY | DUK_ENUM_INCLUDE_SYMBOLS /*enum_flags*/);

        for (;;) {
            duk_hstring *key;

            /* [ hobject props enum(props) ] */

            duk_set_top(thr, 3);

            if (!duk_next(thr, 2, 1 /*get_value*/)) {
                break;
            }

            DUK_DDD(DUK_DDDPRINT("-> key=%!iT, desc=%!iT",
                                 (duk_tval *) duk_get_tval(thr, -2),
                                 (duk_tval *) duk_get_tval(thr, -1)));

            /* [ hobject props enum(props) key desc ] */

            duk_hobject_prepare_property_descriptor(thr,
                                                    4 /*idx_desc*/,
                                                    &defprop_flags,
                                                    &idx_value,
                                                    &get,
                                                    &set);

            /* [ hobject props enum(props) key desc [multiple values] ] */

            if (pass == 0) {
                continue;
            }

            /* This allows symbols on purpose. */
            key = duk_known_hstring(thr, 3);
            DUK_ASSERT(key != NULL);

            duk_hobject_define_property_helper(thr,
                                               defprop_flags,
                                               obj,
                                               key,
                                               idx_value,
                                               get,
                                               set,
                                               1 /*throw_flag*/);
        }
    }

    /*
     *  Return target object
     */

    duk_dup_0(thr);
    return 1;
}
#endif  /* DUK_USE_OBJECT_BUILTIN */

#if defined(DUK_USE_OBJECT_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_seal_freeze_shared(duk_hthread *thr) {
    DUK_ASSERT_TOP(thr, 1);

    duk_seal_freeze_raw(thr, 0, (duk_bool_t) duk_get_current_magic(thr) /*is_freeze*/);
    return 1;
}
#endif  /* DUK_USE_OBJECT_BUILTIN */

#if defined(DUK_USE_OBJECT_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_is_sealed_frozen_shared(duk_hthread *thr) {
    duk_hobject *h;
    duk_bool_t is_frozen;
    duk_uint_t mask;

    is_frozen = (duk_bool_t) duk_get_current_magic(thr);
    mask = duk_get_type_mask(thr, 0);
    if (mask & (DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER)) {
        DUK_ASSERT(is_frozen == 0 || is_frozen == 1);
        duk_push_boolean(thr, (mask & DUK_TYPE_MASK_LIGHTFUNC) ?
                                  1 :               /* lightfunc always frozen and sealed */
                                  (is_frozen ^ 1)); /* buffer sealed but not frozen (index props writable) */
    } else {
        /* ES2015 Sections 19.1.2.12, 19.1.2.13: anything other than an object
         * is considered to be already sealed and frozen.
         */
        h = duk_get_hobject(thr, 0);
        duk_push_boolean(thr, (h == NULL) ||
                              duk_hobject_object_is_sealed_frozen_helper(thr, h, is_frozen /*is_frozen*/));
    }
    return 1;
}
#endif  /* DUK_USE_OBJECT_BUILTIN */

#if defined(DUK_USE_OBJECT_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_object_prototype_to_locale_string(duk_hthread *thr) {
    DUK_ASSERT_TOP(thr, 0);
    (void) duk_push_this_coercible_to_object(thr);
    duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_TO_STRING);
#if 0  /* This is mentioned explicitly in the E5.1 spec, but duk_call_method() checks for it in practice. */
    duk_require_callable(thr, 1);
#endif
    duk_dup_0(thr);  /* -> [ O toString O ] */
    duk_call_method(thr, 0);  /* XXX: call method tail call? */
    return 1;
}
#endif  /* DUK_USE_OBJECT_BUILTIN */

#if defined(DUK_USE_OBJECT_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_object_prototype_value_of(duk_hthread *thr) {
    /* For lightfuncs and plain buffers, returns Object() coerced. */
    (void) duk_push_this_coercible_to_object(thr);
    return 1;
}
#endif  /* DUK_USE_OBJECT_BUILTIN */

#if defined(DUK_USE_OBJECT_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_object_prototype_is_prototype_of(duk_hthread *thr) {
    duk_hobject *h_v;
    duk_hobject *h_obj;

    DUK_ASSERT_TOP(thr, 1);

    h_v = duk_get_hobject(thr, 0);
    if (!h_v) {
        duk_push_false(thr);  /* XXX: tail call: return duk_push_false(thr) */
        return 1;
    }

    h_obj = duk_push_this_coercible_to_object(thr);
    DUK_ASSERT(h_obj != NULL);

    /* E5.1 Section 15.2.4.6, step 3.a, lookup proto once before compare.
     * Prototype loops should cause an error to be thrown.
     */
    duk_push_boolean(thr, duk_hobject_prototype_chain_contains(thr, DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_v), h_obj, 0 /*ignore_loop*/));
    return 1;
}
#endif  /* DUK_USE_OBJECT_BUILTIN */

#if defined(DUK_USE_OBJECT_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_object_prototype_has_own_property(duk_hthread *thr) {
    return (duk_ret_t) duk_hobject_object_ownprop_helper(thr, 0 /*required_desc_flags*/);
}
#endif  /* DUK_USE_OBJECT_BUILTIN */

#if defined(DUK_USE_OBJECT_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_object_prototype_property_is_enumerable(duk_hthread *thr) {
    return (duk_ret_t) duk_hobject_object_ownprop_helper(thr, DUK_PROPDESC_FLAG_ENUMERABLE /*required_desc_flags*/);
}
#endif  /* DUK_USE_OBJECT_BUILTIN */

#if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
/* Shared helper to implement Object.getPrototypeOf,
 * Object.prototype.__proto__ getter, and Reflect.getPrototypeOf.
 *
 * http://www.ecma-international.org/ecma-262/6.0/index.html#sec-get-object.prototype.__proto__
 */
DUK_INTERNAL duk_ret_t duk_bi_object_getprototype_shared(duk_hthread *thr) {
    /*
     *  magic = 0: __proto__ getter
     *  magic = 1: Object.getPrototypeOf()
     *  magic = 2: Reflect.getPrototypeOf()
     */

    duk_hobject *h;
    duk_hobject *proto;
    duk_tval *tv;
    duk_int_t magic;

    magic = duk_get_current_magic(thr);

    if (magic == 0) {
        DUK_ASSERT_TOP(thr, 0);
        duk_push_this_coercible_to_object(thr);
    }
    DUK_ASSERT(duk_get_top(thr) >= 1);
    if (magic < 2) {
        /* ES2015 Section 19.1.2.9, step 1 */
        duk_to_object(thr, 0);
    }
    tv = DUK_GET_TVAL_POSIDX(thr, 0);

    switch (DUK_TVAL_GET_TAG(tv)) {
    case DUK_TAG_BUFFER:
        proto = thr->builtins[DUK_BIDX_UINT8ARRAY_PROTOTYPE];
        break;
    case DUK_TAG_LIGHTFUNC:
        proto = thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE];
        break;
    case DUK_TAG_OBJECT:
        h = DUK_TVAL_GET_OBJECT(tv);
        proto = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h);
        break;
    default:
        /* This implicitly handles CheckObjectCoercible() caused
         * TypeError.
         */
        DUK_DCERROR_TYPE_INVALID_ARGS(thr);
    }
    if (proto != NULL) {
        duk_push_hobject(thr, proto);
    } else {
        duk_push_null(thr);
    }
    return 1;
}
#endif  /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */

#if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
/* Shared helper to implement ES2015 Object.setPrototypeOf,
 * Object.prototype.__proto__ setter, and Reflect.setPrototypeOf.
 *
 * http://www.ecma-international.org/ecma-262/6.0/index.html#sec-get-object.prototype.__proto__
 * http://www.ecma-international.org/ecma-262/6.0/index.html#sec-object.setprototypeof
 */
DUK_INTERNAL duk_ret_t duk_bi_object_setprototype_shared(duk_hthread *thr) {
    /*
     *  magic = 0: __proto__ setter
     *  magic = 1: Object.setPrototypeOf()
     *  magic = 2: Reflect.setPrototypeOf()
     */

    duk_hobject *h_obj;
    duk_hobject *h_new_proto;
    duk_hobject *h_curr;
    duk_ret_t ret_success = 1;  /* retval for success path */
    duk_uint_t mask;
    duk_int_t magic;

    /* Preliminaries for __proto__ and setPrototypeOf (E6 19.1.2.18 steps 1-4). */
    magic = duk_get_current_magic(thr);
    if (magic == 0) {
        duk_push_this_check_object_coercible(thr);
        duk_insert(thr, 0);
        if (!duk_check_type_mask(thr, 1, DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_OBJECT)) {
            return 0;
        }

        /* __proto__ setter returns 'undefined' on success unlike the
         * setPrototypeOf() call which returns the target object.
         */
        ret_success = 0;
    } else {
        if (magic == 1) {
            duk_require_object_coercible(thr, 0);
        } else {
            duk_require_hobject_accept_mask(thr, 0,
                                            DUK_TYPE_MASK_LIGHTFUNC |
                                            DUK_TYPE_MASK_BUFFER);
        }
        duk_require_type_mask(thr, 1, DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_OBJECT);
    }

    h_new_proto = duk_get_hobject(thr, 1);
    /* h_new_proto may be NULL */

    mask = duk_get_type_mask(thr, 0);
    if (mask & (DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER)) {
        duk_hobject *curr_proto;
        curr_proto = thr->builtins[(mask & DUK_TYPE_MASK_LIGHTFUNC) ?
                                       DUK_BIDX_FUNCTION_PROTOTYPE :
                                       DUK_BIDX_UINT8ARRAY_PROTOTYPE];
        if (h_new_proto == curr_proto) {
            goto skip;
        }
        goto fail_nonextensible;
    }
    h_obj = duk_get_hobject(thr, 0);
    if (h_obj == NULL) {
        goto skip;
    }
    DUK_ASSERT(h_obj != NULL);

    /* [[SetPrototypeOf]] standard behavior, E6 9.1.2. */
    /* TODO: implement Proxy object support here */

    if (h_new_proto == DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_obj)) {
        goto skip;
    }
    if (!DUK_HOBJECT_HAS_EXTENSIBLE(h_obj)) {
        goto fail_nonextensible;
    }
    for (h_curr = h_new_proto; h_curr != NULL; h_curr = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_curr)) {
        /* Loop prevention. */
        if (h_curr == h_obj) {
            goto fail_loop;
        }
    }
    DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h_obj, h_new_proto);
    /* fall thru */

 skip:
    duk_set_top(thr, 1);
    if (magic == 2) {
        duk_push_true(thr);
    }
    return ret_success;

 fail_nonextensible:
 fail_loop:
    if (magic != 2) {
        DUK_DCERROR_TYPE_INVALID_ARGS(thr);
    } else {
        duk_push_false(thr);
        return 1;
    }
}
#endif  /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */

#if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_property(duk_hthread *thr) {
    /*
     *  magic = 0: Object.defineProperty()
     *  magic = 1: Reflect.defineProperty()
     */

    duk_hobject *obj;
    duk_hstring *key;
    duk_hobject *get;
    duk_hobject *set;
    duk_idx_t idx_value;
    duk_uint_t defprop_flags;
    duk_small_uint_t magic;
    duk_bool_t throw_flag;
    duk_bool_t ret;

    DUK_ASSERT(thr != NULL);

    DUK_DDD(DUK_DDDPRINT("Object.defineProperty(): ctx=%p obj=%!T key=%!T desc=%!T",
                         (void *) thr,
                         (duk_tval *) duk_get_tval(thr, 0),
                         (duk_tval *) duk_get_tval(thr, 1),
                         (duk_tval *) duk_get_tval(thr, 2)));

    /* [ obj key desc ] */

    magic = (duk_small_uint_t) duk_get_current_magic(thr);

    /* Lightfuncs are currently supported by coercing to a temporary
     * Function object; changes will be allowed (the coerced value is
     * extensible) but will be lost.  Same for plain buffers.
     */
    obj = duk_require_hobject_promote_mask(thr, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
    DUK_ASSERT(obj != NULL);
    key = duk_to_property_key_hstring(thr, 1);
    (void) duk_require_hobject(thr, 2);

    DUK_ASSERT(obj != NULL);
    DUK_ASSERT(key != NULL);
    DUK_ASSERT(duk_get_hobject(thr, 2) != NULL);

    /*
     *  Validate and convert argument property descriptor (an ECMAScript
     *  object) into a set of defprop_flags and possibly property value,
     *  getter, and/or setter values on the value stack.
     *
     *  Lightfunc set/get values are coerced to full Functions.
     */

    duk_hobject_prepare_property_descriptor(thr,
                                            2 /*idx_desc*/,
                                            &defprop_flags,
                                            &idx_value,
                                            &get,
                                            &set);

    /*
     *  Use Object.defineProperty() helper for the actual operation.
     */

    DUK_ASSERT(magic == 0U || magic == 1U);
    throw_flag = magic ^ 1U;
    ret = duk_hobject_define_property_helper(thr,
                                             defprop_flags,
                                             obj,
                                             key,
                                             idx_value,
                                             get,
                                             set,
                                             throw_flag);

    /* Ignore the normalize/validate helper outputs on the value stack,
     * they're popped automatically.
     */

    if (magic == 0U) {
        /* Object.defineProperty(): return target object. */
        duk_push_hobject(thr, obj);
    } else {
        /* Reflect.defineProperty(): return success/fail. */
        duk_push_boolean(thr, ret);
    }
    return 1;
}
#endif  /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */

#if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_get_own_property_descriptor(duk_hthread *thr) {
    DUK_ASSERT_TOP(thr, 2);

    /* ES2015 Section 19.1.2.6, step 1 */
    if (duk_get_current_magic(thr) == 0) {
        duk_to_object(thr, 0);
    }

    /* [ obj key ] */

    duk_hobject_object_get_own_property_descriptor(thr, -2);
    return 1;
}
#endif  /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */

#if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_is_extensible(duk_hthread *thr) {
    /*
     *  magic = 0: Object.isExtensible()
     *  magic = 1: Reflect.isExtensible()
     */

    duk_hobject *h;

    if (duk_get_current_magic(thr) == 0) {
        h = duk_get_hobject(thr, 0);
    } else {
        /* Reflect.isExtensible(): throw if non-object, but we accept lightfuncs
         * and plain buffers here because they pretend to be objects.
         */
        h = duk_require_hobject_accept_mask(thr, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
    }

    duk_push_boolean(thr, (h != NULL) && DUK_HOBJECT_HAS_EXTENSIBLE(h));
    return 1;
}
#endif  /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */

#if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
/* Shared helper for various key/symbol listings, magic:
 * 0=Object.keys()
 * 1=Object.getOwnPropertyNames(),
 * 2=Object.getOwnPropertySymbols(),
 * 3=Reflect.ownKeys()
 */
DUK_LOCAL const duk_small_uint_t duk__object_keys_enum_flags[4] = {
    /* Object.keys() */
    DUK_ENUM_OWN_PROPERTIES_ONLY |
        DUK_ENUM_NO_PROXY_BEHAVIOR,

    /* Object.getOwnPropertyNames() */
    DUK_ENUM_INCLUDE_NONENUMERABLE |
        DUK_ENUM_OWN_PROPERTIES_ONLY |
        DUK_ENUM_NO_PROXY_BEHAVIOR,

    /* Object.getOwnPropertySymbols() */
    DUK_ENUM_INCLUDE_SYMBOLS |
        DUK_ENUM_OWN_PROPERTIES_ONLY |
        DUK_ENUM_EXCLUDE_STRINGS |
        DUK_ENUM_INCLUDE_NONENUMERABLE |
        DUK_ENUM_NO_PROXY_BEHAVIOR,

    /* Reflect.ownKeys() */
    DUK_ENUM_INCLUDE_SYMBOLS |
        DUK_ENUM_OWN_PROPERTIES_ONLY |
        DUK_ENUM_INCLUDE_NONENUMERABLE |
        DUK_ENUM_NO_PROXY_BEHAVIOR
};

DUK_INTERNAL duk_ret_t duk_bi_object_constructor_keys_shared(duk_hthread *thr) {
    duk_hobject *obj;
#if defined(DUK_USE_ES6_PROXY)
    duk_hobject *h_proxy_target;
    duk_hobject *h_proxy_handler;
    duk_hobject *h_trap_result;
#endif
    duk_small_uint_t enum_flags;
    duk_int_t magic;

    DUK_ASSERT_TOP(thr, 1);

    magic = duk_get_current_magic(thr);
    if (magic == 3) {
        /* ES2015 Section 26.1.11 requires a TypeError for non-objects.  Lightfuncs
         * and plain buffers pretend to be objects, so accept those too.
         */
        obj = duk_require_hobject_promote_mask(thr, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
    } else {
        /* ES2015: ToObject coerce. */
        obj = duk_to_hobject(thr, 0);
    }
    DUK_ASSERT(obj != NULL);
    DUK_UNREF(obj);

    /* XXX: proxy chains */

#if defined(DUK_USE_ES6_PROXY)
    /* XXX: better sharing of code between proxy target call sites */
    if (DUK_LIKELY(!duk_hobject_proxy_check(obj,
                                            &h_proxy_target,
                                            &h_proxy_handler))) {
        goto skip_proxy;
    }

    duk_push_hobject(thr, h_proxy_handler);
    if (!duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_OWN_KEYS)) {
        /* Careful with reachability here: don't pop 'obj' before pushing
         * proxy target.
         */
        DUK_DDD(DUK_DDDPRINT("no ownKeys trap, get keys of target instead"));
        duk_pop_2(thr);
        duk_push_hobject(thr, h_proxy_target);
        duk_replace(thr, 0);
        DUK_ASSERT_TOP(thr, 1);
        goto skip_proxy;
    }

    /* [ obj handler trap ] */
    duk_insert(thr, -2);
    duk_push_hobject(thr, h_proxy_target);  /* -> [ obj trap handler target ] */
    duk_call_method(thr, 1 /*nargs*/);      /* -> [ obj trap_result ] */
    h_trap_result = duk_require_hobject(thr, -1);
    DUK_UNREF(h_trap_result);

    magic = duk_get_current_magic(thr);
    DUK_ASSERT(magic >= 0 && magic < (duk_int_t) (sizeof(duk__object_keys_enum_flags) / sizeof(duk_small_uint_t)));
    enum_flags = duk__object_keys_enum_flags[magic];

    duk_proxy_ownkeys_postprocess(thr, h_proxy_target, enum_flags);
    return 1;

 skip_proxy:
#endif  /* DUK_USE_ES6_PROXY */

    DUK_ASSERT_TOP(thr, 1);
    magic = duk_get_current_magic(thr);
    DUK_ASSERT(magic >= 0 && magic < (duk_int_t) (sizeof(duk__object_keys_enum_flags) / sizeof(duk_small_uint_t)));
    enum_flags = duk__object_keys_enum_flags[magic];
    return duk_hobject_get_enumerated_keys(thr, enum_flags);
}
#endif  /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */

#if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_prevent_extensions(duk_hthread *thr) {
    /*
     *  magic = 0: Object.preventExtensions()
     *  magic = 1: Reflect.preventExtensions()
     */

    duk_hobject *h;
    duk_uint_t mask;
    duk_int_t magic;

    magic = duk_get_current_magic(thr);

    /* Silent success for lightfuncs and plain buffers always. */
    mask = DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER;

    /* Object.preventExtensions() silent success for non-object. */
    if (magic == 0) {
        mask |= DUK_TYPE_MASK_UNDEFINED |
                DUK_TYPE_MASK_NULL |
                DUK_TYPE_MASK_BOOLEAN |
                DUK_TYPE_MASK_NUMBER |
                DUK_TYPE_MASK_STRING |
                DUK_TYPE_MASK_POINTER;
    }

    if (duk_check_type_mask(thr, 0, mask)) {
        /* Not an object, already non-extensible so always success. */
        goto done;
    }
    h = duk_require_hobject(thr, 0);
    DUK_ASSERT(h != NULL);

    DUK_HOBJECT_CLEAR_EXTENSIBLE(h);

    /* A non-extensible object cannot gain any more properties,
     * so this is a good time to compact.
     */
    duk_hobject_compact_props(thr, h);

 done:
    if (magic == 1) {
        duk_push_true(thr);
    }
    return 1;
}
#endif  /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */

/*
 *  __defineGetter__, __defineSetter__, __lookupGetter__, __lookupSetter__
 */

#if defined(DUK_USE_ES8)
DUK_INTERNAL duk_ret_t duk_bi_object_prototype_defineaccessor(duk_hthread *thr) {
    duk_push_this(thr);
    duk_insert(thr, 0);
    duk_to_object(thr, 0);
    duk_require_callable(thr, 2);

    /* [ ToObject(this) key getter/setter ] */

    /* ToPropertyKey() coercion is not needed, duk_def_prop() does it. */
    duk_def_prop(thr, 0, DUK_DEFPROP_SET_ENUMERABLE |
                         DUK_DEFPROP_SET_CONFIGURABLE |
                         (duk_get_current_magic(thr) ? DUK_DEFPROP_HAVE_SETTER : DUK_DEFPROP_HAVE_GETTER));
    return 0;
}
DUK_INTERNAL duk_ret_t duk_bi_object_prototype_lookupaccessor(duk_hthread *thr) {
    duk_uint_t sanity;

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

    /* XXX: Prototype walk (with sanity) should be a core property
     * operation, could add a flag to e.g. duk_get_prop_desc().
     */

    /* ToPropertyKey() coercion is not needed, duk_get_prop_desc() does it. */
    sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
    while (!duk_is_undefined(thr, -1)) {
        /* [ key obj ] */
        duk_dup(thr, 0);
        duk_get_prop_desc(thr, 1, 0 /*flags*/);
        if (!duk_is_undefined(thr, -1)) {
            duk_get_prop_stridx(thr, -1, (duk_get_current_magic(thr) != 0 ? DUK_STRIDX_SET : DUK_STRIDX_GET));
            return 1;
        }
        duk_pop(thr);

        if (DUK_UNLIKELY(sanity-- == 0)) {
            DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT);
            DUK_WO_NORETURN(return 0;);
        }

        duk_get_prototype(thr, -1);
        duk_remove(thr, -2);
    }
    return 1;
}
#endif  /* DUK_USE_ES8 */
//#line 1 "duk_bi_performance.c"
/*
 *  High resolution time API (performance.now() et al)
 *
 *  API specification: https://encoding.spec.whatwg.org/#ap://www.w3.org/TR/hr-time/
 */

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

#if defined(DUK_USE_PERFORMANCE_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_performance_now(duk_hthread *thr) {
    /* From API spec:
     * The DOMHighResTimeStamp type is used to store a time value in
     * milliseconds, measured relative from the time origin, global
     * monotonic clock, or a time value that represents a duration
     * between two DOMHighResTimeStamp's.
     */
    duk_push_number(thr, duk_time_get_monotonic_time(thr));
    return 1;
}

#if 0  /* Missing until semantics decided. */
DUK_INTERNAL duk_ret_t duk_bi_performance_timeorigin_getter(duk_hthread *thr) {
    /* No decision yet how to handle timeOrigins, e.g. should one be
     * initialized per heap, or per global object set.  See
     * https://www.w3.org/TR/hr-time/#time-origin.
     */
    duk_push_uint(thr, 0);
    return 1;
}
#endif  /* 0 */
#endif  /* DUK_USE_PERFORMANCE_BUILTIN */
//#line 1 "duk_bi_pointer.c"
/*
 *  Pointer built-ins
 */

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

/*
 *  Constructor
 */

DUK_INTERNAL duk_ret_t duk_bi_pointer_constructor(duk_hthread *thr) {
    /* XXX: this behavior is quite useless now; it would be nice to be able
     * to create pointer values from e.g. numbers or strings.  Numbers are
     * problematic on 64-bit platforms though.  Hex encoded strings?
     */
    if (duk_get_top(thr) == 0) {
        duk_push_pointer(thr, NULL);
    } else {
        duk_to_pointer(thr, 0);
    }
    DUK_ASSERT(duk_is_pointer(thr, 0));
    duk_set_top(thr, 1);

    if (duk_is_constructor_call(thr)) {
        (void) duk_push_object_helper(thr,
                                      DUK_HOBJECT_FLAG_EXTENSIBLE |
                                      DUK_HOBJECT_FLAG_FASTREFS |
                                      DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_POINTER),
                                      DUK_BIDX_POINTER_PROTOTYPE);

        /* Pointer object internal value is immutable. */
        duk_dup_0(thr);
        duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE);
    }
    /* Note: unbalanced stack on purpose */

    return 1;
}

/*
 *  toString(), valueOf()
 */

DUK_INTERNAL duk_ret_t duk_bi_pointer_prototype_tostring_shared(duk_hthread *thr) {
    duk_tval *tv;
    duk_small_int_t to_string = duk_get_current_magic(thr);

    duk_push_this(thr);
    tv = duk_require_tval(thr, -1);
    DUK_ASSERT(tv != NULL);

    if (DUK_TVAL_IS_POINTER(tv)) {
        /* nop */
    } else if (DUK_TVAL_IS_OBJECT(tv)) {
        duk_hobject *h = DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(h != NULL);

        /* Must be a "pointer object", i.e. class "Pointer" */
        if (DUK_HOBJECT_GET_CLASS_NUMBER(h) != DUK_HOBJECT_CLASS_POINTER) {
            goto type_error;
        }

        duk_xget_owndataprop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE);
    } else {
        goto type_error;
    }

    if (to_string) {
        duk_to_string(thr, -1);
    }
    return 1;

 type_error:
    DUK_DCERROR_TYPE_INVALID_ARGS(thr);
}
//#line 1 "duk_bi_promise.c"
/*
 *  Promise built-in
 */

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

#if defined(DUK_USE_PROMISE_BUILTIN)

DUK_INTERNAL duk_ret_t duk_bi_promise_constructor(duk_hthread *thr) {
    DUK_ERROR_TYPE(thr, "unimplemented");
    DUK_WO_NORETURN(return 0;);
}

DUK_INTERNAL duk_ret_t duk_bi_promise_all(duk_hthread *thr) {
    DUK_ERROR_TYPE(thr, "unimplemented");
    DUK_WO_NORETURN(return 0;);
}

DUK_INTERNAL duk_ret_t duk_bi_promise_race(duk_hthread *thr) {
    DUK_ERROR_TYPE(thr, "unimplemented");
    DUK_WO_NORETURN(return 0;);
}

DUK_INTERNAL duk_ret_t duk_bi_promise_reject(duk_hthread *thr) {
    DUK_ERROR_TYPE(thr, "unimplemented");
    DUK_WO_NORETURN(return 0;);
}

DUK_INTERNAL duk_ret_t duk_bi_promise_resolve(duk_hthread *thr) {
    DUK_ERROR_TYPE(thr, "unimplemented");
    DUK_WO_NORETURN(return 0;);
}

DUK_INTERNAL duk_ret_t duk_bi_promise_catch(duk_hthread *thr) {
    DUK_ERROR_TYPE(thr, "unimplemented");
    DUK_WO_NORETURN(return 0;);
}

DUK_INTERNAL duk_ret_t duk_bi_promise_then(duk_hthread *thr) {
    DUK_ERROR_TYPE(thr, "unimplemented");
    DUK_WO_NORETURN(return 0;);
}

#endif  /* DUK_USE_PROMISE_BUILTIN */
//#line 1 "duk_bi_proxy.c"
/*
 *  Proxy built-in (ES2015)
 */

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

#if defined(DUK_USE_ES6_PROXY)
/* Post-process a Proxy ownKeys() result at stack top.  Push a cleaned up
 * array of valid result keys (strings or symbols).  TypeError for invalid
 * values.  Flags are shared with duk_enum().
 */
DUK_INTERNAL void duk_proxy_ownkeys_postprocess(duk_hthread *thr, duk_hobject *h_proxy_target, duk_uint_t flags) {
    duk_uarridx_t i, len, idx;
    duk_propdesc desc;

    DUK_CTX_ASSERT_VALID(thr);
    DUK_ASSERT(h_proxy_target != NULL);

    len = (duk_uarridx_t) duk_get_length(thr, -1);
    idx = 0;
    duk_push_array(thr);
    /* XXX: preallocated dense array, fill in directly */
    for (i = 0; i < len; i++) {
        duk_hstring *h;

        /* [ obj trap_result res_arr ] */
        (void) duk_get_prop_index(thr, -2, i);
        h = duk_get_hstring(thr, -1);
        if (h == NULL) {
            DUK_ERROR_TYPE_INVALID_TRAP_RESULT(thr);
            DUK_WO_NORETURN(return;);
        }

        if (!(flags & DUK_ENUM_INCLUDE_NONENUMERABLE)) {
            /* No support for 'getOwnPropertyDescriptor' trap yet,
             * so check enumerability always from target object
             * descriptor.
             */
            if (duk_hobject_get_own_propdesc(thr, h_proxy_target, duk_known_hstring(thr, -1), &desc, 0 /*flags*/)) {
                if ((desc.flags & DUK_PROPDESC_FLAG_ENUMERABLE) == 0) {
                    DUK_DDD(DUK_DDDPRINT("ignore non-enumerable property: %!T", duk_get_tval(thr, -1)));
                    goto skip_key;
                }
            } else {
                DUK_DDD(DUK_DDDPRINT("ignore non-existent property: %!T", duk_get_tval(thr, -1)));
                goto skip_key;
            }
        }
        if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) {
            if (!(flags & DUK_ENUM_INCLUDE_SYMBOLS)) {
                DUK_DDD(DUK_DDDPRINT("ignore symbol property: %!T", duk_get_tval(thr, -1)));
                goto skip_key;
            }
            if (DUK_HSTRING_HAS_HIDDEN(h) && !(flags & DUK_ENUM_INCLUDE_HIDDEN)) {
                DUK_DDD(DUK_DDDPRINT("ignore hidden symbol property: %!T", duk_get_tval(thr, -1)));
                goto skip_key;
            }
        } else {
            if (flags & DUK_ENUM_EXCLUDE_STRINGS) {
                DUK_DDD(DUK_DDDPRINT("ignore string property: %!T", duk_get_tval(thr, -1)));
                goto skip_key;
            }
        }

        /* [ obj trap_result res_arr propname ] */
        duk_push_uarridx(thr, idx++);
        duk_insert(thr, -2);
        duk_def_prop(thr, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WEC);
        continue;

     skip_key:
        duk_pop(thr);
        continue;
    }

    /* XXX: Missing trap result validation for non-configurable target keys
     * (must be present), for non-extensible target all target keys must be
     * present and no extra keys can be present.
     * http://www.ecma-international.org/ecma-262/6.0/#sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeys
     */

    /* XXX: The key enumerability check should trigger the "getOwnPropertyDescriptor"
     * trap which has not yet been implemented.  In the absence of such a trap,
     * the enumerability should be checked from the target object; this is
     * handled above.
     */
}
#endif  /* DUK_USE_ES6_PROXY */

#if defined(DUK_USE_ES6_PROXY)
DUK_INTERNAL duk_ret_t duk_bi_proxy_constructor(duk_hthread *thr) {
    DUK_ASSERT_TOP(thr, 2);  /* [ target handler ] */

    duk_require_constructor_call(thr);
    duk_push_proxy(thr, 0 /*flags*/);  /* [ target handler ] -> [ proxy ] */
    return 1;  /* replacement */
}
#endif  /* DUK_USE_ES6_PROXY */
//#line 1 "duk_bi_reflect.c"
/*
 *  'Reflect' built-in (ES2016 Section 26.1)
 *  http://www.ecma-international.org/ecma-262/7.0/#sec-reflect-object
 *
 *  Many Reflect built-in functions are provided by shared helpers in
 *  duk_bi_object.c or duk_bi_function.c.
 */

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

#if defined(DUK_USE_REFLECT_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_reflect_object_delete_property(duk_hthread *thr) {
    duk_tval *tv_obj;
    duk_tval *tv_key;
    duk_bool_t ret;

    DUK_ASSERT_TOP(thr, 2);
    (void) duk_require_hobject(thr, 0);
    (void) duk_to_string(thr, 1);

    /* [ target key ] */

    DUK_ASSERT(thr != NULL);
    tv_obj = DUK_GET_TVAL_POSIDX(thr, 0);
    tv_key = DUK_GET_TVAL_POSIDX(thr, 1);
    ret = duk_hobject_delprop(thr, tv_obj, tv_key, 0 /*throw_flag*/);
    duk_push_boolean(thr, ret);
    return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_reflect_object_get(duk_hthread *thr) {
    duk_tval *tv_obj;
    duk_tval *tv_key;
    duk_idx_t nargs;

    DUK_ASSERT(thr != NULL);
    nargs = duk_get_top_require_min(thr, 2 /*min_top*/);
    (void) duk_require_hobject(thr, 0);
    (void) duk_to_string(thr, 1);
    if (nargs >= 3 && !duk_strict_equals(thr, 0, 2)) {
        /* XXX: [[Get]] receiver currently unsupported */
        DUK_ERROR_UNSUPPORTED(thr);
        DUK_WO_NORETURN(return 0;);
    }

    /* [ target key receiver? ...? ] */

    tv_obj = DUK_GET_TVAL_POSIDX(thr, 0);
    tv_key = DUK_GET_TVAL_POSIDX(thr, 1);
    (void) duk_hobject_getprop(thr, tv_obj, tv_key);  /* This could also be a duk_get_prop(). */
    return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_reflect_object_has(duk_hthread *thr) {
    duk_tval *tv_obj;
    duk_tval *tv_key;
    duk_bool_t ret;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT_TOP(thr, 2);
    (void) duk_require_hobject(thr, 0);
    (void) duk_to_string(thr, 1);

    /* [ target key ] */

    tv_obj = DUK_GET_TVAL_POSIDX(thr, 0);
    tv_key = DUK_GET_TVAL_POSIDX(thr, 1);
    ret = duk_hobject_hasprop(thr, tv_obj, tv_key);
    duk_push_boolean(thr, ret);
    return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_reflect_object_set(duk_hthread *thr) {
    duk_tval *tv_obj;
    duk_tval *tv_key;
    duk_tval *tv_val;
    duk_idx_t nargs;
    duk_bool_t ret;

    DUK_ASSERT(thr != NULL);
    nargs = duk_get_top_require_min(thr, 3 /*min_top*/);
    (void) duk_require_hobject(thr, 0);
    (void) duk_to_string(thr, 1);
    if (nargs >= 4 && !duk_strict_equals(thr, 0, 3)) {
        /* XXX: [[Set]] receiver currently unsupported */
        DUK_ERROR_UNSUPPORTED(thr);
        DUK_WO_NORETURN(return 0;);
    }

    /* [ target key value receiver? ...? ] */

    tv_obj = DUK_GET_TVAL_POSIDX(thr, 0);
    tv_key = DUK_GET_TVAL_POSIDX(thr, 1);
    tv_val = DUK_GET_TVAL_POSIDX(thr, 2);
    ret = duk_hobject_putprop(thr, tv_obj, tv_key, tv_val, 0 /*throw_flag*/);
    duk_push_boolean(thr, ret);
    return 1;
}
#endif  /* DUK_USE_REFLECT_BUILTIN */
//#line 1 "duk_bi_regexp.c"
/*
 *  RegExp built-ins
 */

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

#if defined(DUK_USE_REGEXP_SUPPORT)

DUK_LOCAL void duk__get_this_regexp(duk_hthread *thr) {
    duk_hobject *h;

    duk_push_this(thr);
    h = duk_require_hobject_with_class(thr, -1, DUK_HOBJECT_CLASS_REGEXP);
    DUK_ASSERT(h != NULL);
    DUK_UNREF(h);
    duk_insert(thr, 0);  /* prepend regexp to valstack 0 index */
}

/* XXX: much to improve (code size) */
DUK_INTERNAL duk_ret_t duk_bi_regexp_constructor(duk_hthread *thr) {
    duk_hobject *h_pattern;

    DUK_ASSERT_TOP(thr, 2);
    h_pattern = duk_get_hobject(thr, 0);

    if (!duk_is_constructor_call(thr) &&
        h_pattern != NULL &&
        DUK_HOBJECT_GET_CLASS_NUMBER(h_pattern) == DUK_HOBJECT_CLASS_REGEXP &&
        duk_is_undefined(thr, 1)) {
        /* Called as a function, pattern has [[Class]] "RegExp" and
         * flags is undefined -> return object as is.
         */
        /* XXX: ES2015 has a NewTarget SameValue() check which is not
         * yet implemented.
         */
        duk_dup_0(thr);
        return 1;
    }

    /* Else functionality is identical for function call and constructor
     * call.
     */

    if (h_pattern != NULL &&
        DUK_HOBJECT_GET_CLASS_NUMBER(h_pattern) == DUK_HOBJECT_CLASS_REGEXP) {
        duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_SOURCE);
        if (duk_is_undefined(thr, 1)) {
            /* In ES5 one would need to read the flags individually;
             * in ES2015 just read .flags.
             */
            duk_get_prop_stridx(thr, 0, DUK_STRIDX_FLAGS);
        } else {
            /* In ES2015 allowed; overrides argument RegExp flags. */
            duk_dup_1(thr);
        }
    } else {
        if (duk_is_undefined(thr, 0)) {
            duk_push_hstring_empty(thr);
        } else {
            duk_dup_0(thr);
            duk_to_string(thr, -1);  /* Rejects Symbols. */
        }
        if (duk_is_undefined(thr, 1)) {
            duk_push_hstring_empty(thr);
        } else {
            duk_dup_1(thr);
            duk_to_string(thr, -1);  /* Rejects Symbols. */
        }

        /* [ ... pattern flags ] */
    }

    DUK_DDD(DUK_DDDPRINT("RegExp constructor/function call, pattern=%!T, flags=%!T",
                         (duk_tval *) duk_get_tval(thr, -2), (duk_tval *) duk_get_tval(thr, -1)));

    /* [ ... pattern flags ] (both uncoerced) */

    duk_to_string(thr, -2);
    duk_to_string(thr, -1);
    duk_regexp_compile(thr);

    /* [ ... bytecode escaped_source ] */

    duk_regexp_create_instance(thr);

    /* [ ... RegExp ] */

    return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_exec(duk_hthread *thr) {
    duk__get_this_regexp(thr);

    /* [ regexp input ] */

    duk_regexp_match(thr);

    /* [ result ] */

    return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_test(duk_hthread *thr) {
    duk__get_this_regexp(thr);

    /* [ regexp input ] */

    /* result object is created and discarded; wasteful but saves code space */
    duk_regexp_match(thr);

    /* [ result ] */

    duk_push_boolean(thr, (duk_is_null(thr, -1) ? 0 : 1));

    return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_tostring(duk_hthread *thr) {
    /* This must be generic in ES2015 and later. */
    DUK_ASSERT_TOP(thr, 0);
    duk_push_this(thr);
    duk_push_literal(thr, "/");
    duk_get_prop_stridx(thr, 0, DUK_STRIDX_SOURCE);
    duk_dup_m2(thr);  /* another "/" */
    duk_get_prop_stridx(thr, 0, DUK_STRIDX_FLAGS);
    duk_concat(thr, 4);
    return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_flags(duk_hthread *thr) {
    /* .flags is ES2015 but present even when ES2015 bindings are
     * disabled because the constructor relies on it.
     */
    duk_uint8_t buf[8];  /* enough for all flags + NUL */
    duk_uint8_t *p = buf;

    /* .flags is generic and works on any object. */
    duk_push_this(thr);
    (void) duk_require_hobject(thr, -1);
    if (duk_get_prop_stridx_boolean(thr, 0, DUK_STRIDX_GLOBAL, NULL)) {
        *p++ = DUK_ASC_LC_G;
    }
    if (duk_get_prop_stridx_boolean(thr, 0, DUK_STRIDX_IGNORE_CASE, NULL)) {
        *p++ = DUK_ASC_LC_I;
    }
    if (duk_get_prop_stridx_boolean(thr, 0, DUK_STRIDX_MULTILINE, NULL)) {
        *p++ = DUK_ASC_LC_M;
    }
    /* .unicode: to be added */
    /* .sticky: to be added */
    *p++ = DUK_ASC_NUL;
    DUK_ASSERT((duk_size_t) (p - buf) <= sizeof(buf));

    duk_push_string(thr, (const char *) buf);
    return 1;
}

/* Shared helper for providing .source, .global, .multiline, etc getters. */
DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_shared_getter(duk_hthread *thr) {
    duk_hstring *h_bc;
    duk_small_uint_t re_flags;
    duk_hobject *h;
    duk_int_t magic;

    DUK_ASSERT_TOP(thr, 0);

    duk_push_this(thr);
    h = duk_require_hobject(thr, -1);
    magic = duk_get_current_magic(thr);

    if (DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_REGEXP) {
        duk_xget_owndataprop_stridx_short(thr, 0, DUK_STRIDX_INT_SOURCE);
        duk_xget_owndataprop_stridx_short(thr, 0, DUK_STRIDX_INT_BYTECODE);
        h_bc = duk_require_hstring(thr, -1);
        re_flags = (duk_small_uint_t) DUK_HSTRING_GET_DATA(h_bc)[0];  /* Safe even if h_bc length is 0 (= NUL) */
        duk_pop(thr);
    } else if (h == thr->builtins[DUK_BIDX_REGEXP_PROTOTYPE]) {
        /* In ES2015 and ES2016 a TypeError would be thrown here.
         * However, this had real world issues so ES2017 draft
         * allows RegExp.prototype specifically, returning '(?:)'
         * for .source and undefined for all flags.
         */
        if (magic != 16 /* .source */) {
            return 0;
        }
        duk_push_literal(thr, "(?:)");  /* .source handled by switch-case */
        re_flags = 0;
    } else {
        DUK_DCERROR_TYPE_INVALID_ARGS(thr);
    }

    /* [ regexp source ] */

    switch (magic) {
    case 0: {  /* global */
        duk_push_boolean(thr, (re_flags & DUK_RE_FLAG_GLOBAL));
        break;
    }
    case 1: {  /* ignoreCase */
        duk_push_boolean(thr, (re_flags & DUK_RE_FLAG_IGNORE_CASE));
        break;
    }
    case 2: {  /* multiline */
        duk_push_boolean(thr, (re_flags & DUK_RE_FLAG_MULTILINE));
        break;
    }
#if 0
    /* Don't provide until implemented to avoid interfering with feature
     * detection in user code.
     */
    case 3:    /* sticky */
    case 4: {  /* unicode */
        duk_push_false(thr);
        break;
    }
#endif
    default: {  /* source */
        /* leave 'source' on top */
        break;
    }
    }

    return 1;
}

#endif  /* DUK_USE_REGEXP_SUPPORT */
//#line 1 "duk_bi_string.c"
/*
 *  String built-ins
 *
 *  Most String built-ins must only accept strings (or String objects).
 *  Symbols, represented internally as strings, must be generally rejected.
 *  The duk_push_this_coercible_to_string() helper does this automatically.
 */

/* XXX: There are several limitations in the current implementation for
 * strings with >= 0x80000000UL characters.  In some cases one would need
 * to be able to represent the range [-0xffffffff,0xffffffff] and so on.
 * Generally character and byte length are assumed to fit into signed 32
 * bits (< 0x80000000UL).  Places with issues are not marked explicitly
 * below in all cases, look for signed type usage (duk_int_t etc) for
 * offsets/lengths.
 */

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

#if defined(DUK_USE_STRING_BUILTIN)

/*
 *  Helpers
 */

DUK_LOCAL duk_hstring *duk__str_tostring_notregexp(duk_hthread *thr, duk_idx_t idx) {
    duk_hstring *h;

    if (duk_get_class_number(thr, idx) == DUK_HOBJECT_CLASS_REGEXP) {
        DUK_ERROR_TYPE_INVALID_ARGS(thr);
        DUK_WO_NORETURN(return NULL;);
    }
    h = duk_to_hstring(thr, idx);
    DUK_ASSERT(h != NULL);

    return h;
}

DUK_LOCAL duk_int_t duk__str_search_shared(duk_hthread *thr, duk_hstring *h_this, duk_hstring *h_search, duk_int_t start_cpos, duk_bool_t backwards) {
    duk_int_t cpos;
    duk_int_t bpos;
    const duk_uint8_t *p_start, *p_end, *p;
    const duk_uint8_t *q_start;
    duk_int_t q_blen;
    duk_uint8_t firstbyte;
    duk_uint8_t t;

    cpos = start_cpos;

    /* Empty searchstring always matches; cpos must be clamped here.
     * (If q_blen were < 0 due to clamped coercion, it would also be
     * caught here.)
     */
    q_start = DUK_HSTRING_GET_DATA(h_search);
    q_blen = (duk_int_t) DUK_HSTRING_GET_BYTELEN(h_search);
    if (q_blen <= 0) {
        return cpos;
    }
    DUK_ASSERT(q_blen > 0);

    bpos = (duk_int_t) duk_heap_strcache_offset_char2byte(thr, h_this, (duk_uint32_t) cpos);

    p_start = DUK_HSTRING_GET_DATA(h_this);
    p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_this);
    p = p_start + bpos;

    /* This loop is optimized for size.  For speed, there should be
     * two separate loops, and we should ensure that memcmp() can be
     * used without an extra "will searchstring fit" check.  Doing
     * the preconditioning for 'p' and 'p_end' is easy but cpos
     * must be updated if 'p' is wound back (backward scanning).
     */

    firstbyte = q_start[0];  /* leading byte of match string */
    while (p <= p_end && p >= p_start) {
        t = *p;

        /* For ECMAScript strings, this check can only match for
         * initial UTF-8 bytes (not continuation bytes).  For other
         * strings all bets are off.
         */

        if ((t == firstbyte) && ((duk_size_t) (p_end - p) >= (duk_size_t) q_blen)) {
            DUK_ASSERT(q_blen > 0);
            if (duk_memcmp((const void *) p, (const void *) q_start, (size_t) q_blen) == 0) {
                return cpos;
            }
        }

        /* track cpos while scanning */
        if (backwards) {
            /* when going backwards, we decrement cpos 'early';
             * 'p' may point to a continuation byte of the char
             * at offset 'cpos', but that's OK because we'll
             * backtrack all the way to the initial byte.
             */
            if ((t & 0xc0) != 0x80) {
                cpos--;
            }
            p--;
        } else {
            if ((t & 0xc0) != 0x80) {
                cpos++;
            }
            p++;
        }
    }

    /* Not found.  Empty string case is handled specially above. */
    return -1;
}

/*
 *  Constructor
 */

DUK_INTERNAL duk_ret_t duk_bi_string_constructor(duk_hthread *thr) {
    duk_hstring *h;
    duk_uint_t flags;

    /* String constructor needs to distinguish between an argument not given at all
     * vs. given as 'undefined'.  We're a vararg function to handle this properly.
     */

    /* XXX: copy current activation flags to thr, including current magic,
     * is_constructor_call etc.  This takes a few bytes in duk_hthread but
     * makes call sites smaller (there are >30 is_constructor_call and get
     * current magic call sites.
     */

    if (duk_get_top(thr) == 0) {
        duk_push_hstring_empty(thr);
    } else {
        h = duk_to_hstring_acceptsymbol(thr, 0);
        if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h) && !duk_is_constructor_call(thr))) {
            duk_push_symbol_descriptive_string(thr, h);
            duk_replace(thr, 0);
        }
    }
    duk_to_string(thr, 0);  /* catches symbol argument for constructor call */
    DUK_ASSERT(duk_is_string(thr, 0));
    duk_set_top(thr, 1);  /* Top may be 1 or larger. */

    if (duk_is_constructor_call(thr)) {
        /* String object internal value is immutable */
        flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
                DUK_HOBJECT_FLAG_FASTREFS |
                DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ |
                DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_STRING);
        duk_push_object_helper(thr, flags, DUK_BIDX_STRING_PROTOTYPE);
        duk_dup_0(thr);
        duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE);
    }
    /* Note: unbalanced stack on purpose */

    return 1;
}

DUK_LOCAL duk_ret_t duk__construct_from_codepoints(duk_hthread *thr, duk_bool_t nonbmp) {
    duk_bufwriter_ctx bw_alloc;
    duk_bufwriter_ctx *bw;
    duk_idx_t i, n;
    duk_ucodepoint_t cp;

    /* XXX: It would be nice to build the string directly but ToUint16()
     * coercion is needed so a generic helper would not be very
     * helpful (perhaps coerce the value stack first here and then
     * build a string from a duk_tval number sequence in one go?).
     */

    n = duk_get_top(thr);

    bw = &bw_alloc;
    DUK_BW_INIT_PUSHBUF(thr, bw, (duk_size_t) n);  /* initial estimate for ASCII only codepoints */

    for (i = 0; i < n; i++) {
        /* XXX: could improve bufwriter handling to write multiple codepoints
         * with one ensure call but the relative benefit would be quite small.
         */

        if (nonbmp) {
            /* ES2015 requires that (1) SameValue(cp, ToInteger(cp)) and
             * (2) cp >= 0 and cp <= 0x10ffff.  This check does not
             * implement the steps exactly but the outcome should be
             * the same.
             */
            duk_int32_t i32 = 0;
            if (!duk_is_whole_get_int32(duk_to_number(thr, i), &i32) ||
                i32 < 0 || i32 > 0x10ffffL) {
                DUK_DCERROR_RANGE_INVALID_ARGS(thr);
            }
            DUK_ASSERT(i32 >= 0 && i32 <= 0x10ffffL);
            cp = (duk_ucodepoint_t) i32;
            DUK_BW_WRITE_ENSURE_CESU8(thr, bw, cp);
        } else {
#if defined(DUK_USE_NONSTD_STRING_FROMCHARCODE_32BIT)
            /* ToUint16() coercion is mandatory in the E5.1 specification, but
             * this non-compliant behavior makes more sense because we support
             * non-BMP codepoints.  Don't use CESU-8 because that'd create
             * surrogate pairs.
             */
            cp = (duk_ucodepoint_t) duk_to_uint32(thr, i);
            DUK_BW_WRITE_ENSURE_XUTF8(thr, bw, cp);
#else
            cp = (duk_ucodepoint_t) duk_to_uint16(thr, i);
            DUK_ASSERT(cp >= 0 && cp <= 0x10ffffL);
            DUK_BW_WRITE_ENSURE_CESU8(thr, bw, cp);
#endif
        }
    }

    DUK_BW_COMPACT(thr, bw);
    (void) duk_buffer_to_string(thr, -1);  /* Safe, extended UTF-8 or CESU-8 encoded. */
    return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_string_constructor_from_char_code(duk_hthread *thr) {
    return duk__construct_from_codepoints(thr, 0 /*nonbmp*/);
}

#if defined(DUK_USE_ES6)
DUK_INTERNAL duk_ret_t duk_bi_string_constructor_from_code_point(duk_hthread *thr) {
    return duk__construct_from_codepoints(thr, 1 /*nonbmp*/);
}
#endif

/*
 *  toString(), valueOf()
 */

DUK_INTERNAL duk_ret_t duk_bi_string_prototype_to_string(duk_hthread *thr) {
    duk_tval *tv;

    duk_push_this(thr);
    tv = duk_require_tval(thr, -1);
    DUK_ASSERT(tv != NULL);

    if (DUK_TVAL_IS_STRING(tv)) {
        /* return as is */
    } else if (DUK_TVAL_IS_OBJECT(tv)) {
        duk_hobject *h = DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(h != NULL);

        /* Must be a "string object", i.e. class "String" */
        if (DUK_HOBJECT_GET_CLASS_NUMBER(h) != DUK_HOBJECT_CLASS_STRING) {
            goto type_error;
        }

        duk_xget_owndataprop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE);
        DUK_ASSERT(duk_is_string(thr, -1));
    } else {
        goto type_error;
    }

    (void) duk_require_hstring_notsymbol(thr, -1);  /* Reject symbols (and wrapped symbols). */
    return 1;

 type_error:
    DUK_DCERROR_TYPE_INVALID_ARGS(thr);
}

/*
 *  Character and charcode access
 */

DUK_INTERNAL duk_ret_t duk_bi_string_prototype_char_at(duk_hthread *thr) {
    duk_hstring *h;
    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;

    /* Unlike non-obsolete String calls, substr() algorithm in E5.1
     * specification will happily coerce undefined and null to strings
     * ("undefined" and "null").
     */
    duk_push_this(thr);
    h = duk_to_hstring_m1(thr);  /* Reject Symbols. */
    DUK_ASSERT(h != NULL);
    len = (duk_int_t) DUK_HSTRING_GET_CHARLEN(h);

    /* [ start length str ] */

    /* The implementation for computing of start_pos and end_pos differs
     * from the standard algorithm, but is intended to result in the exactly
     * same behavior.  This is not always obvious.
     */

    /* combines steps 2 and 5; -len ensures max() not needed for step 5 */
    start_pos = duk_to_int_clamped(thr, 0, -len, len);
    if (start_pos < 0) {
        start_pos = len + start_pos;
    }
    DUK_ASSERT(start_pos >= 0 && start_pos <= len);

    /* combines steps 3, 6; step 7 is not needed */
    if (duk_is_undefined(thr, 1)) {
        end_pos = len;
    } else {
        DUK_ASSERT(start_pos <= len);
        end_pos = start_pos + duk_to_int_clamped(thr, 1, 0, len - start_pos);
    }
    DUK_ASSERT(start_pos >= 0 && start_pos <= len);
    DUK_ASSERT(end_pos >= 0 && end_pos <= len);
    DUK_ASSERT(end_pos >= start_pos);

    duk_substring(thr, -1, (duk_size_t) start_pos, (duk_size_t) end_pos);
    return 1;
}
#endif  /* DUK_USE_SECTION_B */

DUK_INTERNAL duk_ret_t duk_bi_string_prototype_slice(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, -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
     */

    h_re = duk_get_hobject_with_class(thr, 0, DUK_HOBJECT_CLASS_REGEXP);
    if (h_re) {
#if defined(DUK_USE_REGEXP_SUPPORT)
        is_regexp = 1;
        is_global = duk_get_prop_stridx_boolean(thr, 0, DUK_STRIDX_GLOBAL, NULL);

        if (is_global) {
            /* start match from beginning */
            duk_push_int(thr, 0);
            duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX);
        }
#else  /* DUK_USE_REGEXP_SUPPORT */
        DUK_DCERROR_UNSUPPORTED(thr);
#endif  /* DUK_USE_REGEXP_SUPPORT */
    } else {
        duk_to_string(thr, 0);  /* rejects symbols */
#if defined(DUK_USE_REGEXP_SUPPORT)
        is_regexp = 0;
        is_global = 0;
#endif
    }

    if (duk_is_function(thr, 1)) {
        is_repl_func = 1;
        r_start = NULL;
        r_end = NULL;
    } else {
        duk_hstring *h_repl;

        is_repl_func = 0;
        h_repl = duk_to_hstring(thr, 1);  /* reject symbols */
        DUK_ASSERT(h_repl != NULL);
        r_start = DUK_HSTRING_GET_DATA(h_repl);
        r_end = r_start + DUK_HSTRING_GET_BYTELEN(h_repl);
    }

    prev_match_end_boff = 0;

    for (;;) {
        /*
         *  If matching with a regexp:
         *    - non-global RegExp: lastIndex not touched on a match, zeroed
         *      on a non-match
         *    - global RegExp: on match, lastIndex will be updated by regexp
         *      executor to point to next char after the matching part (so that
         *      characters in the matching part are not matched again)
         *
         *  If matching with a string:
         *    - always non-global match, find first occurrence
         *
         *  We need:
         *    - The character offset of start-of-match for the replacer function
         *    - The byte offsets for start-of-match and end-of-match to implement
         *      the replacement values $&, $`, and $', and to copy non-matching
         *      input string portions (including header and trailer) verbatim.
         *
         *  NOTE: the E5.1 specification is a bit vague how the RegExp should
         *  behave in the replacement process; e.g. is matching done first for
         *  all matches (in the global RegExp case) before any replacer calls
         *  are made?  See: test-bi-string-proto-replace.js for discussion.
         */

        DUK_ASSERT_TOP(thr, 4);

#if defined(DUK_USE_REGEXP_SUPPORT)
        if (is_regexp) {
            duk_dup_0(thr);
            duk_dup_2(thr);
            duk_regexp_match(thr);  /* [ ... regexp input ] -> [ res_obj ] */
            if (!duk_is_object(thr, -1)) {
                duk_pop(thr);
                break;
            }

            duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INDEX);
            DUK_ASSERT(duk_is_number(thr, -1));
            match_start_coff = duk_get_uint(thr, -1);
            duk_pop(thr);

            duk_get_prop_index(thr, -1, 0);
            DUK_ASSERT(duk_is_string(thr, -1));
            h_match = duk_known_hstring(thr, -1);
            duk_pop(thr);  /* h_match is borrowed, remains reachable through match_obj */

            if (DUK_HSTRING_GET_BYTELEN(h_match) == 0) {
                /* This should be equivalent to match() algorithm step 8.f.iii.2:
                 * detect an empty match and allow it, but don't allow it twice.
                 */
                duk_uint32_t last_index;

                duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX);
                last_index = (duk_uint32_t) duk_get_uint(thr, -1);
                DUK_DDD(DUK_DDDPRINT("empty match, bump lastIndex: %ld -> %ld",
                                     (long) last_index, (long) (last_index + 1)));
                duk_pop(thr);
                duk_push_uint(thr, (duk_uint_t) (last_index + 1));
                duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX);
            }

            DUK_ASSERT(duk_get_length(thr, -1) <= DUK_INT_MAX);  /* string limits */
            match_caps = (duk_int_t) duk_get_length(thr, -1);
        } else {
#else  /* DUK_USE_REGEXP_SUPPORT */
        {  /* unconditionally */
#endif  /* DUK_USE_REGEXP_SUPPORT */
            const duk_uint8_t *p_start, *p_end, *p;   /* input string scan */
            const duk_uint8_t *q_start;               /* match string */
            duk_size_t p_blen;
            duk_size_t q_blen;

#if defined(DUK_USE_REGEXP_SUPPORT)
            DUK_ASSERT(!is_global);  /* single match always */
#endif

            p_start = DUK_HSTRING_GET_DATA(h_input);
            p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_input);
            p_blen = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_input);
            p = p_start;

            h_search = duk_known_hstring(thr, 0);
            q_start = DUK_HSTRING_GET_DATA(h_search);
            q_blen = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_search);

            if (q_blen > p_blen) {
                break;  /* no match */
            }

            p_end -= q_blen;  /* ensure full memcmp() fits in while */
            DUK_ASSERT(p_end >= p);

            match_start_coff = 0;

            while (p <= p_end) {
                DUK_ASSERT(p + q_blen <= DUK_HSTRING_GET_DATA(h_input) + DUK_HSTRING_GET_BYTELEN(h_input));
                if (duk_memcmp((const void *) p, (const void *) q_start, (size_t) q_blen) == 0) {
                    duk_dup_0(thr);
                    h_match = duk_known_hstring(thr, -1);
#if defined(DUK_USE_REGEXP_SUPPORT)
                    match_caps = 0;
#endif
                    goto found;
                }

                /* track utf-8 non-continuation bytes */
                if ((p[0] & 0xc0) != 0x80) {
                    match_start_coff++;
                }
                p++;
            }

            /* not found */
            break;
        }
     found:

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

        match_start_boff = (duk_uint32_t) duk_heap_strcache_offset_char2byte(thr, h_input, match_start_coff);

        tmp_sz = (duk_size_t) (match_start_boff - prev_match_end_boff);
        DUK_BW_WRITE_ENSURE_BYTES(thr, bw, DUK_HSTRING_GET_DATA(h_input) + prev_match_end_boff, tmp_sz);

        prev_match_end_boff = match_start_boff + DUK_HSTRING_GET_BYTELEN(h_match);

        if (is_repl_func) {
            duk_idx_t idx_args;
            duk_hstring *h_repl;

            /* regexp res_obj is at index 4 */

            duk_dup_1(thr);
            idx_args = duk_get_top(thr);

#if defined(DUK_USE_REGEXP_SUPPORT)
            if (is_regexp) {
                duk_int_t idx;
                duk_require_stack(thr, match_caps + 2);
                for (idx = 0; idx < match_caps; idx++) {
                    /* match followed by capture(s) */
                    duk_get_prop_index(thr, 4, (duk_uarridx_t) idx);
                }
            } else {
#else  /* DUK_USE_REGEXP_SUPPORT */
            {  /* unconditionally */
#endif  /* DUK_USE_REGEXP_SUPPORT */
                /* match == search string, by definition */
                duk_dup_0(thr);
            }
            duk_push_uint(thr, (duk_uint_t) match_start_coff);
            duk_dup_2(thr);

            /* [ ... replacer match [captures] match_char_offset input ] */

            duk_call(thr, duk_get_top(thr) - idx_args);
            h_repl = duk_to_hstring_m1(thr);  /* -> [ ... repl_value ] */
            DUK_ASSERT(h_repl != NULL);

            DUK_BW_WRITE_ENSURE_HSTRING(thr, bw, h_repl);

            duk_pop(thr);  /* repl_value */
        } else {
            r = r_start;

            while (r < r_end) {
                duk_int_t ch1;
                duk_int_t ch2;
#if defined(DUK_USE_REGEXP_SUPPORT)
                duk_int_t ch3;
#endif
                duk_size_t left;

                ch1 = *r++;
                if (ch1 != DUK_ASC_DOLLAR) {
                    goto repl_write;
                }
                DUK_ASSERT(r <= r_end);
                left = (duk_size_t) (r_end - r);

                if (left <= 0) {
                    goto repl_write;
                }

                ch2 = r[0];
                switch (ch2) {
                case DUK_ASC_DOLLAR: {
                    ch1 = (1 << 8) + DUK_ASC_DOLLAR;
                    goto repl_write;
                }
                case DUK_ASC_AMP: {
                    DUK_BW_WRITE_ENSURE_HSTRING(thr, bw, h_match);
                    r++;
                    continue;
                }
                case DUK_ASC_GRAVE: {
                    tmp_sz = (duk_size_t) match_start_boff;
                    DUK_BW_WRITE_ENSURE_BYTES(thr, bw, DUK_HSTRING_GET_DATA(h_input), tmp_sz);
                    r++;
                    continue;
                }
                case DUK_ASC_SINGLEQUOTE: {
                    duk_uint32_t match_end_boff;

                    /* Use match charlen instead of bytelen, just in case the input and
                     * match codepoint encodings would have different lengths.
                     */
                    /* XXX: charlen computed here, and also in char2byte helper. */
                    match_end_boff = (duk_uint32_t) duk_heap_strcache_offset_char2byte(thr,
                                                                                       h_input,
                                                                                       match_start_coff + (duk_uint_fast32_t) DUK_HSTRING_GET_CHARLEN(h_match));

                    tmp_sz = (duk_size_t) (DUK_HSTRING_GET_BYTELEN(h_input) - match_end_boff);
                    DUK_BW_WRITE_ENSURE_BYTES(thr, bw, DUK_HSTRING_GET_DATA(h_input) + match_end_boff, tmp_sz);
                    r++;
                    continue;
                }
                default: {
#if defined(DUK_USE_REGEXP_SUPPORT)
                    duk_int_t capnum, captmp, capadv;
                    /* XXX: optional check, match_caps is zero if no regexp,
                     * so dollar will be interpreted literally anyway.
                     */

                    if (!is_regexp) {
                        goto repl_write;
                    }

                    if (!(ch2 >= DUK_ASC_0 && ch2 <= DUK_ASC_9)) {
                        goto repl_write;
                    }
                    capnum = ch2 - DUK_ASC_0;
                    capadv = 1;

                    if (left >= 2) {
                        ch3 = r[1];
                        if (ch3 >= DUK_ASC_0 && ch3 <= DUK_ASC_9) {
                            captmp = capnum * 10 + (ch3 - DUK_ASC_0);
                            if (captmp < match_caps) {
                                capnum = captmp;
                                capadv = 2;
                            }
                        }
                    }

                    if (capnum > 0 && capnum < match_caps) {
                        DUK_ASSERT(is_regexp != 0);  /* match_caps == 0 without regexps */

                        /* regexp res_obj is at offset 4 */
                        duk_get_prop_index(thr, 4, (duk_uarridx_t) capnum);
                        if (duk_is_string(thr, -1)) {
                            duk_hstring *h_tmp_str;

                            h_tmp_str = duk_known_hstring(thr, -1);

                            DUK_BW_WRITE_ENSURE_HSTRING(thr, bw, h_tmp_str);
                        } else {
                            /* undefined -> skip (replaced with empty) */
                        }
                        duk_pop(thr);
                        r += capadv;
                        continue;
                    } else {
                        goto repl_write;
                    }
#else  /* DUK_USE_REGEXP_SUPPORT */
                    goto repl_write;  /* unconditionally */
#endif  /* DUK_USE_REGEXP_SUPPORT */
                }  /* default case */
                }  /* switch (ch2) */

             repl_write:
                /* ch1 = (r_increment << 8) + byte */

                DUK_BW_WRITE_ENSURE_U8(thr, bw, (duk_uint8_t) (ch1 & 0xff));
                r += ch1 >> 8;
            }  /* while repl */
        }  /* if (is_repl_func) */

        duk_pop(thr);  /* pop regexp res_obj or match string */

#if defined(DUK_USE_REGEXP_SUPPORT)
        if (!is_global) {
#else
        {  /* unconditionally; is_global==0 */
#endif
            break;
        }
    }

    /* trailer */
    tmp_sz = (duk_size_t) (DUK_HSTRING_GET_BYTELEN(h_input) - prev_match_end_boff);
    DUK_BW_WRITE_ENSURE_BYTES(thr, bw, DUK_HSTRING_GET_DATA(h_input) + prev_match_end_boff, tmp_sz);

    DUK_ASSERT_TOP(thr, 4);
    DUK_BW_COMPACT(thr, bw);
    (void) duk_buffer_to_string(thr, -1);  /* Safe if inputs are safe. */
    return 1;
}

/*
 *  split()
 */

/* XXX: very messy now, but works; clean up, remove unused variables (nomimally
 * used so compiler doesn't complain).
 */

DUK_INTERNAL duk_ret_t duk_bi_string_prototype_split(duk_hthread *thr) {
    duk_hstring *h_input;
    duk_hstring *h_sep;
    duk_uint32_t limit;
    duk_uint32_t arr_idx;
#if defined(DUK_USE_REGEXP_SUPPORT)
    duk_bool_t is_regexp;
#endif
    duk_bool_t matched;  /* set to 1 if any match exists (needed for empty input special case) */
    duk_uint32_t prev_match_end_coff, prev_match_end_boff;
    duk_uint32_t match_start_boff, match_start_coff;
    duk_uint32_t match_end_boff, match_end_coff;

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

    duk_push_array(thr);

    if (duk_is_undefined(thr, 1)) {
        limit = 0xffffffffUL;
    } else {
        limit = duk_to_uint32(thr, 1);
    }

    if (limit == 0) {
        return 1;
    }

    /* If the separator is a RegExp, make a "clone" of it.  The specification
     * algorithm calls [[Match]] directly for specific indices; we emulate this
     * by tweaking lastIndex and using a "force global" variant of duk_regexp_match()
     * which will use global-style matching even when the RegExp itself is non-global.
     */

    if (duk_is_undefined(thr, 0)) {
        /* The spec algorithm first does "R = ToString(separator)" before checking
         * whether separator is undefined.  Since this is side effect free, we can
         * skip the ToString() here.
         */
        duk_dup_2(thr);
        duk_put_prop_index(thr, 3, 0);
        return 1;
    } else if (duk_get_hobject_with_class(thr, 0, DUK_HOBJECT_CLASS_REGEXP) != NULL) {
#if defined(DUK_USE_REGEXP_SUPPORT)
        duk_push_hobject_bidx(thr, DUK_BIDX_REGEXP_CONSTRUCTOR);
        duk_dup_0(thr);
        duk_new(thr, 1);  /* [ ... RegExp val ] -> [ ... res ] */
        duk_replace(thr, 0);
        /* lastIndex is initialized to zero by new RegExp() */
        is_regexp = 1;
#else
        DUK_DCERROR_UNSUPPORTED(thr);
#endif
    } else {
        duk_to_string(thr, 0);
#if defined(DUK_USE_REGEXP_SUPPORT)
        is_regexp = 0;
#endif
    }

    /* stack[0] = separator (string or regexp)
     * stack[1] = limit
     * stack[2] = input string
     * stack[3] = result array
     */

    prev_match_end_boff = 0;
    prev_match_end_coff = 0;
    arr_idx = 0;
    matched = 0;

    for (;;) {
        /*
         *  The specification uses RegExp [[Match]] to attempt match at specific
         *  offsets.  We don't have such a primitive, so we use an actual RegExp
         *  and tweak lastIndex.  Since the RegExp may be non-global, we use a
         *  special variant which forces global-like behavior for matching.
         */

        DUK_ASSERT_TOP(thr, 4);

#if defined(DUK_USE_REGEXP_SUPPORT)
        if (is_regexp) {
            duk_dup_0(thr);
            duk_dup_2(thr);
            duk_regexp_match_force_global(thr);  /* [ ... regexp input ] -> [ res_obj ] */
            if (!duk_is_object(thr, -1)) {
                duk_pop(thr);
                break;
            }
            matched = 1;

            duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INDEX);
            DUK_ASSERT(duk_is_number(thr, -1));
            match_start_coff = duk_get_uint(thr, -1);
            match_start_boff = (duk_uint32_t) duk_heap_strcache_offset_char2byte(thr, h_input, match_start_coff);
            duk_pop(thr);

            if (match_start_coff == DUK_HSTRING_GET_CHARLEN(h_input)) {
                /* don't allow an empty match at the end of the string */
                duk_pop(thr);
                break;
            }

            duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX);
            DUK_ASSERT(duk_is_number(thr, -1));
            match_end_coff = duk_get_uint(thr, -1);
            match_end_boff = (duk_uint32_t) duk_heap_strcache_offset_char2byte(thr, h_input, match_end_coff);
            duk_pop(thr);

            /* empty match -> bump and continue */
            if (prev_match_end_boff == match_end_boff) {
                duk_push_uint(thr, (duk_uint_t) (match_end_coff + 1));
                duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX);
                duk_pop(thr);
                continue;
            }
        } else {
#else  /* DUK_USE_REGEXP_SUPPORT */
        {  /* unconditionally */
#endif  /* DUK_USE_REGEXP_SUPPORT */
            const duk_uint8_t *p_start, *p_end, *p;   /* input string scan */
            const duk_uint8_t *q_start;               /* match string */
            duk_size_t q_blen, q_clen;

            p_start = DUK_HSTRING_GET_DATA(h_input);
            p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_input);
            p = p_start + prev_match_end_boff;

            h_sep = duk_known_hstring(thr, 0);  /* symbol already rejected above */
            q_start = DUK_HSTRING_GET_DATA(h_sep);
            q_blen = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_sep);
            q_clen = (duk_size_t) DUK_HSTRING_GET_CHARLEN(h_sep);

            p_end -= q_blen;  /* ensure full memcmp() fits in while */

            match_start_coff = prev_match_end_coff;

            if (q_blen == 0) {
                /* Handle empty separator case: it will always match, and always
                 * triggers the check in step 13.c.iii initially.  Note that we
                 * must skip to either end of string or start of first codepoint,
                 * skipping over any continuation bytes!
                 *
                 * Don't allow an empty string to match at the end of the input.
                 */

                matched = 1;  /* empty separator can always match */

                match_start_coff++;
                p++;
                while (p < p_end) {
                    if ((p[0] & 0xc0) != 0x80) {
                        goto found;
                    }
                    p++;
                }
                goto not_found;
            }

            DUK_ASSERT(q_blen > 0 && q_clen > 0);
            while (p <= p_end) {
                DUK_ASSERT(p + q_blen <= DUK_HSTRING_GET_DATA(h_input) + DUK_HSTRING_GET_BYTELEN(h_input));
                DUK_ASSERT(q_blen > 0);  /* no issues with empty memcmp() */
                if (duk_memcmp((const void *) p, (const void *) q_start, (size_t) q_blen) == 0) {
                    /* never an empty match, so step 13.c.iii can't be triggered */
                    goto found;
                }

                /* track utf-8 non-continuation bytes */
                if ((p[0] & 0xc0) != 0x80) {
                    match_start_coff++;
                }
                p++;
            }

         not_found:
            /* not found */
            break;

         found:
            matched = 1;
            match_start_boff = (duk_uint32_t) (p - p_start);
            match_end_coff = (duk_uint32_t) (match_start_coff + q_clen);  /* constrained by string length */
            match_end_boff = (duk_uint32_t) (match_start_boff + q_blen);  /* ditto */

            /* empty match (may happen with empty separator) -> bump and continue */
            if (prev_match_end_boff == match_end_boff) {
                prev_match_end_boff++;
                prev_match_end_coff++;
                continue;
            }
        }  /* if (is_regexp) */

        /* stack[0] = separator (string or regexp)
         * stack[1] = limit
         * stack[2] = input string
         * stack[3] = result array
         * stack[4] = regexp res_obj (if is_regexp)
         */

        DUK_DDD(DUK_DDDPRINT("split; match_start b=%ld,c=%ld, match_end b=%ld,c=%ld, prev_end b=%ld,c=%ld",
                             (long) match_start_boff, (long) match_start_coff,
                             (long) match_end_boff, (long) match_end_coff,
                             (long) prev_match_end_boff, (long) prev_match_end_coff));

        duk_push_lstring(thr,
                         (const char *) (DUK_HSTRING_GET_DATA(h_input) + prev_match_end_boff),
                         (duk_size_t) (match_start_boff - prev_match_end_boff));
        duk_put_prop_index(thr, 3, arr_idx);
        arr_idx++;
        if (arr_idx >= limit) {
            goto hit_limit;
        }

#if defined(DUK_USE_REGEXP_SUPPORT)
        if (is_regexp) {
            duk_size_t i, len;

            len = duk_get_length(thr, 4);
            for (i = 1; i < len; i++) {
                DUK_ASSERT(i <= DUK_UARRIDX_MAX);  /* cannot have >4G captures */
                duk_get_prop_index(thr, 4, (duk_uarridx_t) i);
                duk_put_prop_index(thr, 3, arr_idx);
                arr_idx++;
                if (arr_idx >= limit) {
                    goto hit_limit;
                }
            }

            duk_pop(thr);
            /* lastIndex already set up for next match */
        } else {
#else  /* DUK_USE_REGEXP_SUPPORT */
        {  /* unconditionally */
#endif  /* DUK_USE_REGEXP_SUPPORT */
            /* no action */
        }

        prev_match_end_boff = match_end_boff;
        prev_match_end_coff = match_end_coff;
        continue;
    }  /* for */

    /* Combined step 11 (empty string special case) and 14-15. */

    DUK_DDD(DUK_DDDPRINT("split trailer; prev_end b=%ld,c=%ld",
                         (long) prev_match_end_boff, (long) prev_match_end_coff));

    if (DUK_HSTRING_GET_BYTELEN(h_input) > 0 || !matched) {
        /* Add trailer if:
         *   a) non-empty input
         *   b) empty input and no (zero size) match found (step 11)
         */

        duk_push_lstring(thr,
                         (const char *) DUK_HSTRING_GET_DATA(h_input) + prev_match_end_boff,
                         (duk_size_t) (DUK_HSTRING_GET_BYTELEN(h_input) - prev_match_end_boff));
        duk_put_prop_index(thr, 3, arr_idx);
        /* No arr_idx update or limit check */
    }

    return 1;

 hit_limit:
#if defined(DUK_USE_REGEXP_SUPPORT)
    if (is_regexp) {
        duk_pop(thr);
    }
#endif

    return 1;
}

/*
 *  Various
 */

#if defined(DUK_USE_REGEXP_SUPPORT)
DUK_LOCAL void duk__to_regexp_helper(duk_hthread *thr, duk_idx_t idx, duk_bool_t force_new) {
    duk_hobject *h;

    /* Shared helper for match() steps 3-4, search() steps 3-4. */

    DUK_ASSERT(idx >= 0);

    if (force_new) {
        goto do_new;
    }

    h = duk_get_hobject_with_class(thr, idx, DUK_HOBJECT_CLASS_REGEXP);
    if (!h) {
        goto do_new;
    }
    return;

 do_new:
    duk_push_hobject_bidx(thr, DUK_BIDX_REGEXP_CONSTRUCTOR);
    duk_dup(thr, idx);
    duk_new(thr, 1);  /* [ ... RegExp val ] -> [ ... res ] */
    duk_replace(thr, idx);
}
#endif  /* DUK_USE_REGEXP_SUPPORT */

#if defined(DUK_USE_REGEXP_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_string_prototype_search(duk_hthread *thr) {
    /* Easiest way to implement the search required by the specification
     * is to do a RegExp test() with lastIndex forced to zero.  To avoid
     * side effects on the argument, "clone" the RegExp if a RegExp was
     * given as input.
     *
     * The global flag of the RegExp should be ignored; setting lastIndex
     * to zero (which happens when "cloning" the RegExp) should have an
     * equivalent effect.
     */

    DUK_ASSERT_TOP(thr, 1);
    (void) duk_push_this_coercible_to_string(thr);  /* at index 1 */
    duk__to_regexp_helper(thr, 0 /*index*/, 1 /*force_new*/);

    /* stack[0] = regexp
     * stack[1] = string
     */

    /* Avoid using RegExp.prototype methods, as they're writable and
     * configurable and may have been changed.
     */

    duk_dup_0(thr);
    duk_dup_1(thr);  /* [ ... re_obj input ] */
    duk_regexp_match(thr);  /* -> [ ... res_obj ] */

    if (!duk_is_object(thr, -1)) {
        duk_push_int(thr, -1);
        return 1;
    }

    duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INDEX);
    DUK_ASSERT(duk_is_number(thr, -1));
    return 1;
}
#endif  /* DUK_USE_REGEXP_SUPPORT */

#if defined(DUK_USE_REGEXP_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_string_prototype_match(duk_hthread *thr) {
    duk_bool_t global;
    duk_int_t prev_last_index;
    duk_int_t this_index;
    duk_int_t arr_idx;

    DUK_ASSERT_TOP(thr, 1);
    (void) duk_push_this_coercible_to_string(thr);
    duk__to_regexp_helper(thr, 0 /*index*/, 0 /*force_new*/);
    global = duk_get_prop_stridx_boolean(thr, 0, DUK_STRIDX_GLOBAL, NULL);
    DUK_ASSERT_TOP(thr, 2);

    /* stack[0] = regexp
     * stack[1] = string
     */

    if (!global) {
        duk_regexp_match(thr);  /* -> [ res_obj ] */
        return 1;  /* return 'res_obj' */
    }

    /* Global case is more complex. */

    /* [ regexp string ] */

    duk_push_int(thr, 0);
    duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX);
    duk_push_array(thr);

    /* [ regexp string res_arr ] */

    prev_last_index = 0;
    arr_idx = 0;

    for (;;) {
        DUK_ASSERT_TOP(thr, 3);

        duk_dup_0(thr);
        duk_dup_1(thr);
        duk_regexp_match(thr);  /* -> [ ... regexp string ] -> [ ... res_obj ] */

        if (!duk_is_object(thr, -1)) {
            duk_pop(thr);
            break;
        }

        duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX);
        DUK_ASSERT(duk_is_number(thr, -1));
        this_index = duk_get_int(thr, -1);
        duk_pop(thr);

        if (this_index == prev_last_index) {
            this_index++;
            duk_push_int(thr, this_index);
            duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX);
        }
        prev_last_index = this_index;

        duk_get_prop_index(thr, -1, 0);  /* match string */
        duk_put_prop_index(thr, 2, (duk_uarridx_t) arr_idx);
        arr_idx++;
        duk_pop(thr);  /* res_obj */
    }

    if (arr_idx == 0) {
        duk_push_null(thr);
    }

    return 1;  /* return 'res_arr' or 'null' */
}
#endif  /* DUK_USE_REGEXP_SUPPORT */

DUK_INTERNAL duk_ret_t duk_bi_string_prototype_concat(duk_hthread *thr) {
    /* duk_concat() coerces arguments with ToString() in correct order */
    (void) duk_push_this_coercible_to_string(thr);
    duk_insert(thr, 0);  /* this is relatively expensive */
    duk_concat(thr, duk_get_top(thr));
    return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_string_prototype_trim(duk_hthread *thr) {
    DUK_ASSERT_TOP(thr, 0);
    (void) duk_push_this_coercible_to_string(thr);
    duk_trim(thr, 0);
    DUK_ASSERT_TOP(thr, 1);
    return 1;
}

#if defined(DUK_USE_ES6)
DUK_INTERNAL duk_ret_t duk_bi_string_prototype_repeat(duk_hthread *thr) {
    duk_hstring *h_input;
    duk_size_t input_blen;
    duk_size_t result_len;
    duk_int_t count_signed;
    duk_uint_t count;
    const duk_uint8_t *src;
    duk_uint8_t *buf;
    duk_uint8_t *p;
    duk_double_t d;
#if !defined(DUK_USE_PREFER_SIZE)
    duk_size_t copy_size;
    duk_uint8_t *p_end;
#endif

    DUK_ASSERT_TOP(thr, 1);
    h_input = duk_push_this_coercible_to_string(thr);
    DUK_ASSERT(h_input != NULL);
    input_blen = DUK_HSTRING_GET_BYTELEN(h_input);

    /* Count is ToNumber() coerced; +Infinity must be always rejected
     * (even if input string is zero length), as well as negative values
     * and -Infinity.  -Infinity doesn't require an explicit check
     * because duk_get_int() clamps it to DUK_INT_MIN which gets rejected
     * as a negative value (regardless of input string length).
     */
    d = duk_to_number(thr, 0);
    if (duk_double_is_posinf(d)) {
        goto fail_range;
    }
    count_signed = duk_get_int(thr, 0);
    if (count_signed < 0) {
        goto fail_range;
    }
    count = (duk_uint_t) count_signed;

    /* Overflow check for result length. */
    result_len = count * input_blen;
    if (count != 0 && result_len / count != input_blen) {
        goto fail_range;
    }

    /* Temporary fixed buffer, later converted to string. */
    buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, result_len);
    DUK_ASSERT(buf != NULL);
    src = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_input);
    DUK_ASSERT(src != NULL);

#if defined(DUK_USE_PREFER_SIZE)
    p = buf;
    while (count-- > 0) {
        duk_memcpy((void *) p, (const void *) src, input_blen);  /* copy size may be zero, but pointers are valid */
        p += input_blen;
    }
#else  /* DUK_USE_PREFER_SIZE */
    /* Take advantage of already copied pieces to speed up the process
     * especially for small repeated strings.
     */
    p = buf;
    p_end = p + result_len;
    copy_size = input_blen;
    for (;;) {
        duk_size_t remain = (duk_size_t) (p_end - p);
        DUK_DDD(DUK_DDDPRINT("remain=%ld, copy_size=%ld, input_blen=%ld, result_len=%ld",
                             (long) remain, (long) copy_size, (long) input_blen,
                             (long) result_len));
        if (remain <= copy_size) {
            /* If result_len is zero, this case is taken and does
             * a zero size copy (with valid pointers).
             */
            duk_memcpy((void *) p, (const void *) src, remain);
            break;
        } else {
            duk_memcpy((void *) p, (const void *) src, copy_size);
            p += copy_size;
        }

        src = (const duk_uint8_t *) buf;  /* Use buf as source for larger copies. */
        copy_size = (duk_size_t) (p - buf);
    }
#endif  /* DUK_USE_PREFER_SIZE */

    /* XXX: It would be useful to be able to create a duk_hstring with
     * a certain byte size whose data area wasn't initialized and which
     * wasn't in the string table yet.  This would allow a string to be
     * constructed directly without a buffer temporary and when it was
     * finished, it could be injected into the string table.  Currently
     * this isn't possible because duk_hstrings are only tracked by the
     * intern table (they are not in heap_allocated).
     */

    duk_buffer_to_string(thr, -1);  /* Safe if input is safe. */
    return 1;

 fail_range:
    DUK_DCERROR_RANGE_INVALID_ARGS(thr);
}
#endif  /* DUK_USE_ES6 */

DUK_INTERNAL duk_ret_t duk_bi_string_prototype_locale_compare(duk_hthread *thr) {
    duk_hstring *h1;
    duk_hstring *h2;
    duk_size_t h1_len, h2_len, prefix_len;
    duk_small_int_t ret = 0;
    duk_small_int_t rc;

    /* The current implementation of localeCompare() is simply a codepoint
     * by codepoint comparison, implemented with a simple string compare
     * because UTF-8 should preserve codepoint ordering (assuming valid
     * shortest UTF-8 encoding).
     *
     * The specification requires that the return value must be related
     * to the sort order: e.g. negative means that 'this' comes before
     * 'that' in sort order.  We assume an ascending sort order.
     */

    /* XXX: could share code with duk_js_ops.c, duk_js_compare_helper */

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

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

    h1_len = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h1);
    h2_len = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h2);
    prefix_len = (h1_len <= h2_len ? h1_len : h2_len);

    rc = (duk_small_int_t) duk_memcmp((const void *) DUK_HSTRING_GET_DATA(h1),
                                      (const void *) DUK_HSTRING_GET_DATA(h2),
                                      (size_t) prefix_len);

    if (rc < 0) {
        ret = -1;
        goto done;
    } else if (rc > 0) {
        ret = 1;
        goto done;
    }

    /* prefix matches, lengths matter now */
    if (h1_len > h2_len) {
        ret = 1;
        goto done;
    } else if (h1_len == h2_len) {
        DUK_ASSERT(ret == 0);
        goto done;
    }
    ret = -1;
    goto done;

 done:
    duk_push_int(thr, (duk_int_t) ret);
    return 1;
}

#if defined(DUK_USE_ES6)
DUK_INTERNAL duk_ret_t duk_bi_string_prototype_startswith_endswith(duk_hthread *thr) {
    duk_int_t magic;
    duk_hstring *h_target;
    duk_size_t blen_target;
    duk_hstring *h_search;
    duk_size_t blen_search;
    duk_int_t off;
    duk_bool_t result = 0;
    duk_size_t blen_left;

    /* Because string byte lengths are in [0,DUK_INT_MAX] it's safe to
     * subtract two string lengths without overflow.
     */
    DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= DUK_INT_MAX);

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

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

    magic = duk_get_current_magic(thr);

    /* Careful to avoid pointer overflows in the matching logic. */

    blen_target = DUK_HSTRING_GET_BYTELEN(h_target);
    blen_search = DUK_HSTRING_GET_BYTELEN(h_search);

#if 0
    /* If search string is longer than the target string, we can
     * never match.  Could check explicitly, but should be handled
     * correctly below.
     */
    if (blen_search > blen_target) {
        goto finish;
    }
#endif

    off = 0;
    if (duk_is_undefined(thr, 1)) {
        if (magic) {
            off = (duk_int_t) blen_target - (duk_int_t) blen_search;
        } else {
            DUK_ASSERT(off == 0);
        }
    } else {
        duk_int_t len;
        duk_int_t pos;

        DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= DUK_INT_MAX);
        len = (duk_int_t) DUK_HSTRING_GET_CHARLEN(h_target);
        pos = duk_to_int_clamped(thr, 1, 0, len);
        DUK_ASSERT(pos >= 0 && pos <= len);

        off = (duk_int_t) duk_heap_strcache_offset_char2byte(thr, h_target, (duk_uint_fast32_t) pos);
        if (magic) {
            off -= (duk_int_t) blen_search;
        }
    }
    if (off < 0 || off > (duk_int_t) blen_target) {
        goto finish;
    }

    /* The main comparison can be done using a memcmp() rather than
     * doing codepoint comparisons: for CESU-8 strings there is a
     * canonical representation for every codepoint.  But we do need
     * to deal with the char/byte offset translation to find the
     * comparison range.
     */

    DUK_ASSERT(off >= 0);
    DUK_ASSERT((duk_size_t) off <= blen_target);
    blen_left = blen_target - (duk_size_t) off;
    if (blen_left >= blen_search) {
        const duk_uint8_t *p_cmp_start = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_target) + off;
        const duk_uint8_t *p_search = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_search);
        if (duk_memcmp_unsafe((const void *) p_cmp_start, (const void *) p_search, (size_t) blen_search) == 0) {
            result = 1;
        }
    }

 finish:
    duk_push_boolean(thr, result);
    return 1;
}
#endif  /* DUK_USE_ES6 */

#if defined(DUK_USE_ES6)
DUK_INTERNAL duk_ret_t duk_bi_string_prototype_includes(duk_hthread *thr) {
    duk_hstring *h;
    duk_hstring *h_search;
    duk_int_t len;
    duk_int_t pos;

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

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

    len = (duk_int_t) DUK_HSTRING_GET_CHARLEN(h);
    pos = duk_to_int_clamped(thr, 1, 0, len);
    DUK_ASSERT(pos >= 0 && pos <= len);

    pos = duk__str_search_shared(thr, h, h_search, pos, 0 /*backwards*/);
    duk_push_boolean(thr, pos >= 0);
    return 1;
}
#endif  /* DUK_USE_ES6 */
#endif  /* DUK_USE_STRING_BUILTIN */
//#line 1 "duk_bi_symbol.c"
/*
 *  Symbol built-in
 */

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

#if defined(DUK_USE_SYMBOL_BUILTIN)

/*
 *  Constructor
 */

DUK_INTERNAL duk_ret_t duk_bi_symbol_constructor_shared(duk_hthread *thr) {
    const duk_uint8_t *desc;
    duk_size_t len;
    duk_uint8_t *buf;
    duk_uint8_t *p;
    duk_int_t magic;

    magic = duk_get_current_magic(thr);
    if (duk_is_undefined(thr, 0) && (magic == 0)) {
        /* Symbol() accepts undefined and empty string, but they are
         * treated differently.
         */
        desc = NULL;
        len = 0;
    } else {
        /* Symbol.for() coerces undefined to 'undefined' */
        desc = (const duk_uint8_t *) duk_to_lstring(thr, 0, &len);
    }

    /* Maximum symbol data length:
     *   +1    initial byte (0x80 or 0x81)
     *   +len  description
     *   +1    0xff after description, before unique suffix
     *   +17   autogenerated unique suffix: 'ffffffff-ffffffff' is longest
     *   +1    0xff after unique suffix for symbols with undefined description
     */
    buf = (duk_uint8_t *) duk_push_fixed_buffer(thr, 1 + len + 1 + 17 + 1);
    DUK_ASSERT(buf != NULL);
    p = buf + 1;
    DUK_ASSERT(desc != NULL || len == 0);  /* may be NULL if len is 0 */
    duk_memcpy_unsafe((void *) p, (const void *) desc, len);
    p += len;
    if (magic == 0) {
        /* Symbol(): create unique symbol.  Use two 32-bit values
         * to avoid dependency on 64-bit types and 64-bit integer
         * formatting (at least for now).
         */
        if (++thr->heap->sym_counter[0] == 0) {
            thr->heap->sym_counter[1]++;
        }
        p += DUK_SPRINTF((char *) p, "\xFF" "%lx-%lx",
                         (unsigned long) thr->heap->sym_counter[1],
                         (unsigned long) thr->heap->sym_counter[0]);
        if (desc == NULL) {
            /* Special case for 'undefined' description, trailing
             * 0xff distinguishes from empty string description,
             * but needs minimal special case handling elsewhere.
             */
            *p++ = 0xff;
        }
        buf[0] = 0x81;
    } else {
        /* Symbol.for(): create a global symbol */
        buf[0] = 0x80;
    }

    duk_push_lstring(thr, (const char *) buf, (duk_size_t) (p - buf));
    DUK_DDD(DUK_DDDPRINT("created symbol: %!T", duk_get_tval(thr, -1)));
    return 1;
}

DUK_LOCAL duk_hstring *duk__auto_unbox_symbol(duk_hthread *thr, duk_tval *tv_arg) {
    duk_tval *tv;
    duk_hobject *h_obj;
    duk_hstring *h_str;

    DUK_ASSERT(tv_arg != NULL);

    /* XXX: add internal helper: duk_auto_unbox_tval(thr, tv, mask); */
    /* XXX: add internal helper: duk_auto_unbox(thr, tv, idx); */

    tv = tv_arg;
    if (DUK_TVAL_IS_OBJECT(tv)) {
        h_obj = DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(h_obj != NULL);
        if (DUK_HOBJECT_GET_CLASS_NUMBER(h_obj) == DUK_HOBJECT_CLASS_SYMBOL) {
            tv = duk_hobject_get_internal_value_tval_ptr(thr->heap, h_obj);
            if (tv == NULL) {
                return NULL;
            }
        } else {
            return NULL;
        }
    }

    if (!DUK_TVAL_IS_STRING(tv)) {
        return NULL;
    }
    h_str = DUK_TVAL_GET_STRING(tv);
    DUK_ASSERT(h_str != NULL);

    /* Here symbol is more expected than not. */
    if (DUK_UNLIKELY(!DUK_HSTRING_HAS_SYMBOL(h_str))) {
        return NULL;
    }

    return h_str;
}

DUK_INTERNAL duk_ret_t duk_bi_symbol_tostring_shared(duk_hthread *thr) {
    duk_hstring *h_str;

    h_str = duk__auto_unbox_symbol(thr, DUK_HTHREAD_THIS_PTR(thr));
    if (h_str == NULL) {
        return DUK_RET_TYPE_ERROR;
    }

    if (duk_get_current_magic(thr) == 0) {
        /* .toString() */
        duk_push_symbol_descriptive_string(thr, h_str);
    } else {
        /* .valueOf() */
        duk_push_hstring(thr, h_str);
    }
    return 1;
}

DUK_INTERNAL duk_ret_t duk_bi_symbol_key_for(duk_hthread *thr) {
    duk_hstring *h;
    const duk_uint8_t *p;

    /* Argument must be a symbol but not checked here.  The initial byte
     * check will catch non-symbol strings.
     */
    h = duk_require_hstring(thr, 0);
    DUK_ASSERT(h != NULL);

    p = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h);
    DUK_ASSERT(p != NULL);

    /* Even for zero length strings there's at least one NUL byte so
     * we can safely check the initial byte.
     */
    if (p[0] == 0x80) {
        /* Global symbol, return its key (bytes just after the initial byte). */
        duk_push_lstring(thr, (const char *) (p + 1), (duk_size_t) (DUK_HSTRING_GET_BYTELEN(h) - 1));
        return 1;
    } else if (p[0] == 0x81 || p[0] == 0x82 || p[0] == 0xff) {
        /* Local symbol or hidden symbol, return undefined. */
        return 0;
    }

    /* Covers normal strings and unknown initial bytes. */
    return DUK_RET_TYPE_ERROR;
}

DUK_INTERNAL duk_ret_t duk_bi_symbol_toprimitive(duk_hthread *thr) {
    duk_hstring *h_str;

    h_str = duk__auto_unbox_symbol(thr, DUK_HTHREAD_THIS_PTR(thr));
    if (h_str == NULL) {
        return DUK_RET_TYPE_ERROR;
    }
    duk_push_hstring(thr, h_str);
    return 1;
}

#endif  /* DUK_USE_SYMBOL_BUILTIN */
//#line 1 "duk_bi_thread.c"
/*
 *  Thread builtins
 */

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

/*
 *  Constructor
 */

#if defined(DUK_USE_COROUTINE_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_thread_constructor(duk_hthread *thr) {
    duk_hthread *new_thr;
    duk_hobject *func;

    /* Check that the argument is callable; this is not 100% because we
     * don't allow native functions to be a thread's initial function.
     * Resume will reject such functions in any case.
     */
    /* XXX: need a duk_require_func_promote_lfunc() */
    func = duk_require_hobject_promote_lfunc(thr, 0);
    DUK_ASSERT(func != NULL);
    duk_require_callable(thr, 0);

    duk_push_thread(thr);
    new_thr = (duk_hthread *) duk_known_hobject(thr, -1);
    new_thr->state = DUK_HTHREAD_STATE_INACTIVE;

    /* push initial function call to new thread stack; this is
     * picked up by resume().
     */
    duk_push_hobject(new_thr, func);

    return 1;  /* return thread */
}
#endif

/*
 *  Resume a thread.
 *
 *  The thread must be in resumable state, either (a) new thread which hasn't
 *  yet started, or (b) a thread which has previously yielded.  This method
 *  must be called from an ECMAScript function.
 *
 *  Args:
 *    - thread
 *    - value
 *    - isError (defaults to false)
 *
 *  Note: yield and resume handling is currently asymmetric.
 */

#if defined(DUK_USE_COROUTINE_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_thread_resume(duk_hthread *ctx) {
    duk_hthread *thr = (duk_hthread *) ctx;
    duk_hthread *thr_resume;
    duk_hobject *caller_func;
    duk_small_uint_t is_error;

    DUK_DDD(DUK_DDDPRINT("Duktape.Thread.resume(): thread=%!T, value=%!T, is_error=%!T",
                         (duk_tval *) duk_get_tval(thr, 0),
                         (duk_tval *) duk_get_tval(thr, 1),
                         (duk_tval *) duk_get_tval(thr, 2)));

    DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
    DUK_ASSERT(thr->heap->curr_thread == thr);

    thr_resume = duk_require_hthread(thr, 0);
    DUK_ASSERT(duk_get_top(thr) == 3);
    is_error = (duk_small_uint_t) duk_to_boolean_top_pop(thr);
    DUK_ASSERT(duk_get_top(thr) == 2);

    /* [ thread value ] */

    /*
     *  Thread state and calling context checks
     */

    if (thr->callstack_top < 2) {
        DUK_DD(DUK_DDPRINT("resume state invalid: callstack should contain at least 2 entries (caller and Duktape.Thread.resume)"));
        goto state_error;
    }
    DUK_ASSERT(thr->callstack_curr != NULL);
    DUK_ASSERT(thr->callstack_curr->parent != NULL);
    DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL);  /* us */
    DUK_ASSERT(DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr)));
    DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr->parent) != NULL);  /* caller */

    caller_func = DUK_ACT_GET_FUNC(thr->callstack_curr->parent);
    if (!DUK_HOBJECT_IS_COMPFUNC(caller_func)) {
        DUK_DD(DUK_DDPRINT("resume state invalid: caller must be ECMAScript code"));
        goto state_error;
    }

    /* Note: there is no requirement that: 'thr->callstack_preventcount == 1'
     * like for yield.
     */

    if (thr_resume->state != DUK_HTHREAD_STATE_INACTIVE &&
        thr_resume->state != DUK_HTHREAD_STATE_YIELDED) {
        DUK_DD(DUK_DDPRINT("resume state invalid: target thread must be INACTIVE or YIELDED"));
        goto state_error;
    }

    DUK_ASSERT(thr_resume->state == DUK_HTHREAD_STATE_INACTIVE ||
               thr_resume->state == DUK_HTHREAD_STATE_YIELDED);

    /* Further state-dependent pre-checks */

    if (thr_resume->state == DUK_HTHREAD_STATE_YIELDED) {
        /* no pre-checks now, assume a previous yield() has left things in
         * tip-top shape (longjmp handler will assert for these).
         */
    } else {
        duk_hobject *h_fun;

        DUK_ASSERT(thr_resume->state == DUK_HTHREAD_STATE_INACTIVE);

        /* The initial function must be an ECMAScript function (but
         * can be bound).  We must make sure of that before we longjmp
         * because an error in the RESUME handler call processing will
         * not be handled very cleanly.
         */
        if ((thr_resume->callstack_top != 0) ||
            (thr_resume->valstack_top - thr_resume->valstack != 1)) {
            goto state_error;
        }

        duk_push_tval(thr, DUK_GET_TVAL_NEGIDX(thr_resume, -1));
        duk_resolve_nonbound_function(thr);
        h_fun = duk_require_hobject(thr, -1);  /* reject lightfuncs on purpose */
        if (!DUK_HOBJECT_IS_CALLABLE(h_fun) || !DUK_HOBJECT_IS_COMPFUNC(h_fun)) {
            goto state_error;
        }
        duk_pop(thr);
    }

#if 0
    /* This check would prevent a heap destruction time finalizer from
     * launching a coroutine, which would ensure that during finalization
     * 'thr' would always equal heap_thread.  Normal runtime finalizers
     * run with ms_running == 0, i.e. outside mark-and-sweep.  See GH-2030.
     */
    if (thr->heap->ms_running) {
        DUK_D(DUK_DPRINT("refuse Duktape.Thread.resume() when ms_running != 0"));
        goto state_error;
    }
#endif

    /*
     *  The error object has been augmented with a traceback and other
     *  info from its creation point -- usually another thread.  The
     *  error handler is called here right before throwing, but it also
     *  runs in the resumer's thread.  It might be nice to get a traceback
     *  from the resumee but this is not the case now.
     */

#if defined(DUK_USE_AUGMENT_ERROR_THROW)
    if (is_error) {
        DUK_ASSERT_TOP(thr, 2);  /* value (error) is at stack top */
        duk_err_augment_error_throw(thr);  /* in resumer's context */
    }
#endif

#if defined(DUK_USE_DEBUG)
    if (is_error) {
        DUK_DDD(DUK_DDDPRINT("RESUME ERROR: thread=%!T, value=%!T",
                             (duk_tval *) duk_get_tval(thr, 0),
                             (duk_tval *) duk_get_tval(thr, 1)));
    } else if (thr_resume->state == DUK_HTHREAD_STATE_YIELDED) {
        DUK_DDD(DUK_DDDPRINT("RESUME NORMAL: thread=%!T, value=%!T",
                             (duk_tval *) duk_get_tval(thr, 0),
                             (duk_tval *) duk_get_tval(thr, 1)));
    } else {
        DUK_DDD(DUK_DDDPRINT("RESUME INITIAL: thread=%!T, value=%!T",
                             (duk_tval *) duk_get_tval(thr, 0),
                             (duk_tval *) duk_get_tval(thr, 1)));
    }
#endif

    thr->heap->lj.type = DUK_LJ_TYPE_RESUME;

    /* lj value2: thread */
    DUK_ASSERT(thr->valstack_bottom < thr->valstack_top);
    DUK_TVAL_SET_TVAL_UPDREF(thr, &thr->heap->lj.value2, &thr->valstack_bottom[0]);  /* side effects */

    /* lj value1: value */
    DUK_ASSERT(thr->valstack_bottom + 1 < thr->valstack_top);
    DUK_TVAL_SET_TVAL_UPDREF(thr, &thr->heap->lj.value1, &thr->valstack_bottom[1]);  /* side effects */
    DUK_TVAL_CHKFAST_INPLACE_SLOW(&thr->heap->lj.value1);

    thr->heap->lj.iserror = is_error;

    DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL);  /* call is from executor, so we know we have a jmpbuf */
    duk_err_longjmp(thr);  /* execution resumes in bytecode executor */
    DUK_UNREACHABLE();
    /* Never here, fall through to error (from compiler point of view). */

 state_error:
    DUK_DCERROR_TYPE_INVALID_STATE(thr);
}
#endif

/*
 *  Yield the current thread.
 *
 *  The thread must be in yieldable state: it must have a resumer, and there
 *  must not be any yield-preventing calls (native calls and constructor calls,
 *  currently) in the thread's call stack (otherwise a resume would not be
 *  possible later).  This method must be called from an ECMAScript function.
 *
 *  Args:
 *    - value
 *    - isError (defaults to false)
 *
 *  Note: yield and resume handling is currently asymmetric.
 */

#if defined(DUK_USE_COROUTINE_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_thread_yield(duk_hthread *thr) {
    duk_hobject *caller_func;
    duk_small_uint_t is_error;

    DUK_DDD(DUK_DDDPRINT("Duktape.Thread.yield(): value=%!T, is_error=%!T",
                         (duk_tval *) duk_get_tval(thr, 0),
                         (duk_tval *) duk_get_tval(thr, 1)));

    DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
    DUK_ASSERT(thr->heap->curr_thread == thr);

    DUK_ASSERT(duk_get_top(thr) == 2);
    is_error = (duk_small_uint_t) duk_to_boolean_top_pop(thr);
    DUK_ASSERT(duk_get_top(thr) == 1);

    /* [ value ] */

    /*
     *  Thread state and calling context checks
     */

    if (!thr->resumer) {
        DUK_DD(DUK_DDPRINT("yield state invalid: current thread must have a resumer"));
        goto state_error;
    }
    DUK_ASSERT(thr->resumer->state == DUK_HTHREAD_STATE_RESUMED);

    if (thr->callstack_top < 2) {
        DUK_DD(DUK_DDPRINT("yield state invalid: callstack should contain at least 2 entries (caller and Duktape.Thread.yield)"));
        goto state_error;
    }
    DUK_ASSERT(thr->callstack_curr != NULL);
    DUK_ASSERT(thr->callstack_curr->parent != NULL);
    DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL);  /* us */
    DUK_ASSERT(DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr)));
    DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr->parent) != NULL);  /* caller */

    caller_func = DUK_ACT_GET_FUNC(thr->callstack_curr->parent);
    if (!DUK_HOBJECT_IS_COMPFUNC(caller_func)) {
        DUK_DD(DUK_DDPRINT("yield state invalid: caller must be ECMAScript code"));
        goto state_error;
    }

    DUK_ASSERT(thr->callstack_preventcount >= 1);  /* should never be zero, because we (Duktape.Thread.yield) are on the stack */
    if (thr->callstack_preventcount != 1) {
        /* Note: the only yield-preventing call is Duktape.Thread.yield(), hence check for 1, not 0 */
        DUK_DD(DUK_DDPRINT("yield state invalid: there must be no yield-preventing calls in current thread callstack (preventcount is %ld)",
                           (long) thr->callstack_preventcount));
        goto state_error;
    }

    /*
     *  The error object has been augmented with a traceback and other
     *  info from its creation point -- usually the current thread.
     *  The error handler, however, is called right before throwing
     *  and runs in the yielder's thread.
     */

#if defined(DUK_USE_AUGMENT_ERROR_THROW)
    if (is_error) {
        DUK_ASSERT_TOP(thr, 1);  /* value (error) is at stack top */
        duk_err_augment_error_throw(thr);  /* in yielder's context */
    }
#endif

#if defined(DUK_USE_DEBUG)
    if (is_error) {
        DUK_DDD(DUK_DDDPRINT("YIELD ERROR: value=%!T",
                             (duk_tval *) duk_get_tval(thr, 0)));
    } else {
        DUK_DDD(DUK_DDDPRINT("YIELD NORMAL: value=%!T",
                             (duk_tval *) duk_get_tval(thr, 0)));
    }
#endif

    /*
     *  Process yield
     *
     *  After longjmp(), processing continues in bytecode executor longjmp
     *  handler, which will e.g. update thr->resumer to NULL.
     */

    thr->heap->lj.type = DUK_LJ_TYPE_YIELD;

    /* lj value1: value */
    DUK_ASSERT(thr->valstack_bottom < thr->valstack_top);
    DUK_TVAL_SET_TVAL_UPDREF(thr, &thr->heap->lj.value1, &thr->valstack_bottom[0]);  /* side effects */
    DUK_TVAL_CHKFAST_INPLACE_SLOW(&thr->heap->lj.value1);

    thr->heap->lj.iserror = is_error;

    DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL);  /* call is from executor, so we know we have a jmpbuf */
    duk_err_longjmp(thr);  /* execution resumes in bytecode executor */
    DUK_UNREACHABLE();
    /* Never here, fall through to error (from compiler point of view). */

 state_error:
    DUK_DCERROR_TYPE_INVALID_STATE(thr);
}
#endif

#if defined(DUK_USE_COROUTINE_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_thread_current(duk_hthread *thr) {
    duk_push_current_thread(thr);
    return 1;
}
#endif
//#line 1 "duk_bi_thrower.c"
/*
 *  Type error thrower, E5 Section 13.2.3.
 */

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

DUK_INTERNAL duk_ret_t duk_bi_type_error_thrower(duk_hthread *thr) {
    DUK_DCERROR_TYPE_INVALID_ARGS(thr);
}
//#line 1 "duk_debug_fixedbuffer.c"
/*
 *  Fixed buffer helper useful for debugging, requires no allocation
 *  which is critical for debugging.
 */

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

#if defined(DUK_USE_DEBUG)

DUK_INTERNAL void duk_fb_put_bytes(duk_fixedbuffer *fb, const duk_uint8_t *buffer, duk_size_t length) {
    duk_size_t avail;
    duk_size_t copylen;

    avail = (fb->offset >= fb->length ? (duk_size_t) 0 : (duk_size_t) (fb->length - fb->offset));
    if (length > avail) {
        copylen = avail;
        fb->truncated = 1;
    } else {
        copylen = length;
    }
    duk_memcpy_unsafe(fb->buffer + fb->offset, buffer, copylen);
    fb->offset += copylen;
}

DUK_INTERNAL void duk_fb_put_byte(duk_fixedbuffer *fb, duk_uint8_t x) {
    duk_fb_put_bytes(fb, (const duk_uint8_t *) &x, 1);
}

DUK_INTERNAL void duk_fb_put_cstring(duk_fixedbuffer *fb, const char *x) {
    duk_fb_put_bytes(fb, (const duk_uint8_t *) x, (duk_size_t) DUK_STRLEN(x));
}

DUK_INTERNAL void duk_fb_sprintf(duk_fixedbuffer *fb, const char *fmt, ...) {
    duk_size_t avail;
    va_list ap;

    va_start(ap, fmt);
    avail = (fb->offset >= fb->length ? (duk_size_t) 0 : (duk_size_t) (fb->length - fb->offset));
    if (avail > 0) {
        duk_int_t res = (duk_int_t) DUK_VSNPRINTF((char *) (fb->buffer + fb->offset), avail, fmt, ap);
        if (res < 0) {
            /* error */
        } else if ((duk_size_t) res >= avail) {
            /* (maybe) truncated */
            fb->offset += avail;
            if ((duk_size_t) res > avail) {
                /* actual chars dropped (not just NUL term) */
                fb->truncated = 1;
            }
        } else {
            /* normal */
            fb->offset += (duk_size_t) res;
        }
    }
    va_end(ap);
}

DUK_INTERNAL void duk_fb_put_funcptr(duk_fixedbuffer *fb, duk_uint8_t *fptr, duk_size_t fptr_size) {
    char buf[64+1];
    duk_debug_format_funcptr(buf, sizeof(buf), fptr, fptr_size);
    buf[sizeof(buf) - 1] = (char) 0;
    duk_fb_put_cstring(fb, buf);
}

DUK_INTERNAL duk_bool_t duk_fb_is_full(duk_fixedbuffer *fb) {
    return (fb->offset >= fb->length);
}

#endif  /* DUK_USE_DEBUG */
//#line 1 "duk_debug_vsnprintf.c"
/*
 *  Custom formatter for debug printing, allowing Duktape specific data
 *  structures (such as tagged values and heap objects) to be printed with
 *  a nice format string.  Because debug printing should not affect execution
 *  state, formatting here must be independent of execution (see implications
 *  below) and must not allocate memory.
 *
 *  Custom format tags begin with a '%!' to safely distinguish them from
 *  standard format tags.  The following conversions are supported:
 *
 *     %!T    tagged value (duk_tval *)
 *     %!O    heap object (duk_heaphdr *)
 *     %!I    decoded bytecode instruction
 *     %!X    bytecode instruction opcode name (arg is long)
 *     %!C    catcher (duk_catcher *)
 *     %!A    activation (duk_activation *)
 *
 *  Everything is serialized in a JSON-like manner.  The default depth is one
 *  level, internal prototype is not followed, and internal properties are not
 *  serialized.  The following modifiers change this behavior:
 *
 *     @      print pointers
 *     #      print binary representations (where applicable)
 *     d      deep traversal of own properties (not prototype)
 *     p      follow prototype chain (useless without 'd')
 *     i      include internal properties (other than prototype)
 *     x      hexdump buffers
 *     h      heavy formatting
 *
 *  For instance, the following serializes objects recursively, but does not
 *  follow the prototype chain nor print internal properties: "%!dO".
 *
 *  Notes:
 *
 *    * Standard snprintf return value semantics seem to vary.  This
 *      implementation returns the number of bytes it actually wrote
 *      (excluding the null terminator).  If retval == buffer size,
 *      output was truncated (except for corner cases).
 *
 *    * Output format is intentionally different from ECMAScript
 *      formatting requirements, as formatting here serves debugging
 *      of internals.
 *
 *    * Depth checking (and updating) is done in each type printer
 *      separately, to allow them to call each other freely.
 *
 *    * Some pathological structures might take ages to print (e.g.
 *      self recursion with 100 properties pointing to the object
 *      itself).  To guard against these, each printer also checks
 *      whether the output buffer is full; if so, early exit.
 *
 *    * Reference loops are detected using a loop stack.
 */

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

#if defined(DUK_USE_DEBUG)

/* #include stdio.h -> already included */
/* #include stdarg.h -> already included */
//#include <string.h>

/* list of conversion specifiers that terminate a format tag;
 * this is unfortunately guesswork.
 */
#define DUK__ALLOWED_STANDARD_SPECIFIERS  "diouxXeEfFgGaAcsCSpnm"

/* maximum length of standard format tag that we support */
#define DUK__MAX_FORMAT_TAG_LENGTH  32

/* heapobj recursion depth when deep printing is selected */
#define DUK__DEEP_DEPTH_LIMIT  8

/* maximum recursion depth for loop detection stacks */
#define DUK__LOOP_STACK_DEPTH  256

/* must match bytecode defines now; build autogenerate? */
DUK_LOCAL const char * const duk__bc_optab[256] = {
    "LDREG", "STREG", "JUMP", "LDCONST", "LDINT", "LDINTX", "LDTHIS", "LDUNDEF",
    "LDNULL", "LDTRUE", "LDFALSE", "GETVAR", "BNOT", "LNOT", "UNM", "UNP",
    "EQ_RR", "EQ_CR", "EQ_RC", "EQ_CC", "NEQ_RR", "NEQ_CR", "NEQ_RC", "NEQ_CC",
    "SEQ_RR", "SEQ_CR", "SEQ_RC", "SEQ_CC", "SNEQ_RR", "SNEQ_CR", "SNEQ_RC", "SNEQ_CC",

    "GT_RR", "GT_CR", "GT_RC", "GT_CC", "GE_RR", "GE_CR", "GE_RC", "GE_CC",
    "LT_RR", "LT_CR", "LT_RC", "LT_CC", "LE_RR", "LE_CR", "LE_RC", "LE_CC",
    "IFTRUE_R", "IFTRUE_C", "IFFALSE_R", "IFFALSE_C", "ADD_RR", "ADD_CR", "ADD_RC", "ADD_CC",
    "SUB_RR", "SUB_CR", "SUB_RC", "SUB_CC", "MUL_RR", "MUL_CR", "MUL_RC", "MUL_CC",

    "DIV_RR", "DIV_CR", "DIV_RC", "DIV_CC", "MOD_RR", "MOD_CR", "MOD_RC", "MOD_CC",
    "EXP_RR", "EXP_CR", "EXP_RC", "EXP_CC", "BAND_RR", "BAND_CR", "BAND_RC", "BAND_CC",
    "BOR_RR", "BOR_CR", "BOR_RC", "BOR_CC", "BXOR_RR", "BXOR_CR", "BXOR_RC", "BXOR_CC",
    "BASL_RR", "BASL_CR", "BASL_RC", "BASL_CC", "BLSR_RR", "BLSR_CR", "BLSR_RC", "BLSR_CC",

    "BASR_RR", "BASR_CR", "BASR_RC", "BASR_CC", "INSTOF_RR", "INSTOF_CR", "INSTOF_RC", "INSTOF_CC",
    "IN_RR", "IN_CR", "IN_RC", "IN_CC", "GETPROP_RR", "GETPROP_CR", "GETPROP_RC", "GETPROP_CC",
    "PUTPROP_RR", "PUTPROP_CR", "PUTPROP_RC", "PUTPROP_CC", "DELPROP_RR", "DELPROP_CR", "DELPROP_RC", "DELPROP_CC",
    "PREINCR", "PREDECR", "POSTINCR", "POSTDECR", "PREINCV", "PREDECV", "POSTINCV", "POSTDECV",

    "PREINCP_RR", "PREINCP_CR", "PREINCP_RC", "PREINCP_CC", "PREDECP_RR", "PREDECP_CR", "PREDECP_RC", "PREDECP_CC",
    "POSTINCP_RR", "POSTINCP_CR", "POSTINCP_RC", "POSTINCP_CC", "POSTDECP_RR", "POSTDECP_CR", "POSTDECP_RC", "POSTDECP_CC",
    "DECLVAR_RR", "DECLVAR_CR", "DECLVAR_RC", "DECLVAR_CC", "REGEXP_RR", "REGEXP_RC", "REGEXP_CR", "REGEXP_CC",
    "CLOSURE", "TYPEOF", "TYPEOFID", "PUTVAR", "DELVAR", "RETREG", "RETUNDEF", "RETCONST",

    "RETCONSTN", "LABEL", "ENDLABEL", "BREAK", "CONTINUE", "TRYCATCH", "ENDTRY", "ENDCATCH",
    "ENDFIN", "THROW", "INVLHS", "CSREG", "CSVAR_RR", "CSVAR_CR", "CSVAR_RC", "CSVAR_CC",
    "CALL0", "CALL1", "CALL2", "CALL3", "CALL4", "CALL5", "CALL6", "CALL7",
    "CALL8", "CALL9", "CALL10", "CALL11", "CALL12", "CALL13", "CALL14", "CALL15",

    "NEWOBJ", "NEWARR", "MPUTOBJ", "MPUTOBJI", "INITSET", "INITGET", "MPUTARR", "MPUTARRI",
    "SETALEN", "INITENUM", "NEXTENUM", "NEWTARGET", "DEBUGGER", "NOP", "INVALID", "UNUSED207",
    "GETPROPC_RR", "GETPROPC_CR", "GETPROPC_RC", "GETPROPC_CC", "UNUSED212", "UNUSED213", "UNUSED214", "UNUSED215",
    "UNUSED216", "UNUSED217", "UNUSED218", "UNUSED219", "UNUSED220", "UNUSED221", "UNUSED222", "UNUSED223",

    "UNUSED224", "UNUSED225", "UNUSED226", "UNUSED227", "UNUSED228", "UNUSED229", "UNUSED230", "UNUSED231",
    "UNUSED232", "UNUSED233", "UNUSED234", "UNUSED235", "UNUSED236", "UNUSED237", "UNUSED238", "UNUSED239",
    "UNUSED240", "UNUSED241", "UNUSED242", "UNUSED243", "UNUSED244", "UNUSED245", "UNUSED246", "UNUSED247",
    "UNUSED248", "UNUSED249", "UNUSED250", "UNUSED251", "UNUSED252", "UNUSED253", "UNUSED254", "UNUSED255"
};

typedef struct duk__dprint_state duk__dprint_state;
struct duk__dprint_state {
    duk_fixedbuffer *fb;

    /* loop_stack_index could be perhaps be replaced by 'depth', but it's nice
     * to not couple these two mechanisms unnecessarily.
     */
    duk_hobject *loop_stack[DUK__LOOP_STACK_DEPTH];
    duk_int_t loop_stack_index;
    duk_int_t loop_stack_limit;

    duk_int_t depth;
    duk_int_t depth_limit;

    duk_bool_t pointer;
    duk_bool_t heavy;
    duk_bool_t binary;
    duk_bool_t follow_proto;
    duk_bool_t internal;
    duk_bool_t hexdump;
};

/* helpers */
DUK_LOCAL_DECL void duk__print_hstring(duk__dprint_state *st, duk_hstring *k, duk_bool_t quotes);
DUK_LOCAL_DECL void duk__print_hobject(duk__dprint_state *st, duk_hobject *h);
DUK_LOCAL_DECL void duk__print_hbuffer(duk__dprint_state *st, duk_hbuffer *h);
DUK_LOCAL_DECL void duk__print_tval(duk__dprint_state *st, duk_tval *tv);
DUK_LOCAL_DECL void duk__print_instr(duk__dprint_state *st, duk_instr_t ins);
DUK_LOCAL_DECL void duk__print_heaphdr(duk__dprint_state *st, duk_heaphdr *h);
DUK_LOCAL_DECL void duk__print_shared_heaphdr(duk__dprint_state *st, duk_heaphdr *h);
DUK_LOCAL_DECL void duk__print_shared_heaphdr_string(duk__dprint_state *st, duk_heaphdr_string *h);

DUK_LOCAL void duk__print_shared_heaphdr(duk__dprint_state *st, duk_heaphdr *h) {
    duk_fixedbuffer *fb = st->fb;

    if (st->heavy) {
        duk_fb_sprintf(fb, "(%p)", (void *) h);
    }

    if (!h) {
        return;
    }

    if (st->binary) {
        duk_size_t i;
        duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_LBRACKET);
        for (i = 0; i < (duk_size_t) sizeof(*h); i++) {
            duk_fb_sprintf(fb, "%02lx", (unsigned long) ((duk_uint8_t *)h)[i]);
        }
        duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RBRACKET);
    }

#if defined(DUK_USE_REFERENCE_COUNTING)  /* currently implicitly also DUK_USE_DOUBLE_LINKED_HEAP */
    if (st->heavy) {
        duk_fb_sprintf(fb, "[h_next=%p,h_prev=%p,h_refcount=%lu,h_flags=%08lx,type=%ld,"
                       "reachable=%ld,temproot=%ld,finalizable=%ld,finalized=%ld]",
                       (void *) DUK_HEAPHDR_GET_NEXT(NULL, h),
                       (void *) DUK_HEAPHDR_GET_PREV(NULL, h),
                       (unsigned long) DUK_HEAPHDR_GET_REFCOUNT(h),
                       (unsigned long) DUK_HEAPHDR_GET_FLAGS(h),
                       (long) DUK_HEAPHDR_GET_TYPE(h),
                       (long) (DUK_HEAPHDR_HAS_REACHABLE(h) ? 1 : 0),
                       (long) (DUK_HEAPHDR_HAS_TEMPROOT(h) ? 1 : 0),
                       (long) (DUK_HEAPHDR_HAS_FINALIZABLE(h) ? 1 : 0),
                       (long) (DUK_HEAPHDR_HAS_FINALIZED(h) ? 1 : 0));
    }
#else
    if (st->heavy) {
        duk_fb_sprintf(fb, "[h_next=%p,h_flags=%08lx,type=%ld,reachable=%ld,temproot=%ld,finalizable=%ld,finalized=%ld]",
                       (void *) DUK_HEAPHDR_GET_NEXT(NULL, h),
                       (unsigned long) DUK_HEAPHDR_GET_FLAGS(h),
                       (long) DUK_HEAPHDR_GET_TYPE(h),
                       (long) (DUK_HEAPHDR_HAS_REACHABLE(h) ? 1 : 0),
                       (long) (DUK_HEAPHDR_HAS_TEMPROOT(h) ? 1 : 0),
                       (long) (DUK_HEAPHDR_HAS_FINALIZABLE(h) ? 1 : 0),
                       (long) (DUK_HEAPHDR_HAS_FINALIZED(h) ? 1 : 0));
    }
#endif
}

DUK_LOCAL void duk__print_shared_heaphdr_string(duk__dprint_state *st, duk_heaphdr_string *h) {
    duk_fixedbuffer *fb = st->fb;

    if (st->heavy) {
        duk_fb_sprintf(fb, "(%p)", (void *) h);
    }

    if (!h) {
        return;
    }

    if (st->binary) {
        duk_size_t i;
        duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_LBRACKET);
        for (i = 0; i < (duk_size_t) sizeof(*h); i++) {
            duk_fb_sprintf(fb, "%02lx", (unsigned long) ((duk_uint8_t *)h)[i]);
        }
        duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RBRACKET);
    }

#if defined(DUK_USE_REFERENCE_COUNTING)
    if (st->heavy) {
        duk_fb_sprintf(fb, "[h_refcount=%lu,h_flags=%08lx,type=%ld,reachable=%ld,temproot=%ld,finalizable=%ld,finalized=%ld]",
                       (unsigned long) DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h),
                       (unsigned long) DUK_HEAPHDR_GET_FLAGS((duk_heaphdr *) h),
                       (long) DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) h),
                       (long) (DUK_HEAPHDR_HAS_REACHABLE((duk_heaphdr *) h) ? 1 : 0),
                       (long) (DUK_HEAPHDR_HAS_TEMPROOT((duk_heaphdr *) h) ? 1 : 0),
                       (long) (DUK_HEAPHDR_HAS_FINALIZABLE((duk_heaphdr *) h) ? 1 : 0),
                       (long) (DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) h) ? 1 : 0));
    }
#else
    if (st->heavy) {
        duk_fb_sprintf(fb, "[h_flags=%08lx,type=%ld,reachable=%ld,temproot=%ld,finalizable=%ld,finalized=%ld]",
                       (unsigned long) DUK_HEAPHDR_GET_FLAGS((duk_heaphdr *) h),
                       (long) DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) h),
                       (long) (DUK_HEAPHDR_HAS_REACHABLE((duk_heaphdr *) h) ? 1 : 0),
                       (long) (DUK_HEAPHDR_HAS_TEMPROOT((duk_heaphdr *) h) ? 1 : 0),
                       (long) (DUK_HEAPHDR_HAS_FINALIZABLE((duk_heaphdr *) h) ? 1 : 0),
                       (long) (DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) h) ? 1 : 0));
    }
#endif
}

DUK_LOCAL void duk__print_hstring(duk__dprint_state *st, duk_hstring *h, duk_bool_t quotes) {
    duk_fixedbuffer *fb = st->fb;
    const duk_uint8_t *p;
    const duk_uint8_t *p_end;

    /* terminal type: no depth check */

    if (duk_fb_is_full(fb)) {
        return;
    }

    duk__print_shared_heaphdr_string(st, &h->hdr);

    if (!h) {
        duk_fb_put_cstring(fb, "NULL");
        return;
    }

    p = DUK_HSTRING_GET_DATA(h);
    p_end = p + DUK_HSTRING_GET_BYTELEN(h);

    if (p_end > p && p[0] == DUK_ASC_UNDERSCORE) {
        /* If property key begins with underscore, encode it with
         * forced quotes (e.g. "_Foo") to distinguish it from encoded
         * internal properties (e.g. \x82Bar -> _Bar).
         */
        quotes = 1;
    }

    if (quotes) {
        duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_DOUBLEQUOTE);
    }
    while (p < p_end) {
        duk_uint8_t ch = *p++;

        /* two special escapes: '\' and '"', other printables as is */
        if (ch == '\\') {
            duk_fb_sprintf(fb, "\\\\");
        } else if (ch == '"') {
            duk_fb_sprintf(fb, "\\\"");
        } else if (ch >= 0x20 && ch <= 0x7e) {
            duk_fb_put_byte(fb, ch);
        } else if (ch == 0x82 && !quotes) {
            /* encode \x82Bar as _Bar if no quotes are
             * applied, this is for readable internal keys.
             */
            duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_UNDERSCORE);
        } else {
            duk_fb_sprintf(fb, "\\x%02lx", (unsigned long) ch);
        }
    }
    if (quotes) {
        duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_DOUBLEQUOTE);
    }
#if defined(DUK_USE_REFERENCE_COUNTING)
    /* XXX: limit to quoted strings only, to save keys from being cluttered? */
    duk_fb_sprintf(fb, "/%lu", (unsigned long) DUK_HEAPHDR_GET_REFCOUNT(&h->hdr));
#endif
}

#define DUK__COMMA()  do { \
        if (first) { \
            first = 0; \
        } else { \
            duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_COMMA); \
        } \
    } while (0)

DUK_LOCAL void duk__print_hobject(duk__dprint_state *st, duk_hobject *h) {
    duk_fixedbuffer *fb = st->fb;
    duk_uint_fast32_t i;
    duk_tval *tv;
    duk_hstring *key;
    duk_bool_t first = 1;
    const char *brace1 = "{";
    const char *brace2 = "}";
    duk_bool_t pushed_loopstack = 0;

    if (duk_fb_is_full(fb)) {
        return;
    }

    duk__print_shared_heaphdr(st, &h->hdr);

    if (h && DUK_HOBJECT_HAS_ARRAY_PART(h)) {
        brace1 = "[";
        brace2 = "]";
    }

    if (!h) {
        duk_fb_put_cstring(fb, "NULL");
        goto finished;
    }

    if (st->depth >= st->depth_limit) {
        const char *subtype = "generic";

        if (DUK_HOBJECT_IS_COMPFUNC(h)) {
            subtype = "compfunc";
        } else if (DUK_HOBJECT_IS_NATFUNC(h)) {
            subtype = "natfunc";
        } else if (DUK_HOBJECT_IS_THREAD(h)) {
            subtype = "thread";
        } else if (DUK_HOBJECT_IS_BUFOBJ(h)) {
            subtype = "bufobj";
        } else if (DUK_HOBJECT_IS_ARRAY(h)) {
            subtype = "array";
        }
        duk_fb_sprintf(fb, "%sobject/%s %p%s", (const char *) brace1, subtype, (void *) h, (const char *) brace2);
        return;
    }

    for (i = 0; i < (duk_uint_fast32_t) st->loop_stack_index; i++) {
        if (st->loop_stack[i] == h) {
            duk_fb_sprintf(fb, "%sLOOP:%p%s", (const char *) brace1, (void *) h, (const char *) brace2);
            return;
        }
    }

    /* after this, return paths should 'goto finished' for decrement */
    st->depth++;

    if (st->loop_stack_index >= st->loop_stack_limit) {
        duk_fb_sprintf(fb, "%sOUT-OF-LOOP-STACK%s", (const char *) brace1, (const char *) brace2);
        goto finished;
    }
    st->loop_stack[st->loop_stack_index++] = h;
    pushed_loopstack = 1;

    /*
     *  Notation: double underscore used for internal properties which are not
     *  stored in the property allocation (e.g. '__valstack').
     */

    duk_fb_put_cstring(fb, brace1);

    if (DUK_HOBJECT_GET_PROPS(NULL, h)) {
        duk_uint32_t a_limit;

        a_limit = DUK_HOBJECT_GET_ASIZE(h);
        if (st->internal) {
            /* dump all allocated entries, unused entries print as 'unused',
             * note that these may extend beyond current 'length' and look
             * a bit funny.
             */
        } else {
            /* leave out trailing 'unused' elements */
            while (a_limit > 0) {
                tv = DUK_HOBJECT_A_GET_VALUE_PTR(NULL, h, a_limit - 1);
                if (!DUK_TVAL_IS_UNUSED(tv)) {
                    break;
                }
                a_limit--;
            }
        }

        for (i = 0; i < a_limit; i++) {
            tv = DUK_HOBJECT_A_GET_VALUE_PTR(NULL, h, i);
            DUK__COMMA();
            duk__print_tval(st, tv);
        }
        for (i = 0; i < DUK_HOBJECT_GET_ENEXT(h); i++) {
            key = DUK_HOBJECT_E_GET_KEY(NULL, h, i);
            if (!key) {
                continue;
            }
            if (!st->internal && DUK_HSTRING_HAS_HIDDEN(key)) {
                continue;
            }
            DUK__COMMA();
            duk__print_hstring(st, key, 0);
            duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_COLON);
            if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(NULL, h, i)) {
                duk_fb_sprintf(fb, "[get:%p,set:%p]",
                               (void *) DUK_HOBJECT_E_GET_VALUE(NULL, h, i).a.get,
                               (void *) DUK_HOBJECT_E_GET_VALUE(NULL, h, i).a.set);
            } else {
                tv = &DUK_HOBJECT_E_GET_VALUE(NULL, h, i).v;
                duk__print_tval(st, tv);
            }
            if (st->heavy) {
                duk_fb_sprintf(fb, "<%02lx>", (unsigned long) DUK_HOBJECT_E_GET_FLAGS(NULL, h, i));
            }
        }
    }
    if (st->internal) {
        if (DUK_HOBJECT_IS_ARRAY(h)) {
            DUK__COMMA(); duk_fb_sprintf(fb, "__array:true");
        }
        if (DUK_HOBJECT_HAS_EXTENSIBLE(h)) {
            DUK__COMMA(); duk_fb_sprintf(fb, "__extensible:true");
        }
        if (DUK_HOBJECT_HAS_CONSTRUCTABLE(h)) {
            DUK__COMMA(); duk_fb_sprintf(fb, "__constructable:true");
        }
        if (DUK_HOBJECT_HAS_BOUNDFUNC(h)) {
            DUK__COMMA(); duk_fb_sprintf(fb, "__boundfunc:true");
        }
        if (DUK_HOBJECT_HAS_COMPFUNC(h)) {
            DUK__COMMA(); duk_fb_sprintf(fb, "__compfunc:true");
        }
        if (DUK_HOBJECT_HAS_NATFUNC(h)) {
            DUK__COMMA(); duk_fb_sprintf(fb, "__natfunc:true");
        }
        if (DUK_HOBJECT_HAS_BUFOBJ(h)) {
            DUK__COMMA(); duk_fb_sprintf(fb, "__bufobj:true");
        }
        if (DUK_HOBJECT_IS_THREAD(h)) {
            DUK__COMMA(); duk_fb_sprintf(fb, "__thread:true");
        }
        if (DUK_HOBJECT_HAS_ARRAY_PART(h)) {
            DUK__COMMA(); duk_fb_sprintf(fb, "__array_part:true");
        }
        if (DUK_HOBJECT_HAS_STRICT(h)) {
            DUK__COMMA(); duk_fb_sprintf(fb, "__strict:true");
        }
        if (DUK_HOBJECT_HAS_NOTAIL(h)) {
            DUK__COMMA(); duk_fb_sprintf(fb, "__notail:true");
        }
        if (DUK_HOBJECT_HAS_NEWENV(h)) {
            DUK__COMMA(); duk_fb_sprintf(fb, "__newenv:true");
        }
        if (DUK_HOBJECT_HAS_NAMEBINDING(h)) {
            DUK__COMMA(); duk_fb_sprintf(fb, "__namebinding:true");
        }
        if (DUK_HOBJECT_HAS_CREATEARGS(h)) {
            DUK__COMMA(); duk_fb_sprintf(fb, "__createargs:true");
        }
        if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(h)) {
            DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_array:true");
        }
        if (DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(h)) {
            DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_stringobj:true");
        }
        if (DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(h)) {
            DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_arguments:true");
        }
        if (DUK_HOBJECT_IS_BUFOBJ(h)) {
            DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_bufobj:true");
        }
        if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(h)) {
            DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_proxyobj:true");
        }
    }

    if (st->internal && DUK_HOBJECT_IS_ARRAY(h)) {
        duk_harray *a = (duk_harray *) h;
        DUK__COMMA(); duk_fb_sprintf(fb, "__length:%ld", (long) a->length);
        DUK__COMMA(); duk_fb_sprintf(fb, "__length_nonwritable:%ld", (long) a->length_nonwritable);
    } else if (st->internal && DUK_HOBJECT_IS_COMPFUNC(h)) {
        duk_hcompfunc *f = (duk_hcompfunc *) h;
        DUK__COMMA(); duk_fb_put_cstring(fb, "__data:");
        duk__print_hbuffer(st, (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(NULL, f));
        DUK__COMMA(); duk_fb_put_cstring(fb, "__lexenv:"); duk__print_hobject(st, DUK_HCOMPFUNC_GET_LEXENV(NULL, f));
        DUK__COMMA(); duk_fb_put_cstring(fb, "__varenv:"); duk__print_hobject(st, DUK_HCOMPFUNC_GET_VARENV(NULL, f));
        DUK__COMMA(); duk_fb_sprintf(fb, "__nregs:%ld", (long) f->nregs);
        DUK__COMMA(); duk_fb_sprintf(fb, "__nargs:%ld", (long) f->nargs);
#if defined(DUK_USE_DEBUGGER_SUPPORT)
        DUK__COMMA(); duk_fb_sprintf(fb, "__start_line:%ld", (long) f->start_line);
        DUK__COMMA(); duk_fb_sprintf(fb, "__end_line:%ld", (long) f->end_line);
#endif
        DUK__COMMA(); duk_fb_put_cstring(fb, "__data:");
        duk__print_hbuffer(st, (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(NULL, f));
    } else if (st->internal && DUK_HOBJECT_IS_NATFUNC(h)) {
        duk_hnatfunc *f = (duk_hnatfunc *) h;
        DUK__COMMA(); duk_fb_sprintf(fb, "__func:");
        duk_fb_put_funcptr(fb, (duk_uint8_t *) &f->func, sizeof(f->func));
        DUK__COMMA(); duk_fb_sprintf(fb, "__nargs:%ld", (long) f->nargs);
        DUK__COMMA(); duk_fb_sprintf(fb, "__magic:%ld", (long) f->magic);
    } else if (st->internal && DUK_HOBJECT_IS_DECENV(h)) {
        duk_hdecenv *e = (duk_hdecenv *) h;
        DUK__COMMA(); duk_fb_sprintf(fb, "__thread:"); duk__print_hobject(st, (duk_hobject *) e->thread);
        DUK__COMMA(); duk_fb_sprintf(fb, "__varmap:"); duk__print_hobject(st, (duk_hobject *) e->varmap);
        DUK__COMMA(); duk_fb_sprintf(fb, "__regbase_byteoff:%ld", (long) e->regbase_byteoff);
    } else if (st->internal && DUK_HOBJECT_IS_OBJENV(h)) {
        duk_hobjenv *e = (duk_hobjenv *) h;
        DUK__COMMA(); duk_fb_sprintf(fb, "__target:"); duk__print_hobject(st, (duk_hobject *) e->target);
        DUK__COMMA(); duk_fb_sprintf(fb, "__has_this:%ld", (long) e->has_this);
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
    } else if (st->internal && DUK_HOBJECT_IS_BUFOBJ(h)) {
        duk_hbufobj *b = (duk_hbufobj *) h;
        DUK__COMMA(); duk_fb_sprintf(fb, "__buf:");
        duk__print_hbuffer(st, (duk_hbuffer *) b->buf);
        DUK__COMMA(); duk_fb_sprintf(fb, "__buf_prop:");
        duk__print_hobject(st, (duk_hobject *) b->buf_prop);
        DUK__COMMA(); duk_fb_sprintf(fb, "__offset:%ld", (long) b->offset);
        DUK__COMMA(); duk_fb_sprintf(fb, "__length:%ld", (long) b->length);
        DUK__COMMA(); duk_fb_sprintf(fb, "__shift:%ld", (long) b->shift);
        DUK__COMMA(); duk_fb_sprintf(fb, "__elemtype:%ld", (long) b->elem_type);
#endif
    } else if (st->internal && DUK_HOBJECT_IS_PROXY(h)) {
        duk_hproxy *p = (duk_hproxy *) h;
        DUK__COMMA(); duk_fb_sprintf(fb, "__target:");
        duk__print_hobject(st, p->target);
        DUK__COMMA(); duk_fb_sprintf(fb, "__handler:");
        duk__print_hobject(st, p->handler);
    } else if (st->internal && DUK_HOBJECT_IS_THREAD(h)) {
        duk_hthread *t = (duk_hthread *) h;
        DUK__COMMA(); duk_fb_sprintf(fb, "__ptr_curr_pc:%p", (void *) t->ptr_curr_pc);
        DUK__COMMA(); duk_fb_sprintf(fb, "__heap:%p", (void *) t->heap);
        DUK__COMMA(); duk_fb_sprintf(fb, "__strict:%ld", (long) t->strict);
        DUK__COMMA(); duk_fb_sprintf(fb, "__state:%ld", (long) t->state);
        DUK__COMMA(); duk_fb_sprintf(fb, "__unused1:%ld", (long) t->unused1);
        DUK__COMMA(); duk_fb_sprintf(fb, "__unused2:%ld", (long) t->unused2);
        DUK__COMMA(); duk_fb_sprintf(fb, "__valstack:%p", (void *) t->valstack);
        DUK__COMMA(); duk_fb_sprintf(fb, "__valstack_end:%p/%ld", (void *) t->valstack_end, (long) (t->valstack_end - t->valstack));
        DUK__COMMA(); duk_fb_sprintf(fb, "__valstack_alloc_end:%p/%ld", (void *) t->valstack_alloc_end, (long) (t->valstack_alloc_end - t->valstack));
        DUK__COMMA(); duk_fb_sprintf(fb, "__valstack_bottom:%p/%ld", (void *) t->valstack_bottom, (long) (t->valstack_bottom - t->valstack));
        DUK__COMMA(); duk_fb_sprintf(fb, "__valstack_top:%p/%ld", (void *) t->valstack_top, (long) (t->valstack_top - t->valstack));
        DUK__COMMA(); duk_fb_sprintf(fb, "__callstack_curr:%p", (void *) t->callstack_curr);
        DUK__COMMA(); duk_fb_sprintf(fb, "__callstack_top:%ld", (long) t->callstack_top);
        DUK__COMMA(); duk_fb_sprintf(fb, "__callstack_preventcount:%ld", (long) t->callstack_preventcount);
        DUK__COMMA(); duk_fb_sprintf(fb, "__resumer:"); duk__print_hobject(st, (duk_hobject *) t->resumer);
        DUK__COMMA(); duk_fb_sprintf(fb, "__compile_ctx:%p", (void *) t->compile_ctx);
#if defined(DUK_USE_INTERRUPT_COUNTER)
        DUK__COMMA(); duk_fb_sprintf(fb, "__interrupt_counter:%ld", (long) t->interrupt_counter);
        DUK__COMMA(); duk_fb_sprintf(fb, "__interrupt_init:%ld", (long) t->interrupt_init);
#endif

        /* XXX: print built-ins array? */

    }
#if defined(DUK_USE_REFERENCE_COUNTING)
    if (st->internal) {
        DUK__COMMA(); duk_fb_sprintf(fb, "__refcount:%lu", (unsigned long) DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h));
    }
#endif
    if (st->internal) {
        DUK__COMMA(); duk_fb_sprintf(fb, "__class:%ld", (long) DUK_HOBJECT_GET_CLASS_NUMBER(h));
    }

    DUK__COMMA(); duk_fb_sprintf(fb, "__heapptr:%p", (void *) h);  /* own pointer */

    /* prototype should be last, for readability */
    if (DUK_HOBJECT_GET_PROTOTYPE(NULL, h)) {
        if (st->follow_proto) {
            DUK__COMMA(); duk_fb_put_cstring(fb, "__prototype:"); duk__print_hobject(st, DUK_HOBJECT_GET_PROTOTYPE(NULL, h));
        } else {
            DUK__COMMA(); duk_fb_sprintf(fb, "__prototype:%p", (void *) DUK_HOBJECT_GET_PROTOTYPE(NULL, h));
        }
    }

    duk_fb_put_cstring(fb, brace2);

#if defined(DUK_USE_HOBJECT_HASH_PART)
    if (st->heavy && DUK_HOBJECT_GET_HSIZE(h) > 0) {
        duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_LANGLE);
        for (i = 0; i < DUK_HOBJECT_GET_HSIZE(h); i++) {
            duk_uint_t h_idx = DUK_HOBJECT_H_GET_INDEX(NULL, h, i);
            if (i > 0) {
                duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_COMMA);
            }
            if (h_idx == DUK_HOBJECT_HASHIDX_UNUSED) {
                duk_fb_sprintf(fb, "u");
            } else if (h_idx == DUK_HOBJECT_HASHIDX_DELETED) {
                duk_fb_sprintf(fb, "d");
            } else {
                duk_fb_sprintf(fb, "%ld", (long) h_idx);
            }
        }
        duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RANGLE);
    }
#endif

 finished:
    st->depth--;
    if (pushed_loopstack) {
        st->loop_stack_index--;
        st->loop_stack[st->loop_stack_index] = NULL;
    }
}

DUK_LOCAL void duk__print_hbuffer(duk__dprint_state *st, duk_hbuffer *h) {
    duk_fixedbuffer *fb = st->fb;
    duk_size_t i, n;
    duk_uint8_t *p;

    if (duk_fb_is_full(fb)) {
        return;
    }

    /* terminal type: no depth check */

    if (!h) {
        duk_fb_put_cstring(fb, "NULL");
        return;
    }

    if (DUK_HBUFFER_HAS_DYNAMIC(h)) {
        if (DUK_HBUFFER_HAS_EXTERNAL(h)) {
            duk_hbuffer_external *g = (duk_hbuffer_external *) h;
            duk_fb_sprintf(fb, "buffer:external:%p:%ld",
                           (void *) DUK_HBUFFER_EXTERNAL_GET_DATA_PTR(NULL, g),
                           (long) DUK_HBUFFER_EXTERNAL_GET_SIZE(g));
        } else {
            duk_hbuffer_dynamic *g = (duk_hbuffer_dynamic *) h;
            duk_fb_sprintf(fb, "buffer:dynamic:%p:%ld",
                           (void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(NULL, g),
                           (long) DUK_HBUFFER_DYNAMIC_GET_SIZE(g));
        }
    } else {
        duk_fb_sprintf(fb, "buffer:fixed:%ld", (long) DUK_HBUFFER_GET_SIZE(h));
    }

#if defined(DUK_USE_REFERENCE_COUNTING)
    duk_fb_sprintf(fb, "/%lu", (unsigned long) DUK_HEAPHDR_GET_REFCOUNT(&h->hdr));
#endif

    if (st->hexdump) {
        duk_fb_sprintf(fb, "=[");
        n = DUK_HBUFFER_GET_SIZE(h);
        p = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(NULL, h);
        for (i = 0; i < n; i++) {
            duk_fb_sprintf(fb, "%02lx", (unsigned long) p[i]);
        }
        duk_fb_sprintf(fb, "]");
    }
}

DUK_LOCAL void duk__print_heaphdr(duk__dprint_state *st, duk_heaphdr *h) {
    duk_fixedbuffer *fb = st->fb;

    if (duk_fb_is_full(fb)) {
        return;
    }

    if (!h) {
        duk_fb_put_cstring(fb, "NULL");
        return;
    }

    switch (DUK_HEAPHDR_GET_TYPE(h)) {
    case DUK_HTYPE_STRING:
        duk__print_hstring(st, (duk_hstring *) h, 1);
        break;
    case DUK_HTYPE_OBJECT:
        duk__print_hobject(st, (duk_hobject *) h);
        break;
    case DUK_HTYPE_BUFFER:
        duk__print_hbuffer(st, (duk_hbuffer *) h);
        break;
    default:
        duk_fb_sprintf(fb, "[unknown htype %ld]", (long) DUK_HEAPHDR_GET_TYPE(h));
        break;
    }
}

DUK_LOCAL void duk__print_tval(duk__dprint_state *st, duk_tval *tv) {
    duk_fixedbuffer *fb = st->fb;

    if (duk_fb_is_full(fb)) {
        return;
    }

    /* depth check is done when printing an actual type */

    if (st->heavy) {
        duk_fb_sprintf(fb, "(%p)", (void *) tv);
    }

    if (!tv) {
        duk_fb_put_cstring(fb, "NULL");
        return;
    }

    if (st->binary) {
        duk_size_t i;
        duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_LBRACKET);
        for (i = 0; i < (duk_size_t) sizeof(*tv); i++) {
            duk_fb_sprintf(fb, "%02lx", (unsigned long) ((duk_uint8_t *)tv)[i]);
        }
        duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RBRACKET);
    }

    if (st->heavy) {
        duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_LANGLE);
    }
    switch (DUK_TVAL_GET_TAG(tv)) {
    case DUK_TAG_UNDEFINED: {
        duk_fb_put_cstring(fb, "undefined");
        break;
    }
    case DUK_TAG_UNUSED: {
        duk_fb_put_cstring(fb, "unused");
        break;
    }
    case DUK_TAG_NULL: {
        duk_fb_put_cstring(fb, "null");
        break;
    }
    case DUK_TAG_BOOLEAN: {
        duk_fb_put_cstring(fb, DUK_TVAL_GET_BOOLEAN(tv) ? "true" : "false");
        break;
    }
    case DUK_TAG_STRING: {
        /* Note: string is a terminal heap object, so no depth check here */
        duk__print_hstring(st, DUK_TVAL_GET_STRING(tv), 1);
        break;
    }
    case DUK_TAG_OBJECT: {
        duk__print_hobject(st, DUK_TVAL_GET_OBJECT(tv));
        break;
    }
    case DUK_TAG_BUFFER: {
        duk__print_hbuffer(st, DUK_TVAL_GET_BUFFER(tv));
        break;
    }
    case DUK_TAG_POINTER: {
        duk_fb_sprintf(fb, "pointer:%p", (void *) DUK_TVAL_GET_POINTER(tv));
        break;
    }
    case DUK_TAG_LIGHTFUNC: {
        duk_c_function func;
        duk_small_uint_t lf_flags;

        DUK_TVAL_GET_LIGHTFUNC(tv, func, lf_flags);
        duk_fb_sprintf(fb, "lightfunc:");
        duk_fb_put_funcptr(fb, (duk_uint8_t *) &func, sizeof(func));
        duk_fb_sprintf(fb, ":%04lx", (long) lf_flags);
        break;
    }
#if defined(DUK_USE_FASTINT)
    case DUK_TAG_FASTINT:
        DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
        DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
        duk_fb_sprintf(fb, "%.18g_F", (double) DUK_TVAL_GET_NUMBER(tv));
        break;
#endif
    default: {
        /* IEEE double is approximately 16 decimal digits; print a couple extra */
        DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
        DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
        duk_fb_sprintf(fb, "%.18g", (double) DUK_TVAL_GET_NUMBER(tv));
        break;
    }
    }
    if (st->heavy) {
        duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RANGLE);
    }
}

DUK_LOCAL void duk__print_instr(duk__dprint_state *st, duk_instr_t ins) {
    duk_fixedbuffer *fb = st->fb;
    duk_small_int_t op;
    const char *op_name;

    op = (duk_small_int_t) DUK_DEC_OP(ins);
    op_name = duk__bc_optab[op];

    /* XXX: option to fix opcode length so it lines up nicely */

    if (op == DUK_OP_JUMP) {
        duk_int_t diff1 = (duk_int_t) (DUK_DEC_ABC(ins) - DUK_BC_JUMP_BIAS);  /* from next pc */
        duk_int_t diff2 = diff1 + 1;                                          /* from curr pc */

        duk_fb_sprintf(fb, "%s %ld (to pc%c%ld)",
                       (const char *) op_name, (long) diff1,
                       (int) (diff2 >= 0 ? '+' : '-'),  /* char format: use int */
                       (long) (diff2 >= 0 ? diff2 : -diff2));
    } else {
        duk_fb_sprintf(fb, "%s %ld, %ld, %ld",
                       (const char *) op_name, (long) DUK_DEC_A(ins),
                       (long) DUK_DEC_B(ins), (long) DUK_DEC_C(ins));
    }
}

DUK_LOCAL void duk__print_opcode(duk__dprint_state *st, duk_small_int_t opcode) {
    duk_fixedbuffer *fb = st->fb;

    if (opcode < DUK_BC_OP_MIN || opcode > DUK_BC_OP_MAX) {
        duk_fb_sprintf(fb, "?(%ld)", (long) opcode);
    } else {
        duk_fb_sprintf(fb, "%s", (const char *) duk__bc_optab[opcode]);
    }
}

DUK_LOCAL void duk__print_catcher(duk__dprint_state *st, duk_catcher *cat) {
    duk_fixedbuffer *fb = st->fb;

    if (duk_fb_is_full(fb)) {
        return;
    }

    if (!cat) {
        duk_fb_put_cstring(fb, "NULL");
        return;
    }

    duk_fb_sprintf(fb, "[catcher ptr=%p parent=%p varname=%p pc_base=%p, idx_base=%ld, flags=0x%08lx]",
                   (void *) cat,
                   (void *) cat->parent, (void *) cat->h_varname, (void *) cat->pc_base,
               (long) cat->idx_base, (unsigned long) cat->flags);
}


DUK_LOCAL void duk__print_activation(duk__dprint_state *st, duk_activation *act) {
    duk_fixedbuffer *fb = st->fb;

    if (duk_fb_is_full(fb)) {
        return;
    }

    if (!act) {
        duk_fb_put_cstring(fb, "NULL");
        return;
    }

    /* prev_caller: conditional, omitted on purpose, it's rarely used. */
    /* prev_line: conditional, omitted on purpose (but would be nice). */
    duk_fb_sprintf(fb, "[activation ptr=%p tv_func=<omit> func=%p parent=%p var_env=%p lex_env=%p cat=%p curr_pc=%p bottom_byteoff=%ld retval_byteoff=%ld reserve_byteoff=%ld flags=%ld]",
                   (void *) act,
                   (void *) act->func, (void *) act->parent, (void *) act->var_env,
               (void *) act->lex_env, (void *) act->cat, (void *) act->curr_pc,
               (long) act->bottom_byteoff, (long) act->retval_byteoff, (long) act->reserve_byteoff,
               (long) act->flags);
}

DUK_INTERNAL duk_int_t duk_debug_vsnprintf(char *str, duk_size_t size, const char *format, va_list ap) {
    duk_fixedbuffer fb;
    const char *p = format;
    const char *p_end = p + DUK_STRLEN(format);
    duk_int_t retval;

    duk_memzero(&fb, sizeof(fb));
    fb.buffer = (duk_uint8_t *) str;
    fb.length = size;
    fb.offset = 0;
    fb.truncated = 0;

    while (p < p_end) {
        char ch = *p++;
        const char *p_begfmt = NULL;
        duk_bool_t got_exclamation = 0;
        duk_bool_t got_long = 0;  /* %lf, %ld etc */
        duk__dprint_state st;

        if (ch != DUK_ASC_PERCENT) {
            duk_fb_put_byte(&fb, (duk_uint8_t) ch);
            continue;
        }

        /*
         *  Format tag parsing.  Since we don't understand all the
         *  possible format tags allowed, we just scan for a terminating
         *  specifier and keep track of relevant modifiers that we do
         *  understand.  See man 3 printf.
         */

        duk_memzero(&st, sizeof(st));
        st.fb = &fb;
        st.depth = 0;
        st.depth_limit = 1;
        st.loop_stack_index = 0;
        st.loop_stack_limit = DUK__LOOP_STACK_DEPTH;

        p_begfmt = p - 1;
        while (p < p_end) {
            ch = *p++;

            if (ch == DUK_ASC_STAR) {
                /* unsupported: would consume multiple args */
                goto format_error;
            } else if (ch == DUK_ASC_PERCENT) {
                duk_fb_put_byte(&fb, (duk_uint8_t) DUK_ASC_PERCENT);
                break;
            } else if (ch == DUK_ASC_EXCLAMATION) {
                got_exclamation = 1;
            } else if (!got_exclamation && ch == DUK_ASC_LC_L) {
                got_long = 1;
            } else if (got_exclamation && ch == DUK_ASC_LC_D) {
                st.depth_limit = DUK__DEEP_DEPTH_LIMIT;
            } else if (got_exclamation && ch == DUK_ASC_LC_P) {
                st.follow_proto = 1;
            } else if (got_exclamation && ch == DUK_ASC_LC_I) {
                st.internal = 1;
            } else if (got_exclamation && ch == DUK_ASC_LC_X) {
                st.hexdump = 1;
            } else if (got_exclamation && ch == DUK_ASC_LC_H) {
                st.heavy = 1;
            } else if (got_exclamation && ch == DUK_ASC_ATSIGN) {
                st.pointer = 1;
            } else if (got_exclamation && ch == DUK_ASC_HASH) {
                st.binary = 1;
            } else if (got_exclamation && ch == DUK_ASC_UC_T) {
                duk_tval *t = va_arg(ap, duk_tval *);
                if (st.pointer && !st.heavy) {
                    duk_fb_sprintf(&fb, "(%p)", (void *) t);
                }
                duk__print_tval(&st, t);
                break;
            } else if (got_exclamation && ch == DUK_ASC_UC_O) {
                duk_heaphdr *t = va_arg(ap, duk_heaphdr *);
                if (st.pointer && !st.heavy) {
                    duk_fb_sprintf(&fb, "(%p)", (void *) t);
                }
                duk__print_heaphdr(&st, t);
                break;
            } else if (got_exclamation && ch == DUK_ASC_UC_I) {
                duk_instr_t t = va_arg(ap, duk_instr_t);
                duk__print_instr(&st, t);
                break;
            } else if (got_exclamation && ch == DUK_ASC_UC_X) {
                long t = va_arg(ap, long);
                duk__print_opcode(&st, (duk_small_int_t) t);
                break;
            } else if (got_exclamation && ch == DUK_ASC_UC_C) {
                duk_catcher *t = va_arg(ap, duk_catcher *);
                duk__print_catcher(&st, t);
                break;
            } else if (got_exclamation && ch == DUK_ASC_UC_A) {
                duk_activation *t = va_arg(ap, duk_activation *);
                duk__print_activation(&st, t);
                break;
            } else if (!got_exclamation && strchr(DUK__ALLOWED_STANDARD_SPECIFIERS, (int) ch)) {
                char fmtbuf[DUK__MAX_FORMAT_TAG_LENGTH];
                duk_size_t fmtlen;

                DUK_ASSERT(p >= p_begfmt);
                fmtlen = (duk_size_t) (p - p_begfmt);
                if (fmtlen >= sizeof(fmtbuf)) {
                    /* format is too large, abort */
                    goto format_error;
                }
                duk_memzero(fmtbuf, sizeof(fmtbuf));
                duk_memcpy(fmtbuf, p_begfmt, fmtlen);

                /* assume exactly 1 arg, which is why '*' is forbidden; arg size still
                 * depends on type though.
                 */

                if (ch == DUK_ASC_LC_F || ch == DUK_ASC_LC_G || ch == DUK_ASC_LC_E) {
                    /* %f and %lf both consume a 'long' */
                    double arg = va_arg(ap, double);
                    duk_fb_sprintf(&fb, fmtbuf, arg);
                } else if (ch == DUK_ASC_LC_D && got_long) {
                    /* %ld */
                    long arg = va_arg(ap, long);
                    duk_fb_sprintf(&fb, fmtbuf, arg);
                } else if (ch == DUK_ASC_LC_D) {
                    /* %d; only 16 bits are guaranteed */
                    int arg = va_arg(ap, int);
                    duk_fb_sprintf(&fb, fmtbuf, arg);
                } else if (ch == DUK_ASC_LC_U && got_long) {
                    /* %lu */
                    unsigned long arg = va_arg(ap, unsigned long);
                    duk_fb_sprintf(&fb, fmtbuf, arg);
                } else if (ch == DUK_ASC_LC_U) {
                    /* %u; only 16 bits are guaranteed */
                    unsigned int arg = va_arg(ap, unsigned int);
                    duk_fb_sprintf(&fb, fmtbuf, arg);
                } else if (ch == DUK_ASC_LC_X && got_long) {
                    /* %lx */
                    unsigned long arg = va_arg(ap, unsigned long);
                    duk_fb_sprintf(&fb, fmtbuf, arg);
                } else if (ch == DUK_ASC_LC_X) {
                    /* %x; only 16 bits are guaranteed */
                    unsigned int arg = va_arg(ap, unsigned int);
                    duk_fb_sprintf(&fb, fmtbuf, arg);
                } else if (ch == DUK_ASC_LC_S) {
                    /* %s */
                    const char *arg = va_arg(ap, const char *);
                    if (arg == NULL) {
                        /* '%s' and NULL is not portable, so special case
                         * it for debug printing.
                         */
                        duk_fb_sprintf(&fb, "NULL");
                    } else {
                        duk_fb_sprintf(&fb, fmtbuf, arg);
                    }
                } else if (ch == DUK_ASC_LC_P) {
                    /* %p */
                    void *arg = va_arg(ap, void *);
                    if (arg == NULL) {
                        /* '%p' and NULL is portable, but special case it
                         * anyway to get a standard NULL marker in logs.
                         */
                        duk_fb_sprintf(&fb, "NULL");
                    } else {
                        duk_fb_sprintf(&fb, fmtbuf, arg);
                    }
                } else if (ch == DUK_ASC_LC_C) {
                    /* '%c', passed concretely as int */
                    int arg = va_arg(ap, int);
                    duk_fb_sprintf(&fb, fmtbuf, arg);
                } else {
                    /* Should not happen. */
                    duk_fb_sprintf(&fb, "INVALID-FORMAT(%s)", (const char *) fmtbuf);
                }
                break;
            } else {
                /* ignore */
            }
        }
    }
    goto done;

 format_error:
    duk_fb_put_cstring(&fb, "FMTERR");
    /* fall through */

 done:
    retval = (duk_int_t) fb.offset;
    duk_fb_put_byte(&fb, (duk_uint8_t) 0);

    /* return total chars written excluding terminator */
    return retval;
}

#if 0  /*unused*/
DUK_INTERNAL duk_int_t duk_debug_snprintf(char *str, duk_size_t size, const char *format, ...) {
    duk_int_t retval;
    va_list ap;
    va_start(ap, format);
    retval = duk_debug_vsnprintf(str, size, format, ap);
    va_end(ap);
    return retval;
}
#endif

/* Formatting function pointers is tricky: there is no standard pointer for
 * function pointers and the size of a function pointer may depend on the
 * specific pointer type.  This helper formats a function pointer based on
 * its memory layout to get something useful on most platforms.
 */
DUK_INTERNAL void duk_debug_format_funcptr(char *buf, duk_size_t buf_size, duk_uint8_t *fptr, duk_size_t fptr_size) {
    duk_size_t i;
    duk_uint8_t *p = (duk_uint8_t *) buf;
    duk_uint8_t *p_end = (duk_uint8_t *) (buf + buf_size - 1);

    DUK_ASSERT(buf != NULL);
    duk_memzero(buf, buf_size);

    for (i = 0; i < fptr_size; i++) {
        duk_int_t left = (duk_int_t) (p_end - p);
        duk_uint8_t ch;
        if (left <= 0) {
            break;
        }

        /* Quite approximate but should be useful for little and big endian. */
#if defined(DUK_USE_INTEGER_BE)
        ch = fptr[i];
#else
        ch = fptr[fptr_size - 1 - i];
#endif
        p += DUK_SNPRINTF((char *) p, (duk_size_t) left, "%02lx", (unsigned long) ch);
    }
}

#endif  /* DUK_USE_DEBUG */

/* automatic undefs */
#undef DUK__ALLOWED_STANDARD_SPECIFIERS
#undef DUK__COMMA
#undef DUK__DEEP_DEPTH_LIMIT
#undef DUK__LOOP_STACK_DEPTH
#undef DUK__MAX_FORMAT_TAG_LENGTH
//#line 1 "duk_debugger.c"
/*
 *  Duktape debugger
 */

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

#if defined(DUK_USE_DEBUGGER_SUPPORT)

/*
 *  Assert helpers
 */

#if defined(DUK_USE_ASSERTIONS)
#define DUK__DBG_TPORT_ENTER() do { \
        DUK_ASSERT(heap->dbg_calling_transport == 0); \
        heap->dbg_calling_transport = 1; \
    } while (0)
#define DUK__DBG_TPORT_EXIT() do { \
        DUK_ASSERT(heap->dbg_calling_transport == 1); \
        heap->dbg_calling_transport = 0; \
    } while (0)
#else
#define DUK__DBG_TPORT_ENTER() do {} while (0)
#define DUK__DBG_TPORT_EXIT() do {} while (0)
#endif

/*
 *  Helper structs
 */

typedef union {
    void *p;
    duk_uint_t b[1];
    /* Use b[] to access the size of the union, which is strictly not
     * correct.  Can't use fixed size unless there's feature detection
     * for pointer byte size.
     */
} duk__ptr_union;

/*
 *  Detach handling
 */

#define DUK__SET_CONN_BROKEN(thr,reason) do { \
        /* For now shared handler is fine. */ \
        duk__debug_do_detach1((thr)->heap, (reason)); \
    } while (0)

DUK_LOCAL void duk__debug_do_detach1(duk_heap *heap, duk_int_t reason) {
    /* Can be called multiple times with no harm.  Mark the transport
     * bad (dbg_read_cb == NULL) and clear state except for the detached
     * callback and the udata field.  The detached callback is delayed
     * to the message loop so that it can be called between messages;
     * this avoids corner cases related to immediate debugger reattach
     * inside the detached callback.
     */

    if (heap->dbg_detaching) {
        DUK_D(DUK_DPRINT("debugger already detaching, ignore detach1"));
        return;
    }

    DUK_D(DUK_DPRINT("debugger transport detaching, marking transport broken"));

    heap->dbg_detaching = 1;  /* prevent multiple in-progress detaches */

    if (heap->dbg_write_cb != NULL) {
        duk_hthread *thr;

        thr = heap->heap_thread;
        DUK_ASSERT(thr != NULL);

        duk_debug_write_notify(thr, DUK_DBG_CMD_DETACHING);
        duk_debug_write_int(thr, reason);
        duk_debug_write_eom(thr);
    }

    heap->dbg_read_cb = NULL;
    heap->dbg_write_cb = NULL;
    heap->dbg_peek_cb = NULL;
    heap->dbg_read_flush_cb = NULL;
    heap->dbg_write_flush_cb = NULL;
    heap->dbg_request_cb = NULL;
    /* heap->dbg_detached_cb: keep */
    /* heap->dbg_udata: keep */
    /* heap->dbg_processing: keep on purpose to avoid debugger re-entry in detaching state */
    heap->dbg_state_dirty = 0;
    heap->dbg_force_restart = 0;
    heap->dbg_pause_flags = 0;
    heap->dbg_pause_act = NULL;
    heap->dbg_pause_startline = 0;
    heap->dbg_have_next_byte = 0;
    duk_debug_clear_paused(heap);  /* XXX: some overlap with field inits above */
    heap->dbg_state_dirty = 0;     /* XXX: clear_paused sets dirty; rework? */

    /* Ensure there are no stale active breakpoint pointers.
     * Breakpoint list is currently kept - we could empty it
     * here but we'd need to handle refcounts correctly, and
     * we'd need a 'thr' reference for that.
     *
     * XXX: clear breakpoint on either attach or detach?
     */
    heap->dbg_breakpoints_active[0] = (duk_breakpoint *) NULL;
}

DUK_LOCAL void duk__debug_do_detach2(duk_heap *heap) {
    duk_debug_detached_function detached_cb;
    void *detached_udata;
    duk_hthread *thr;

    thr = heap->heap_thread;
    if (thr == NULL) {
        DUK_ASSERT(heap->dbg_detached_cb == NULL);
        return;
    }

    /* Safe to call multiple times. */

    detached_cb = heap->dbg_detached_cb;
    detached_udata = heap->dbg_udata;
    heap->dbg_detached_cb = NULL;
    heap->dbg_udata = NULL;

    if (detached_cb) {
        /* Careful here: state must be wiped before the call
         * so that we can cleanly handle a re-attach from
         * inside the callback.
         */
        DUK_D(DUK_DPRINT("detached during message loop, delayed call to detached_cb"));
        detached_cb(thr, detached_udata);
    }

    heap->dbg_detaching = 0;
}

DUK_INTERNAL void duk_debug_do_detach(duk_heap *heap) {
    duk__debug_do_detach1(heap, 0);
    duk__debug_do_detach2(heap);
}

/* Called on a read/write error: NULL all callbacks except the detached
 * callback so that we never accidentally call them after a read/write
 * error has been indicated.  This is especially important for the transport
 * I/O callbacks to fulfill guaranteed callback semantics.
 */
DUK_LOCAL void duk__debug_null_most_callbacks(duk_hthread *thr) {
    duk_heap *heap;

    DUK_ASSERT(thr != NULL);

    heap = thr->heap;
    DUK_D(DUK_DPRINT("transport read/write error, NULL all callbacks expected detached"));
    heap->dbg_read_cb = NULL;
    heap->dbg_write_cb = NULL;  /* this is especially critical to avoid another write call in detach1() */
    heap->dbg_peek_cb = NULL;
    heap->dbg_read_flush_cb = NULL;
    heap->dbg_write_flush_cb = NULL;
    heap->dbg_request_cb = NULL;
    /* keep heap->dbg_detached_cb */
}

/*
 *  Pause handling
 */

DUK_LOCAL void duk__debug_set_pause_state(duk_hthread *thr, duk_heap *heap, duk_small_uint_t pause_flags) {
    duk_uint_fast32_t line;

    line = duk_debug_curr_line(thr);
    if (line == 0) {
        /* No line info for current function. */
        duk_small_uint_t updated_flags;

        updated_flags = pause_flags & ~(DUK_PAUSE_FLAG_LINE_CHANGE);
        DUK_D(DUK_DPRINT("no line info for current activation, disable line-based pause flags: 0x%08lx -> 0x%08lx",
                         (long) pause_flags, (long) updated_flags));
        pause_flags = updated_flags;
    }

    heap->dbg_pause_flags = pause_flags;
    heap->dbg_pause_act = thr->callstack_curr;
    heap->dbg_pause_startline = (duk_uint32_t) line;
    heap->dbg_state_dirty = 1;

    DUK_D(DUK_DPRINT("set state for automatic pause triggers, flags=0x%08lx, act=%p, startline=%ld",
                     (long) heap->dbg_pause_flags, (void *) heap->dbg_pause_act,
                     (long) heap->dbg_pause_startline));
}

/*
 *  Debug connection peek and flush primitives
 */

DUK_INTERNAL duk_bool_t duk_debug_read_peek(duk_hthread *thr) {
    duk_heap *heap;
    duk_bool_t ret;

    DUK_ASSERT(thr != NULL);
    heap = thr->heap;
    DUK_ASSERT(heap != NULL);

    if (heap->dbg_read_cb == NULL) {
        DUK_D(DUK_DPRINT("attempt to peek in detached state, return zero (= no data)"));
        return 0;
    }
    if (heap->dbg_peek_cb == NULL) {
        DUK_DD(DUK_DDPRINT("no peek callback, return zero (= no data)"));
        return 0;
    }

    DUK__DBG_TPORT_ENTER();
    ret = (duk_bool_t) (heap->dbg_peek_cb(heap->dbg_udata) > 0);
    DUK__DBG_TPORT_EXIT();
    return ret;
}

DUK_INTERNAL void duk_debug_read_flush(duk_hthread *thr) {
    duk_heap *heap;

    DUK_ASSERT(thr != NULL);
    heap = thr->heap;
    DUK_ASSERT(heap != NULL);

    if (heap->dbg_read_cb == NULL) {
        DUK_D(DUK_DPRINT("attempt to read flush in detached state, ignore"));
        return;
    }
    if (heap->dbg_read_flush_cb == NULL) {
        DUK_DD(DUK_DDPRINT("no read flush callback, ignore"));
        return;
    }

    DUK__DBG_TPORT_ENTER();
    heap->dbg_read_flush_cb(heap->dbg_udata);
    DUK__DBG_TPORT_EXIT();
}

DUK_INTERNAL void duk_debug_write_flush(duk_hthread *thr) {
    duk_heap *heap;

    DUK_ASSERT(thr != NULL);
    heap = thr->heap;
    DUK_ASSERT(heap != NULL);

    if (heap->dbg_read_cb == NULL) {
        DUK_D(DUK_DPRINT("attempt to write flush in detached state, ignore"));
        return;
    }
    if (heap->dbg_write_flush_cb == NULL) {
        DUK_DD(DUK_DDPRINT("no write flush callback, ignore"));
        return;
    }

    DUK__DBG_TPORT_ENTER();
    heap->dbg_write_flush_cb(heap->dbg_udata);
    DUK__DBG_TPORT_EXIT();
}

/*
 *  Debug connection skip primitives
 */

/* Skip fully. */
DUK_INTERNAL void duk_debug_skip_bytes(duk_hthread *thr, duk_size_t length) {
    duk_uint8_t dummy[64];
    duk_size_t now;

    DUK_ASSERT(thr != NULL);

    while (length > 0) {
        now = (length > sizeof(dummy) ? sizeof(dummy) : length);
        duk_debug_read_bytes(thr, dummy, now);
        length -= now;
    }
}

DUK_INTERNAL void duk_debug_skip_byte(duk_hthread *thr) {
    DUK_ASSERT(thr != NULL);

    (void) duk_debug_read_byte(thr);
}

/*
 *  Debug connection read primitives
 */

/* Peek ahead in the stream one byte. */
DUK_INTERNAL uint8_t duk_debug_peek_byte(duk_hthread *thr) {
    /* It is important not to call this if the last byte read was an EOM.
     * Reading ahead in this scenario would cause unnecessary blocking if
     * another message is not available.
     */

    duk_uint8_t x;

    x = duk_debug_read_byte(thr);
    thr->heap->dbg_have_next_byte = 1;
    thr->heap->dbg_next_byte = x;
    return x;
}

/* Read fully. */
DUK_INTERNAL void duk_debug_read_bytes(duk_hthread *thr, duk_uint8_t *data, duk_size_t length) {
    duk_heap *heap;
    duk_uint8_t *p;
    duk_size_t left;
    duk_size_t got;

    DUK_ASSERT(thr != NULL);
    heap = thr->heap;
    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(data != NULL);

    if (heap->dbg_read_cb == NULL) {
        DUK_D(DUK_DPRINT("attempt to read %ld bytes in detached state, return zero data", (long) length));
        goto fail;
    }

    /* NOTE: length may be zero */
    p = data;
    if (length >= 1 && heap->dbg_have_next_byte) {
        heap->dbg_have_next_byte = 0;
        *p++ = heap->dbg_next_byte;
    }
    for (;;) {
        left = (duk_size_t) ((data + length) - p);
        if (left == 0) {
            break;
        }
        DUK_ASSERT(heap->dbg_read_cb != NULL);
        DUK_ASSERT(left >= 1);
#if defined(DUK_USE_DEBUGGER_TRANSPORT_TORTURE)
        left = 1;
#endif
        DUK__DBG_TPORT_ENTER();
        got = heap->dbg_read_cb(heap->dbg_udata, (char *) p, left);
        DUK__DBG_TPORT_EXIT();

        if (got == 0 || got > left) {
            DUK_D(DUK_DPRINT("connection error during read, return zero data"));
            duk__debug_null_most_callbacks(thr);  /* avoid calling write callback in detach1() */
            DUK__SET_CONN_BROKEN(thr, 1);
            goto fail;
        }
        p += got;
    }
    return;

 fail:
    duk_memzero((void *) data, (size_t) length);
}

DUK_INTERNAL duk_uint8_t duk_debug_read_byte(duk_hthread *thr) {
    duk_uint8_t x;

    x = 0;  /* just in case callback is broken and won't write 'x' */
    duk_debug_read_bytes(thr, &x, 1);
    return x;
}

DUK_LOCAL duk_uint32_t duk__debug_read_uint32_raw(duk_hthread *thr) {
    duk_uint8_t buf[4];

    DUK_ASSERT(thr != NULL);

    duk_debug_read_bytes(thr, buf, 4);
    return ((duk_uint32_t) buf[0] << 24) |
           ((duk_uint32_t) buf[1] << 16) |
           ((duk_uint32_t) buf[2] << 8) |
           (duk_uint32_t) buf[3];
}

DUK_LOCAL duk_int32_t duk__debug_read_int32_raw(duk_hthread *thr) {
    return (duk_int32_t) duk__debug_read_uint32_raw(thr);
}

DUK_LOCAL duk_uint16_t duk__debug_read_uint16_raw(duk_hthread *thr) {
    duk_uint8_t buf[2];

    DUK_ASSERT(thr != NULL);

    duk_debug_read_bytes(thr, buf, 2);
    return ((duk_uint16_t) buf[0] << 8) |
           (duk_uint16_t) buf[1];
}

DUK_INTERNAL duk_int32_t duk_debug_read_int(duk_hthread *thr) {
    duk_small_uint_t x;
    duk_small_uint_t t;

    DUK_ASSERT(thr != NULL);

    x = duk_debug_read_byte(thr);
    if (x >= 0xc0) {
        t = duk_debug_read_byte(thr);
        return (duk_int32_t) (((x - 0xc0) << 8) + t);
    } else if (x >= 0x80) {
        return (duk_int32_t) (x - 0x80);
    } else if (x == DUK_DBG_IB_INT4) {
        return (duk_int32_t) duk__debug_read_uint32_raw(thr);
    }

    DUK_D(DUK_DPRINT("debug connection error: failed to decode int"));
    DUK__SET_CONN_BROKEN(thr, 1);
    return 0;
}

DUK_LOCAL duk_hstring *duk__debug_read_hstring_raw(duk_hthread *thr, duk_uint32_t len) {
    duk_uint8_t buf[31];
    duk_uint8_t *p;

    if (len <= sizeof(buf)) {
        duk_debug_read_bytes(thr, buf, (duk_size_t) len);
        duk_push_lstring(thr, (const char *) buf, (duk_size_t) len);
    } else {
        p = (duk_uint8_t *) duk_push_fixed_buffer(thr, (duk_size_t) len);  /* zero for paranoia */
        DUK_ASSERT(p != NULL);
        duk_debug_read_bytes(thr, p, (duk_size_t) len);
        (void) duk_buffer_to_string(thr, -1);  /* Safety relies on debug client, which is OK. */
    }

    return duk_require_hstring(thr, -1);
}

DUK_INTERNAL duk_hstring *duk_debug_read_hstring(duk_hthread *thr) {
    duk_small_uint_t x;
    duk_uint32_t len;

    DUK_ASSERT(thr != NULL);

    x = duk_debug_read_byte(thr);
    if (x >= 0x60 && x <= 0x7f) {
        /* For short strings, use a fixed temp buffer. */
        len = (duk_uint32_t) (x - 0x60);
    } else if (x == DUK_DBG_IB_STR2) {
        len = (duk_uint32_t) duk__debug_read_uint16_raw(thr);
    } else if (x == DUK_DBG_IB_STR4) {
        len = (duk_uint32_t) duk__debug_read_uint32_raw(thr);
    } else {
        goto fail;
    }

    return duk__debug_read_hstring_raw(thr, len);

 fail:
    DUK_D(DUK_DPRINT("debug connection error: failed to decode int"));
    DUK__SET_CONN_BROKEN(thr, 1);
    duk_push_hstring_empty(thr);  /* always push some string */
    return duk_require_hstring(thr, -1);
}

DUK_LOCAL duk_hbuffer *duk__debug_read_hbuffer_raw(duk_hthread *thr, duk_uint32_t len) {
    duk_uint8_t *p;

    p = (duk_uint8_t *) duk_push_fixed_buffer(thr, (duk_size_t) len);  /* zero for paranoia */
    DUK_ASSERT(p != NULL);
    duk_debug_read_bytes(thr, p, (duk_size_t) len);

    return duk_require_hbuffer(thr, -1);
}

DUK_LOCAL void *duk__debug_read_pointer_raw(duk_hthread *thr) {
    duk_small_uint_t x;
    duk__ptr_union pu;

    DUK_ASSERT(thr != NULL);

    x = duk_debug_read_byte(thr);
    if (x != sizeof(pu)) {
        goto fail;
    }
    duk_debug_read_bytes(thr, (duk_uint8_t *) &pu.p, sizeof(pu));
#if defined(DUK_USE_INTEGER_LE)
    duk_byteswap_bytes((duk_uint8_t *) pu.b, sizeof(pu));
#endif
    return (void *) pu.p;

 fail:
    DUK_D(DUK_DPRINT("debug connection error: failed to decode pointer"));
    DUK__SET_CONN_BROKEN(thr, 1);
    return (void *) NULL;
}

DUK_LOCAL duk_double_t duk__debug_read_double_raw(duk_hthread *thr) {
    duk_double_union du;

    DUK_ASSERT(sizeof(du.uc) == 8);
    duk_debug_read_bytes(thr, (duk_uint8_t *) du.uc, sizeof(du.uc));
    DUK_DBLUNION_DOUBLE_NTOH(&du);
    return du.d;
}

#if 0
DUK_INTERNAL duk_heaphdr *duk_debug_read_heapptr(duk_hthread *thr) {
    duk_small_uint_t x;

    DUK_ASSERT(thr != NULL);

    x = duk_debug_read_byte(thr);
    if (x != DUK_DBG_IB_HEAPPTR) {
        goto fail;
    }

    return (duk_heaphdr *) duk__debug_read_pointer_raw(thr);

 fail:
    DUK_D(DUK_DPRINT("debug connection error: failed to decode heapptr"));
    DUK__SET_CONN_BROKEN(thr, 1);
    return NULL;
}
#endif

DUK_INTERNAL duk_heaphdr *duk_debug_read_any_ptr(duk_hthread *thr) {
    duk_small_uint_t x;

    DUK_ASSERT(thr != NULL);

    x = duk_debug_read_byte(thr);
    switch (x) {
    case DUK_DBG_IB_OBJECT:
    case DUK_DBG_IB_POINTER:
    case DUK_DBG_IB_HEAPPTR:
        /* Accept any pointer-like value; for 'object' dvalue, read
         * and ignore the class number.
         */
        if (x == DUK_DBG_IB_OBJECT) {
            duk_debug_skip_byte(thr);
        }
        break;
    default:
        goto fail;
    }

    return (duk_heaphdr *) duk__debug_read_pointer_raw(thr);

 fail:
    DUK_D(DUK_DPRINT("debug connection error: failed to decode any pointer (object, pointer, heapptr)"));
    DUK__SET_CONN_BROKEN(thr, 1);
    return NULL;
}

DUK_INTERNAL duk_tval *duk_debug_read_tval(duk_hthread *thr) {
    duk_uint8_t x;
    duk_uint_t t;
    duk_uint32_t len;

    DUK_ASSERT(thr != NULL);

    x = duk_debug_read_byte(thr);

    if (x >= 0xc0) {
        t = (duk_uint_t) (x - 0xc0);
        t = (t << 8) + duk_debug_read_byte(thr);
        duk_push_uint(thr, (duk_uint_t) t);
        goto return_ptr;
    }
    if (x >= 0x80) {
        duk_push_uint(thr, (duk_uint_t) (x - 0x80));
        goto return_ptr;
    }
    if (x >= 0x60) {
        len = (duk_uint32_t) (x - 0x60);
        duk__debug_read_hstring_raw(thr, len);
        goto return_ptr;
    }

    switch (x) {
    case DUK_DBG_IB_INT4: {
        duk_int32_t i = duk__debug_read_int32_raw(thr);
        duk_push_i32(thr, i);
        break;
    }
    case DUK_DBG_IB_STR4: {
        len = duk__debug_read_uint32_raw(thr);
        duk__debug_read_hstring_raw(thr, len);
        break;
    }
    case DUK_DBG_IB_STR2: {
        len = duk__debug_read_uint16_raw(thr);
        duk__debug_read_hstring_raw(thr, len);
        break;
    }
    case DUK_DBG_IB_BUF4: {
        len = duk__debug_read_uint32_raw(thr);
        duk__debug_read_hbuffer_raw(thr, len);
        break;
    }
    case DUK_DBG_IB_BUF2: {
        len = duk__debug_read_uint16_raw(thr);
        duk__debug_read_hbuffer_raw(thr, len);
        break;
    }
    case DUK_DBG_IB_UNDEFINED: {
        duk_push_undefined(thr);
        break;
    }
    case DUK_DBG_IB_NULL: {
        duk_push_null(thr);
        break;
    }
    case DUK_DBG_IB_TRUE: {
        duk_push_true(thr);
        break;
    }
    case DUK_DBG_IB_FALSE: {
        duk_push_false(thr);
        break;
    }
    case DUK_DBG_IB_NUMBER: {
        duk_double_t d;
        d = duk__debug_read_double_raw(thr);
        duk_push_number(thr, d);
        break;
    }
    case DUK_DBG_IB_OBJECT: {
        duk_heaphdr *h;
        duk_debug_skip_byte(thr);
        h = (duk_heaphdr *) duk__debug_read_pointer_raw(thr);
        duk_push_heapptr(thr, (void *) h);
        break;
    }
    case DUK_DBG_IB_POINTER: {
        void *ptr;
        ptr = duk__debug_read_pointer_raw(thr);
        duk_push_pointer(thr, ptr);
        break;
    }
    case DUK_DBG_IB_LIGHTFUNC: {
        /* XXX: Not needed for now, so not implemented.  Note that
         * function pointers may have different size/layout than
         * a void pointer.
         */
        DUK_D(DUK_DPRINT("reading lightfunc values unimplemented"));
        goto fail;
    }
    case DUK_DBG_IB_HEAPPTR: {
        duk_heaphdr *h;
        h = (duk_heaphdr *) duk__debug_read_pointer_raw(thr);
        duk_push_heapptr(thr, (void *) h);
        break;
    }
    case DUK_DBG_IB_UNUSED:  /* unused: not accepted in inbound messages */
    default:
        goto fail;
    }

 return_ptr:
    return DUK_GET_TVAL_NEGIDX(thr, -1);

 fail:
    DUK_D(DUK_DPRINT("debug connection error: failed to decode tval"));
    DUK__SET_CONN_BROKEN(thr, 1);
    return NULL;
}

/*
 *  Debug connection write primitives
 */

/* Write fully. */
DUK_INTERNAL void duk_debug_write_bytes(duk_hthread *thr, const duk_uint8_t *data, duk_size_t length) {
    duk_heap *heap;
    const duk_uint8_t *p;
    duk_size_t left;
    duk_size_t got;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(length == 0 || data != NULL);
    heap = thr->heap;
    DUK_ASSERT(heap != NULL);

    if (heap->dbg_write_cb == NULL) {
        DUK_D(DUK_DPRINT("attempt to write %ld bytes in detached state, ignore", (long) length));
        return;
    }
    if (length == 0) {
        /* Avoid doing an actual write callback with length == 0,
         * because that's reserved for a write flush.
         */
        return;
    }
    DUK_ASSERT(data != NULL);

    p = data;
    for (;;) {
        left = (duk_size_t) ((data + length) - p);
        if (left == 0) {
            break;
        }
        DUK_ASSERT(heap->dbg_write_cb != NULL);
        DUK_ASSERT(left >= 1);
#if defined(DUK_USE_DEBUGGER_TRANSPORT_TORTURE)
        left = 1;
#endif
        DUK__DBG_TPORT_ENTER();
        got = heap->dbg_write_cb(heap->dbg_udata, (const char *) p, left);
        DUK__DBG_TPORT_EXIT();

        if (got == 0 || got > left) {
            duk__debug_null_most_callbacks(thr);  /* avoid calling write callback in detach1() */
            DUK_D(DUK_DPRINT("connection error during write"));
            DUK__SET_CONN_BROKEN(thr, 1);
            return;
        }
        p += got;
    }
}

DUK_INTERNAL void duk_debug_write_byte(duk_hthread *thr, duk_uint8_t x) {
    duk_debug_write_bytes(thr, (const duk_uint8_t *) &x, 1);
}

DUK_INTERNAL void duk_debug_write_unused(duk_hthread *thr) {
    duk_debug_write_byte(thr, DUK_DBG_IB_UNUSED);
}

DUK_INTERNAL void duk_debug_write_undefined(duk_hthread *thr) {
    duk_debug_write_byte(thr, DUK_DBG_IB_UNDEFINED);
}

#if defined(DUK_USE_DEBUGGER_INSPECT)
DUK_INTERNAL void duk_debug_write_null(duk_hthread *thr) {
    duk_debug_write_byte(thr, DUK_DBG_IB_NULL);
}
#endif

DUK_INTERNAL void duk_debug_write_boolean(duk_hthread *thr, duk_uint_t val) {
    duk_debug_write_byte(thr, val ? DUK_DBG_IB_TRUE : DUK_DBG_IB_FALSE);
}

/* Write signed 32-bit integer. */
DUK_INTERNAL void duk_debug_write_int(duk_hthread *thr, duk_int32_t x) {
    duk_uint8_t buf[5];
    duk_size_t len;

    DUK_ASSERT(thr != NULL);

    if (x >= 0 && x <= 0x3fL) {
        buf[0] = (duk_uint8_t) (0x80 + x);
        len = 1;
    } else if (x >= 0 && x <= 0x3fffL) {
        buf[0] = (duk_uint8_t) (0xc0 + (x >> 8));
        buf[1] = (duk_uint8_t) (x & 0xff);
        len = 2;
    } else {
        /* Signed integers always map to 4 bytes now. */
        buf[0] = (duk_uint8_t) DUK_DBG_IB_INT4;
        buf[1] = (duk_uint8_t) ((x >> 24) & 0xff);
        buf[2] = (duk_uint8_t) ((x >> 16) & 0xff);
        buf[3] = (duk_uint8_t) ((x >> 8) & 0xff);
        buf[4] = (duk_uint8_t) (x & 0xff);
        len = 5;
    }
    duk_debug_write_bytes(thr, buf, len);
}

/* Write unsigned 32-bit integer. */
DUK_INTERNAL void duk_debug_write_uint(duk_hthread *thr, duk_uint32_t x) {
    /* The debugger protocol doesn't support a plain integer encoding for
     * the full 32-bit unsigned range (only 32-bit signed).  For now,
     * unsigned 32-bit values simply written as signed ones.  This is not
     * a concrete issue except for 32-bit heaphdr fields.  Proper solutions
     * would be to (a) write such integers as IEEE doubles or (b) add an
     * unsigned 32-bit dvalue.
     */
    if (x >= 0x80000000UL) {
        DUK_D(DUK_DPRINT("writing unsigned integer 0x%08lx as signed integer",
                         (long) x));
    }
    duk_debug_write_int(thr, (duk_int32_t) x);
}

DUK_INTERNAL void duk_debug_write_strbuf(duk_hthread *thr, const char *data, duk_size_t length, duk_uint8_t marker_base) {
    duk_uint8_t buf[5];
    duk_size_t buflen;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(length == 0 || data != NULL);

    if (length <= 0x1fUL && marker_base == DUK_DBG_IB_STR4) {
        /* For strings, special form for short lengths. */
        buf[0] = (duk_uint8_t) (0x60 + length);
        buflen = 1;
    } else if (length <= 0xffffUL) {
        buf[0] = (duk_uint8_t) (marker_base + 1);
        buf[1] = (duk_uint8_t) (length >> 8);
        buf[2] = (duk_uint8_t) (length & 0xff);
        buflen = 3;
    } else {
        buf[0] = (duk_uint8_t) marker_base;
        buf[1] = (duk_uint8_t) (length >> 24);
        buf[2] = (duk_uint8_t) ((length >> 16) & 0xff);
        buf[3] = (duk_uint8_t) ((length >> 8) & 0xff);
        buf[4] = (duk_uint8_t) (length & 0xff);
        buflen = 5;
    }

    duk_debug_write_bytes(thr, (const duk_uint8_t *) buf, buflen);
    duk_debug_write_bytes(thr, (const duk_uint8_t *) data, length);
}

DUK_INTERNAL void duk_debug_write_string(duk_hthread *thr, const char *data, duk_size_t length) {
    duk_debug_write_strbuf(thr, data, length, DUK_DBG_IB_STR4);
}

DUK_INTERNAL void duk_debug_write_cstring(duk_hthread *thr, const char *data) {
    DUK_ASSERT(thr != NULL);

    duk_debug_write_string(thr,
                           data,
                           data ? DUK_STRLEN(data) : 0);
}

DUK_INTERNAL void duk_debug_write_hstring(duk_hthread *thr, duk_hstring *h) {
    DUK_ASSERT(thr != NULL);

    /* XXX: differentiate null pointer from empty string? */
    duk_debug_write_string(thr,
                           (h != NULL ? (const char *) DUK_HSTRING_GET_DATA(h) : NULL),
                           (h != NULL ? (duk_size_t) DUK_HSTRING_GET_BYTELEN(h) : 0));
}

DUK_LOCAL void duk__debug_write_hstring_safe_top(duk_hthread *thr) {
    duk_debug_write_hstring(thr, duk_safe_to_hstring(thr, -1));
}

DUK_INTERNAL void duk_debug_write_buffer(duk_hthread *thr, const char *data, duk_size_t length) {
    duk_debug_write_strbuf(thr, data, length, DUK_DBG_IB_BUF4);
}

DUK_INTERNAL void duk_debug_write_hbuffer(duk_hthread *thr, duk_hbuffer *h) {
    DUK_ASSERT(thr != NULL);

    duk_debug_write_buffer(thr,
                           (h != NULL ? (const char *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h) : NULL),
                           (h != NULL ? (duk_size_t) DUK_HBUFFER_GET_SIZE(h) : 0));
}

DUK_LOCAL void duk__debug_write_pointer_raw(duk_hthread *thr, void *ptr, duk_uint8_t ibyte) {
    duk_uint8_t buf[2];
    duk__ptr_union pu;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(sizeof(ptr) >= 1 && sizeof(ptr) <= 16);
    /* ptr may be NULL */

    buf[0] = ibyte;
    buf[1] = sizeof(pu);
    duk_debug_write_bytes(thr, buf, 2);
    pu.p = (void *) ptr;
#if defined(DUK_USE_INTEGER_LE)
    duk_byteswap_bytes((duk_uint8_t *) pu.b, sizeof(pu));
#endif
    duk_debug_write_bytes(thr, (const duk_uint8_t *) &pu.p, (duk_size_t) sizeof(pu));
}

DUK_INTERNAL void duk_debug_write_pointer(duk_hthread *thr, void *ptr) {
    duk__debug_write_pointer_raw(thr, ptr, DUK_DBG_IB_POINTER);
}

#if defined(DUK_USE_DEBUGGER_DUMPHEAP) || defined(DUK_USE_DEBUGGER_INSPECT)
DUK_INTERNAL void duk_debug_write_heapptr(duk_hthread *thr, duk_heaphdr *h) {
    duk__debug_write_pointer_raw(thr, (void *) h, DUK_DBG_IB_HEAPPTR);
}
#endif  /* DUK_USE_DEBUGGER_DUMPHEAP || DUK_USE_DEBUGGER_INSPECT */

DUK_INTERNAL void duk_debug_write_hobject(duk_hthread *thr, duk_hobject *obj) {
    duk_uint8_t buf[3];
    duk__ptr_union pu;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(sizeof(obj) >= 1 && sizeof(obj) <= 16);
    DUK_ASSERT(obj != NULL);

    buf[0] = DUK_DBG_IB_OBJECT;
    buf[1] = (duk_uint8_t) DUK_HOBJECT_GET_CLASS_NUMBER(obj);
    buf[2] = sizeof(pu);
    duk_debug_write_bytes(thr, buf, 3);
    pu.p = (void *) obj;
#if defined(DUK_USE_INTEGER_LE)
    duk_byteswap_bytes((duk_uint8_t *) pu.b, sizeof(pu));
#endif
    duk_debug_write_bytes(thr, (const duk_uint8_t *) &pu.p, (duk_size_t) sizeof(pu));
}

DUK_INTERNAL void duk_debug_write_tval(duk_hthread *thr, duk_tval *tv) {
    duk_c_function lf_func;
    duk_small_uint_t lf_flags;
    duk_uint8_t buf[4];
    duk_double_union du1;
    duk_double_union du2;
    duk_int32_t i32;

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

    switch (DUK_TVAL_GET_TAG(tv)) {
    case DUK_TAG_UNDEFINED:
        duk_debug_write_byte(thr, DUK_DBG_IB_UNDEFINED);
        break;
    case DUK_TAG_UNUSED:
        duk_debug_write_byte(thr, DUK_DBG_IB_UNUSED);
        break;
    case DUK_TAG_NULL:
        duk_debug_write_byte(thr, DUK_DBG_IB_NULL);
        break;
    case DUK_TAG_BOOLEAN:
        DUK_ASSERT(DUK_TVAL_GET_BOOLEAN(tv) == 0 ||
                   DUK_TVAL_GET_BOOLEAN(tv) == 1);
        duk_debug_write_boolean(thr, DUK_TVAL_GET_BOOLEAN(tv));
        break;
    case DUK_TAG_POINTER:
        duk_debug_write_pointer(thr, (void *) DUK_TVAL_GET_POINTER(tv));
        break;
    case DUK_TAG_LIGHTFUNC:
        DUK_TVAL_GET_LIGHTFUNC(tv, lf_func, lf_flags);
        buf[0] = DUK_DBG_IB_LIGHTFUNC;
        buf[1] = (duk_uint8_t) (lf_flags >> 8);
        buf[2] = (duk_uint8_t) (lf_flags & 0xff);
        buf[3] = sizeof(lf_func);
        duk_debug_write_bytes(thr, buf, 4);
        duk_debug_write_bytes(thr, (const duk_uint8_t *) &lf_func, sizeof(lf_func));
        break;
    case DUK_TAG_STRING:
        duk_debug_write_hstring(thr, DUK_TVAL_GET_STRING(tv));
        break;
    case DUK_TAG_OBJECT:
        duk_debug_write_hobject(thr, DUK_TVAL_GET_OBJECT(tv));
        break;
    case DUK_TAG_BUFFER:
        duk_debug_write_hbuffer(thr, DUK_TVAL_GET_BUFFER(tv));
        break;
#if defined(DUK_USE_FASTINT)
    case DUK_TAG_FASTINT:
#endif
    default:
        /* Numbers are normalized to big (network) endian.  We can
         * (but are not required) to use integer dvalues when there's
         * no loss of precision.
         *
         * XXX: share check with other code; this check is slow but
         * reliable and doesn't require careful exponent/mantissa
         * mask tricks as in the fastint downgrade code.
         */
        DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
        DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
        du1.d = DUK_TVAL_GET_NUMBER(tv);
        i32 = (duk_int32_t) du1.d;
        du2.d = (duk_double_t) i32;

        DUK_DD(DUK_DDPRINT("i32=%ld du1=%02x%02x%02x%02x%02x%02x%02x%02x "
                           "du2=%02x%02x%02x%02x%02x%02x%02x%02x",
                           (long) i32,
                           (unsigned int) du1.uc[0], (unsigned int) du1.uc[1],
                           (unsigned int) du1.uc[2], (unsigned int) du1.uc[3],
                           (unsigned int) du1.uc[4], (unsigned int) du1.uc[5],
                           (unsigned int) du1.uc[6], (unsigned int) du1.uc[7],
                           (unsigned int) du2.uc[0], (unsigned int) du2.uc[1],
                           (unsigned int) du2.uc[2], (unsigned int) du2.uc[3],
                           (unsigned int) du2.uc[4], (unsigned int) du2.uc[5],
                           (unsigned int) du2.uc[6], (unsigned int) du2.uc[7]));

        if (duk_memcmp((const void *) du1.uc, (const void *) du2.uc, sizeof(du1.uc)) == 0) {
            duk_debug_write_int(thr, i32);
        } else {
            DUK_DBLUNION_DOUBLE_HTON(&du1);
            duk_debug_write_byte(thr, DUK_DBG_IB_NUMBER);
            duk_debug_write_bytes(thr, (const duk_uint8_t *) du1.uc, sizeof(du1.uc));
        }
    }
}

#if defined(DUK_USE_DEBUGGER_DUMPHEAP)
/* Variant for writing duk_tvals so that any heap allocated values are
 * written out as tagged heap pointers.
 */
DUK_LOCAL void duk__debug_write_tval_heapptr(duk_hthread *thr, duk_tval *tv) {
    if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) {
        duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv);
        duk_debug_write_heapptr(thr, h);
    } else {
        duk_debug_write_tval(thr, tv);
    }
}
#endif  /* DUK_USE_DEBUGGER_DUMPHEAP */

/*
 *  Debug connection message write helpers
 */

#if 0  /* unused */
DUK_INTERNAL void duk_debug_write_request(duk_hthread *thr, duk_small_uint_t command) {
    duk_debug_write_byte(thr, DUK_DBG_IB_REQUEST);
    duk_debug_write_int(thr, command);
}
#endif

DUK_INTERNAL void duk_debug_write_reply(duk_hthread *thr) {
    duk_debug_write_byte(thr, DUK_DBG_IB_REPLY);
}

DUK_INTERNAL void duk_debug_write_error_eom(duk_hthread *thr, duk_small_uint_t err_code, const char *msg) {
    /* Allow NULL 'msg' */
    duk_debug_write_byte(thr, DUK_DBG_IB_ERROR);
    duk_debug_write_int(thr, (duk_int32_t) err_code);
    duk_debug_write_cstring(thr, msg);
    duk_debug_write_eom(thr);
}

DUK_INTERNAL void duk_debug_write_notify(duk_hthread *thr, duk_small_uint_t command) {
    duk_debug_write_byte(thr, DUK_DBG_IB_NOTIFY);
    duk_debug_write_int(thr, (duk_int32_t) command);
}

DUK_INTERNAL void duk_debug_write_eom(duk_hthread *thr) {
    duk_debug_write_byte(thr, DUK_DBG_IB_EOM);

    /* As an initial implementation, write flush after every EOM (and the
     * version identifier).  A better implementation would flush only when
     * Duktape is finished processing messages so that a flush only happens
     * after all outbound messages are finished on that occasion.
     */
    duk_debug_write_flush(thr);
}

/*
 *  Status message and helpers
 */

DUK_INTERNAL duk_uint_fast32_t duk_debug_curr_line(duk_hthread *thr) {
    duk_activation *act;
    duk_uint_fast32_t line;
    duk_uint_fast32_t pc;

    act = thr->callstack_curr;
    if (act == NULL) {
        return 0;
    }

    /* We're conceptually between two opcodes; act->pc indicates the next
     * instruction to be executed.  This is usually the correct pc/line to
     * indicate in Status.  (For the 'debugger' statement this now reports
     * the pc/line after the debugger statement because the debugger opcode
     * has already been executed.)
     */

    pc = duk_hthread_get_act_curr_pc(thr, act);

    /* XXX: this should be optimized to be a raw query and avoid valstack
     * operations if possible.
     */
    duk_push_tval(thr, &act->tv_func);
    line = duk_hobject_pc2line_query(thr, -1, pc);
    duk_pop(thr);
    return line;
}

DUK_INTERNAL void duk_debug_send_status(duk_hthread *thr) {
    duk_activation *act;

    duk_debug_write_notify(thr, DUK_DBG_CMD_STATUS);
    duk_debug_write_int(thr, (DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap) ? 1 : 0));

    act = thr->callstack_curr;
    if (act == NULL) {
        duk_debug_write_undefined(thr);
        duk_debug_write_undefined(thr);
        duk_debug_write_int(thr, 0);
        duk_debug_write_int(thr, 0);
    } else {
        duk_push_tval(thr, &act->tv_func);
        duk_get_prop_literal(thr, -1, "fileName");
        duk__debug_write_hstring_safe_top(thr);
        duk_get_prop_literal(thr, -2, "name");
        duk__debug_write_hstring_safe_top(thr);
        duk_pop_3(thr);
        /* Report next pc/line to be executed. */
        duk_debug_write_uint(thr, (duk_uint32_t) duk_debug_curr_line(thr));
        duk_debug_write_uint(thr, (duk_uint32_t) duk_hthread_get_act_curr_pc(thr, act));
    }

    duk_debug_write_eom(thr);
}

#if defined(DUK_USE_DEBUGGER_THROW_NOTIFY)
DUK_INTERNAL void duk_debug_send_throw(duk_hthread *thr, duk_bool_t fatal) {
    /*
     *  NFY <int: 5> <int: fatal> <str: msg> <str: filename> <int: linenumber> EOM
     */

    duk_activation *act;
    duk_uint32_t pc;

    DUK_ASSERT(thr->valstack_top > thr->valstack);  /* At least: ... [err] */

    duk_debug_write_notify(thr, DUK_DBG_CMD_THROW);
    duk_debug_write_int(thr, (duk_int32_t) fatal);

    /* Report thrown value to client coerced to string */
    duk_dup_top(thr);
    duk__debug_write_hstring_safe_top(thr);
    duk_pop(thr);

    if (duk_is_error(thr, -1)) {
        /* Error instance, use augmented error data directly */
        duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_FILE_NAME);
        duk__debug_write_hstring_safe_top(thr);
        duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_LINE_NUMBER);
        duk_debug_write_uint(thr, duk_get_uint(thr, -1));
        duk_pop_2(thr);
    } else {
        /* For anything other than an Error instance, we calculate the
         * error location directly from the current activation if one
         * exists.
         */
        act = thr->callstack_curr;
        if (act != NULL) {
            duk_push_tval(thr, &act->tv_func);
            duk_get_prop_literal(thr, -1, "fileName");
            duk__debug_write_hstring_safe_top(thr);
            pc = (duk_uint32_t) duk_hthread_get_act_prev_pc(thr, act);
            duk_debug_write_uint(thr, (duk_uint32_t) duk_hobject_pc2line_query(thr, -2, pc));
            duk_pop_2(thr);
        } else {
            /* Can happen if duk_throw() is called on an empty
             * callstack.
             */
            duk_debug_write_cstring(thr, "");
            duk_debug_write_uint(thr, 0);
        }
    }

    duk_debug_write_eom(thr);
}
#endif  /* DUK_USE_DEBUGGER_THROW_NOTIFY */

/*
 *  Debug message processing
 */

/* Skip dvalue. */
DUK_LOCAL duk_bool_t duk__debug_skip_dvalue(duk_hthread *thr) {
    duk_uint8_t x;
    duk_uint32_t len;

    x = duk_debug_read_byte(thr);

    if (x >= 0xc0) {
        duk_debug_skip_byte(thr);
        return 0;
    }
    if (x >= 0x80) {
        return 0;
    }
    if (x >= 0x60) {
        duk_debug_skip_bytes(thr, (duk_size_t) (x - 0x60));
        return 0;
    }
    switch(x) {
    case DUK_DBG_IB_EOM:
        return 1;  /* Return 1: got EOM */
    case DUK_DBG_IB_REQUEST:
    case DUK_DBG_IB_REPLY:
    case DUK_DBG_IB_ERROR:
    case DUK_DBG_IB_NOTIFY:
        break;
    case DUK_DBG_IB_INT4:
        (void) duk__debug_read_uint32_raw(thr);
        break;
    case DUK_DBG_IB_STR4:
    case DUK_DBG_IB_BUF4:
        len = duk__debug_read_uint32_raw(thr);
        duk_debug_skip_bytes(thr, len);
        break;
    case DUK_DBG_IB_STR2:
    case DUK_DBG_IB_BUF2:
        len = duk__debug_read_uint16_raw(thr);
        duk_debug_skip_bytes(thr, len);
        break;
    case DUK_DBG_IB_UNUSED:
    case DUK_DBG_IB_UNDEFINED:
    case DUK_DBG_IB_NULL:
    case DUK_DBG_IB_TRUE:
    case DUK_DBG_IB_FALSE:
        break;
    case DUK_DBG_IB_NUMBER:
        duk_debug_skip_bytes(thr, 8);
        break;
    case DUK_DBG_IB_OBJECT:
        duk_debug_skip_byte(thr);
        len = duk_debug_read_byte(thr);
        duk_debug_skip_bytes(thr, len);
        break;
    case DUK_DBG_IB_POINTER:
    case DUK_DBG_IB_HEAPPTR:
        len = duk_debug_read_byte(thr);
        duk_debug_skip_bytes(thr, len);
        break;
    case DUK_DBG_IB_LIGHTFUNC:
        duk_debug_skip_bytes(thr, 2);
        len = duk_debug_read_byte(thr);
        duk_debug_skip_bytes(thr, len);
        break;
    default:
        goto fail;
    }

    return 0;

 fail:
    DUK__SET_CONN_BROKEN(thr, 1);
    return 1;  /* Pretend like we got EOM */
}

/* Skip dvalues to EOM. */
DUK_LOCAL void duk__debug_skip_to_eom(duk_hthread *thr) {
    for (;;) {
        if (duk__debug_skip_dvalue(thr)) {
            break;
        }
    }
}

/* Read and validate a call stack index.  If index is invalid, write out an
 * error message and return zero.
 */
DUK_LOCAL duk_int32_t duk__debug_read_validate_csindex(duk_hthread *thr) {
    duk_int32_t level;
    level = duk_debug_read_int(thr);
    if (level >= 0 || -level > (duk_int32_t) thr->callstack_top) {
        duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "invalid callstack index");
        return 0;  /* zero indicates failure */
    }
    return level;
}

/* Read a call stack index and lookup the corresponding duk_activation.
 * If index is invalid, write out an error message and return NULL.
 */
DUK_LOCAL duk_activation *duk__debug_read_level_get_activation(duk_hthread *thr) {
    duk_activation *act;
    duk_int32_t level;

    level = duk_debug_read_int(thr);
    act = duk_hthread_get_activation_for_level(thr, level);
    if (act == NULL) {
        duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "invalid callstack index");
    }
    return act;
}

/*
 *  Simple commands
 */

DUK_LOCAL void duk__debug_handle_basic_info(duk_hthread *thr, duk_heap *heap) {
    DUK_UNREF(heap);
    DUK_D(DUK_DPRINT("debug command Version"));

    duk_debug_write_reply(thr);
    duk_debug_write_int(thr, DUK_VERSION);
    duk_debug_write_cstring(thr, DUK_GIT_DESCRIBE);
    duk_debug_write_cstring(thr, DUK_USE_TARGET_INFO);
#if defined(DUK_USE_DOUBLE_LE)
    duk_debug_write_int(thr, 1);
#elif defined(DUK_USE_DOUBLE_ME)
    duk_debug_write_int(thr, 2);
#elif defined(DUK_USE_DOUBLE_BE)
    duk_debug_write_int(thr, 3);
#else
    duk_debug_write_int(thr, 0);
#endif
    duk_debug_write_int(thr, (duk_int_t) sizeof(void *));
    duk_debug_write_eom(thr);
}

DUK_LOCAL void duk__debug_handle_trigger_status(duk_hthread *thr, duk_heap *heap) {
    DUK_UNREF(heap);
    DUK_D(DUK_DPRINT("debug command TriggerStatus"));

    duk_debug_write_reply(thr);
    duk_debug_write_eom(thr);
    heap->dbg_state_dirty = 1;
}

DUK_LOCAL void duk__debug_handle_pause(duk_hthread *thr, duk_heap *heap) {
    DUK_D(DUK_DPRINT("debug command Pause"));
    duk_debug_set_paused(heap);
    duk_debug_write_reply(thr);
    duk_debug_write_eom(thr);
}

DUK_LOCAL void duk__debug_handle_resume(duk_hthread *thr, duk_heap *heap) {
    duk_small_uint_t pause_flags;

    DUK_D(DUK_DPRINT("debug command Resume"));

    duk_debug_clear_paused(heap);

    pause_flags = 0;
#if 0  /* manual testing */
    pause_flags |= DUK_PAUSE_FLAG_ONE_OPCODE;
    pause_flags |= DUK_PAUSE_FLAG_CAUGHT_ERROR;
    pause_flags |= DUK_PAUSE_FLAG_UNCAUGHT_ERROR;
#endif
#if defined(DUK_USE_DEBUGGER_PAUSE_UNCAUGHT)
    pause_flags |= DUK_PAUSE_FLAG_UNCAUGHT_ERROR;
#endif

    duk__debug_set_pause_state(thr, heap, pause_flags);

    duk_debug_write_reply(thr);
    duk_debug_write_eom(thr);
}

DUK_LOCAL void duk__debug_handle_step(duk_hthread *thr, duk_heap *heap, duk_int32_t cmd) {
    duk_small_uint_t pause_flags;

    DUK_D(DUK_DPRINT("debug command StepInto/StepOver/StepOut: %d", (int) cmd));

    if (cmd == DUK_DBG_CMD_STEPINTO) {
        pause_flags = DUK_PAUSE_FLAG_LINE_CHANGE |
                      DUK_PAUSE_FLAG_FUNC_ENTRY |
                      DUK_PAUSE_FLAG_FUNC_EXIT;
    } else if (cmd == DUK_DBG_CMD_STEPOVER) {
        pause_flags = DUK_PAUSE_FLAG_LINE_CHANGE |
                      DUK_PAUSE_FLAG_FUNC_EXIT;
    } else {
        DUK_ASSERT(cmd == DUK_DBG_CMD_STEPOUT);
        pause_flags = DUK_PAUSE_FLAG_FUNC_EXIT;
    }
#if defined(DUK_USE_DEBUGGER_PAUSE_UNCAUGHT)
    pause_flags |= DUK_PAUSE_FLAG_UNCAUGHT_ERROR;
#endif

    /* If current activation doesn't have line information, line-based
     * pause flags are automatically disabled.  As a result, e.g.
     * StepInto will then pause on (native) function entry or exit.
     */
    duk_debug_clear_paused(heap);
    duk__debug_set_pause_state(thr, heap, pause_flags);

    duk_debug_write_reply(thr);
    duk_debug_write_eom(thr);
}

DUK_LOCAL void duk__debug_handle_list_break(duk_hthread *thr, duk_heap *heap) {
    duk_small_int_t i;

    DUK_D(DUK_DPRINT("debug command ListBreak"));
    duk_debug_write_reply(thr);
    for (i = 0; i < (duk_small_int_t) heap->dbg_breakpoint_count; i++) {
        duk_debug_write_hstring(thr, heap->dbg_breakpoints[i].filename);
        duk_debug_write_uint(thr, (duk_uint32_t) heap->dbg_breakpoints[i].line);
    }
    duk_debug_write_eom(thr);
}

DUK_LOCAL void duk__debug_handle_add_break(duk_hthread *thr, duk_heap *heap) {
    duk_hstring *filename;
    duk_uint32_t linenumber;
    duk_small_int_t idx;

    DUK_UNREF(heap);

    filename = duk_debug_read_hstring(thr);
    linenumber = (duk_uint32_t) duk_debug_read_int(thr);
    DUK_D(DUK_DPRINT("debug command AddBreak: %!O:%ld", (duk_hobject *) filename, (long) linenumber));
    idx = duk_debug_add_breakpoint(thr, filename, linenumber);
    if (idx >= 0) {
        duk_debug_write_reply(thr);
        duk_debug_write_int(thr, (duk_int32_t) idx);
        duk_debug_write_eom(thr);
    } else {
        duk_debug_write_error_eom(thr, DUK_DBG_ERR_TOOMANY, "no space for breakpoint");
    }
}

DUK_LOCAL void duk__debug_handle_del_break(duk_hthread *thr, duk_heap *heap) {
    duk_small_uint_t idx;

    DUK_UNREF(heap);

    DUK_D(DUK_DPRINT("debug command DelBreak"));
    idx = (duk_small_uint_t) duk_debug_read_int(thr);
    if (duk_debug_remove_breakpoint(thr, idx)) {
        duk_debug_write_reply(thr);
        duk_debug_write_eom(thr);
    } else {
        duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "invalid breakpoint index");
    }
}

DUK_LOCAL void duk__debug_handle_get_var(duk_hthread *thr, duk_heap *heap) {
    duk_activation *act;
    duk_hstring *str;
    duk_bool_t rc;

    DUK_UNREF(heap);
    DUK_D(DUK_DPRINT("debug command GetVar"));

    act = duk__debug_read_level_get_activation(thr);
    if (act == NULL) {
        return;
    }
    str = duk_debug_read_hstring(thr);  /* push to stack */
    DUK_ASSERT(str != NULL);

    rc = duk_js_getvar_activation(thr, act, str, 0);

    duk_debug_write_reply(thr);
    if (rc) {
        duk_debug_write_int(thr, 1);
        DUK_ASSERT(duk_get_tval(thr, -2) != NULL);
        duk_debug_write_tval(thr, duk_get_tval(thr, -2));
    } else {
        duk_debug_write_int(thr, 0);
        duk_debug_write_unused(thr);
    }
    duk_debug_write_eom(thr);
}

DUK_LOCAL void duk__debug_handle_put_var(duk_hthread *thr, duk_heap *heap) {
    duk_activation *act;
    duk_hstring *str;
    duk_tval *tv;

    DUK_UNREF(heap);
    DUK_D(DUK_DPRINT("debug command PutVar"));

    act = duk__debug_read_level_get_activation(thr);
    if (act == NULL) {
        return;
    }
    str = duk_debug_read_hstring(thr);  /* push to stack */
    DUK_ASSERT(str != NULL);
    tv = duk_debug_read_tval(thr);
    if (tv == NULL) {
        /* detached */
        return;
    }

    duk_js_putvar_activation(thr, act, str, tv, 0);

    /* XXX: Current putvar implementation doesn't have a success flag,
     * add one and send to debug client?
     */
    duk_debug_write_reply(thr);
    duk_debug_write_eom(thr);
}

DUK_LOCAL void duk__debug_handle_get_call_stack(duk_hthread *thr, duk_heap *heap) {
    duk_hthread *curr_thr = thr;
    duk_activation *curr_act;
    duk_uint_fast32_t pc;
    duk_uint_fast32_t line;

    DUK_ASSERT(thr != NULL);
    DUK_UNREF(heap);

    duk_debug_write_reply(thr);
    while (curr_thr != NULL) {
        for (curr_act = curr_thr->callstack_curr; curr_act != NULL; curr_act = curr_act->parent) {
            /* PC/line semantics here are:
             *   - For callstack top we're conceptually between two
             *     opcodes and current PC indicates next line to
             *     execute, so report that (matches Status).
             *   - For other activations we're conceptually still
             *     executing the instruction at PC-1, so report that
             *     (matches error stacktrace behavior).
             *   - See: https://github.com/svaarala/duktape/issues/281
             */

            /* XXX: optimize to use direct reads, i.e. avoid
             * value stack operations.
             */
            duk_push_tval(thr, &curr_act->tv_func);
            duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_FILE_NAME);
            duk__debug_write_hstring_safe_top(thr);
            duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_NAME);
            duk__debug_write_hstring_safe_top(thr);
            pc = duk_hthread_get_act_curr_pc(thr, curr_act);
            if (curr_act != curr_thr->callstack_curr && pc > 0) {
                pc--;
            }
            line = duk_hobject_pc2line_query(thr, -3, pc);
            duk_debug_write_uint(thr, (duk_uint32_t) line);
            duk_debug_write_uint(thr, (duk_uint32_t) pc);
            duk_pop_3(thr);
        }
        curr_thr = curr_thr->resumer;
    }
    /* SCANBUILD: warning about 'thr' potentially being NULL here,
     * warning is incorrect because thr != NULL always here.
     */
    duk_debug_write_eom(thr);
}

DUK_LOCAL void duk__debug_handle_get_locals(duk_hthread *thr, duk_heap *heap) {
    duk_activation *act;
    duk_hstring *varname;

    DUK_UNREF(heap);

    act = duk__debug_read_level_get_activation(thr);
    if (act == NULL) {
        return;
    }

    duk_debug_write_reply(thr);

    /* XXX: several nice-to-have improvements here:
     *   - Use direct reads avoiding value stack operations
     *   - Avoid triggering getters, indicate getter values to debug client
     *   - If side effects are possible, add error catching
     */

    if (DUK_TVAL_IS_OBJECT(&act->tv_func)) {
        duk_hobject *h_func = DUK_TVAL_GET_OBJECT(&act->tv_func);
        duk_hobject *h_varmap;

        h_varmap = duk_hobject_get_varmap(thr, h_func);
        if (h_varmap != NULL) {
            duk_push_hobject(thr, h_varmap);
            duk_enum(thr, -1, 0 /*enum_flags*/);
            while (duk_next(thr, -1 /*enum_index*/, 0 /*get_value*/)) {
                varname = duk_known_hstring(thr, -1);

                duk_js_getvar_activation(thr, act, varname, 0 /*throw_flag*/);
                /* [ ... func varmap enum key value this ] */
                duk_debug_write_hstring(thr, duk_get_hstring(thr, -3));
                duk_debug_write_tval(thr, duk_get_tval(thr, -2));
                duk_pop_3(thr);  /* -> [ ... func varmap enum ] */
            }
        } else {
            DUK_D(DUK_DPRINT("varmap missing in GetLocals, ignore"));
        }
    } else {
        DUK_D(DUK_DPRINT("varmap is not an object in GetLocals, ignore"));
    }

    duk_debug_write_eom(thr);
}

DUK_LOCAL void duk__debug_handle_eval(duk_hthread *thr, duk_heap *heap) {
    duk_small_uint_t call_flags;
    duk_int_t call_ret;
    duk_small_int_t eval_err;
    duk_bool_t direct_eval;
    duk_int32_t level;
    duk_idx_t idx_func;

    DUK_UNREF(heap);

    DUK_D(DUK_DPRINT("debug command Eval"));

    /* The eval code is executed within the lexical environment of a specified
     * activation.  For now, use global object eval() function, with the eval
     * considered a 'direct call to eval'.
     *
     * Callstack index for debug commands only affects scope -- the callstack
     * as seen by, e.g. Duktape.act() will be the same regardless.
     */

    /* nargs == 2 so we can pass a callstack index to eval(). */
    idx_func = duk_get_top(thr);
    duk_push_c_function(thr, duk_bi_global_object_eval, 2 /*nargs*/);
    duk_push_undefined(thr);  /* 'this' binding shouldn't matter here */

    /* Read callstack index, if non-null. */
    if (duk_debug_peek_byte(thr) == DUK_DBG_IB_NULL) {
        direct_eval = 0;
        level = -1;  /* Not needed, but silences warning. */
        (void) duk_debug_read_byte(thr);
    } else {
        direct_eval = 1;
        level = duk__debug_read_validate_csindex(thr);
        if (level == 0) {
            return;
        }
    }

    DUK_ASSERT(!direct_eval ||
               (level < 0 && -level <= (duk_int32_t) thr->callstack_top));

    (void) duk_debug_read_hstring(thr);
    if (direct_eval) {
        duk_push_int(thr, level - 1);  /* compensate for eval() call */
    }

    /* [ ... eval "eval" eval_input level? ] */

    call_flags = 0;
    if (direct_eval) {
        duk_activation *act;
        duk_hobject *fun;

        act = duk_hthread_get_activation_for_level(thr, level);
        if (act != NULL) {
            fun = DUK_ACT_GET_FUNC(act);
            if (fun != NULL && DUK_HOBJECT_IS_COMPFUNC(fun)) {
                /* Direct eval requires that there's a current
                 * activation and it is an ECMAScript function.
                 * When Eval is executed from e.g. cooperate API
                 * call we'll need to do an indirect eval instead.
                 */
                call_flags |= DUK_CALL_FLAG_DIRECT_EVAL;
            }
        }
    }

    call_ret = duk_pcall_method_flags(thr, duk_get_top(thr) - (idx_func + 2), call_flags);

    if (call_ret == DUK_EXEC_SUCCESS) {
        eval_err = 0;
        /* Use result value as is. */
    } else {
        /* For errors a string coerced result is most informative
         * right now, as the debug client doesn't have the capability
         * to traverse the error object.
         */
        eval_err = 1;
        duk_safe_to_string(thr, -1);
    }

    /* [ ... result ] */

    duk_debug_write_reply(thr);
    duk_debug_write_int(thr, (duk_int32_t) eval_err);
    DUK_ASSERT(duk_get_tval(thr, -1) != NULL);
    duk_debug_write_tval(thr, duk_get_tval(thr, -1));
    duk_debug_write_eom(thr);
}

DUK_LOCAL void duk__debug_handle_detach(duk_hthread *thr, duk_heap *heap) {
    DUK_UNREF(heap);
    DUK_D(DUK_DPRINT("debug command Detach"));

    duk_debug_write_reply(thr);
    duk_debug_write_eom(thr);

    DUK_D(DUK_DPRINT("debug connection detached, mark broken"));
    DUK__SET_CONN_BROKEN(thr, 0);  /* not an error */
}

DUK_LOCAL void duk__debug_handle_apprequest(duk_hthread *thr, duk_heap *heap) {
    duk_idx_t old_top;

    DUK_D(DUK_DPRINT("debug command AppRequest"));

    old_top = duk_get_top(thr);  /* save stack top */

    if (heap->dbg_request_cb != NULL) {
        duk_idx_t nrets;
        duk_idx_t nvalues = 0;
        duk_idx_t top, idx;

        /* Read tvals from the message and push them onto the valstack,
         * then call the request callback to process the request.
         */
        while (duk_debug_peek_byte(thr) != DUK_DBG_IB_EOM) {
            duk_tval *tv;
            if (!duk_check_stack(thr, 1)) {
                DUK_D(DUK_DPRINT("failed to allocate space for request dvalue(s)"));
                goto fail;
            }
            tv = duk_debug_read_tval(thr);  /* push to stack */
            if (tv == NULL) {
                /* detached */
                return;
            }
            nvalues++;
        }
        DUK_ASSERT(duk_get_top(thr) == old_top + nvalues);

        /* Request callback should push values for reply to client onto valstack */
        DUK_D(DUK_DPRINT("calling into AppRequest request_cb with nvalues=%ld, old_top=%ld, top=%ld",
                         (long) nvalues, (long) old_top, (long) duk_get_top(thr)));
        nrets = heap->dbg_request_cb(thr, heap->dbg_udata, nvalues);
        DUK_D(DUK_DPRINT("returned from AppRequest request_cb; nvalues=%ld -> nrets=%ld, old_top=%ld, top=%ld",
                         (long) nvalues, (long) nrets, (long) old_top, (long) duk_get_top(thr)));
        if (nrets >= 0) {
            DUK_ASSERT(duk_get_top(thr) >= old_top + nrets);
            if (duk_get_top(thr) < old_top + nrets) {
                DUK_D(DUK_DPRINT("AppRequest callback doesn't match value stack configuration, "
                                 "top=%ld < old_top=%ld + nrets=%ld; "
                                 "this might mean it's unsafe to continue!",
                                 (long) duk_get_top(thr), (long) old_top, (long) nrets));
                goto fail;
            }

            /* Reply with tvals pushed by request callback */
            duk_debug_write_byte(thr, DUK_DBG_IB_REPLY);
            top = duk_get_top(thr);
            for (idx = top - nrets; idx < top; idx++) {
                duk_debug_write_tval(thr, DUK_GET_TVAL_POSIDX(thr, idx));
            }
            duk_debug_write_eom(thr);
        } else {
            DUK_ASSERT(duk_get_top(thr) >= old_top + 1);
            if (duk_get_top(thr) < old_top + 1) {
                DUK_D(DUK_DPRINT("request callback return value doesn't match value stack configuration"));
                goto fail;
            }
            duk_debug_write_error_eom(thr, DUK_DBG_ERR_APPLICATION, duk_get_string(thr, -1));
        }

        duk_set_top(thr, old_top);  /* restore stack top */
    } else {
        DUK_D(DUK_DPRINT("no request callback, treat AppRequest as unsupported"));
        duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNSUPPORTED, "AppRequest unsupported by target");
    }

    return;

 fail:
    duk_set_top(thr, old_top);  /* restore stack top */
    DUK__SET_CONN_BROKEN(thr, 1);
}

/*
 *  DumpHeap command
 */

#if defined(DUK_USE_DEBUGGER_DUMPHEAP)
/* XXX: this has some overlap with object inspection; remove this and make
 * DumpHeap return lists of heapptrs instead?
 */
DUK_LOCAL void duk__debug_dump_heaphdr(duk_hthread *thr, duk_heap *heap, duk_heaphdr *hdr) {
    DUK_UNREF(heap);

    duk_debug_write_heapptr(thr, hdr);
    duk_debug_write_uint(thr, (duk_uint32_t) DUK_HEAPHDR_GET_TYPE(hdr));
    duk_debug_write_uint(thr, (duk_uint32_t) DUK_HEAPHDR_GET_FLAGS_RAW(hdr));
#if defined(DUK_USE_REFERENCE_COUNTING)
    duk_debug_write_uint(thr, (duk_uint32_t) DUK_HEAPHDR_GET_REFCOUNT(hdr));
#else
    duk_debug_write_int(thr, (duk_int32_t) -1);
#endif

    switch (DUK_HEAPHDR_GET_TYPE(hdr)) {
    case DUK_HTYPE_STRING: {
        duk_hstring *h = (duk_hstring *) hdr;

        duk_debug_write_uint(thr, (duk_uint32_t) DUK_HSTRING_GET_BYTELEN(h));
        duk_debug_write_uint(thr, (duk_uint32_t) DUK_HSTRING_GET_CHARLEN(h));
        duk_debug_write_uint(thr, (duk_uint32_t) DUK_HSTRING_GET_HASH(h));
        duk_debug_write_hstring(thr, h);
        break;
    }
    case DUK_HTYPE_OBJECT: {
        duk_hobject *h = (duk_hobject *) hdr;
        duk_hstring *k;
        duk_uint_fast32_t i;

        duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_GET_CLASS_NUMBER(h));
        duk_debug_write_heapptr(thr, (duk_heaphdr *) DUK_HOBJECT_GET_PROTOTYPE(heap, h));
        duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_GET_ESIZE(h));
        duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_GET_ENEXT(h));
        duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_GET_ASIZE(h));
        duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_GET_HSIZE(h));

        for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(h); i++) {
            duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_E_GET_FLAGS(heap, h, i));
            k = DUK_HOBJECT_E_GET_KEY(heap, h, i);
            duk_debug_write_heapptr(thr, (duk_heaphdr *) k);
            if (k == NULL) {
                duk_debug_write_int(thr, 0);  /* isAccessor */
                duk_debug_write_unused(thr);
                continue;
            }
            if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(heap, h, i)) {
                duk_debug_write_int(thr, 1);  /* isAccessor */
                duk_debug_write_heapptr(thr, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->a.get);
                duk_debug_write_heapptr(thr, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->a.set);
            } else {
                duk_debug_write_int(thr, 0);  /* isAccessor */

                duk__debug_write_tval_heapptr(thr, &DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->v);
            }
        }

        for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(h); i++) {
            /* Note: array dump will include elements beyond
             * 'length'.
             */
            duk__debug_write_tval_heapptr(thr, DUK_HOBJECT_A_GET_VALUE_PTR(heap, h, i));
        }
        break;
    }
    case DUK_HTYPE_BUFFER: {
        duk_hbuffer *h = (duk_hbuffer *) hdr;

        duk_debug_write_uint(thr, (duk_uint32_t) DUK_HBUFFER_GET_SIZE(h));
        duk_debug_write_buffer(thr, (const char *) DUK_HBUFFER_GET_DATA_PTR(heap, h), (duk_size_t) DUK_HBUFFER_GET_SIZE(h));
        break;
    }
    default: {
        DUK_D(DUK_DPRINT("invalid htype: %d", (int) DUK_HEAPHDR_GET_TYPE(hdr)));
    }
    }
}

DUK_LOCAL void duk__debug_dump_heap_allocated(duk_hthread *thr, duk_heap *heap) {
    duk_heaphdr *hdr;

    hdr = heap->heap_allocated;
    while (hdr != NULL) {
        duk__debug_dump_heaphdr(thr, heap, hdr);
        hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr);
    }
}

DUK_LOCAL void duk__debug_dump_strtab(duk_hthread *thr, duk_heap *heap) {
    duk_uint32_t i;
    duk_hstring *h;

    for (i = 0; i < heap->st_size; i++) {
#if defined(DUK_USE_STRTAB_PTRCOMP)
        h = DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, heap->strtable16[i]);
#else
        h = heap->strtable[i];
#endif
        while (h != NULL) {
            duk__debug_dump_heaphdr(thr, heap, (duk_heaphdr *) h);
            h = h->hdr.h_next;
        }
    }
}

DUK_LOCAL void duk__debug_handle_dump_heap(duk_hthread *thr, duk_heap *heap) {
    DUK_D(DUK_DPRINT("debug command DumpHeap"));

    duk_debug_write_reply(thr);
    duk__debug_dump_heap_allocated(thr, heap);
    duk__debug_dump_strtab(thr, heap);
    duk_debug_write_eom(thr);
}
#endif  /* DUK_USE_DEBUGGER_DUMPHEAP */

DUK_LOCAL void duk__debug_handle_get_bytecode(duk_hthread *thr, duk_heap *heap) {
    duk_activation *act;
    duk_hcompfunc *fun = NULL;
    duk_size_t i, n;
    duk_tval *tv;
    duk_hobject **fn;
    duk_int32_t level = -1;
    duk_uint8_t ibyte;

    DUK_UNREF(heap);

    DUK_D(DUK_DPRINT("debug command GetBytecode"));

    ibyte = duk_debug_peek_byte(thr);
    if (ibyte != DUK_DBG_IB_EOM) {
        tv = duk_debug_read_tval(thr);
        if (tv == NULL) {
            /* detached */
            return;
        }
        if (DUK_TVAL_IS_OBJECT(tv)) {
            /* tentative, checked later */
            fun = (duk_hcompfunc *) DUK_TVAL_GET_OBJECT(tv);
            DUK_ASSERT(fun != NULL);
        } else if (DUK_TVAL_IS_NUMBER(tv)) {
            level = (duk_int32_t) DUK_TVAL_GET_NUMBER(tv);
        } else {
            DUK_D(DUK_DPRINT("invalid argument to GetBytecode: %!T", tv));
            goto fail_args;
        }
    }

    if (fun == NULL) {
        act = duk_hthread_get_activation_for_level(thr, level);
        if (act == NULL) {
            goto fail_index;
        }
        fun = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act);
    }

    if (fun == NULL || !DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) fun)) {
        DUK_D(DUK_DPRINT("invalid argument to GetBytecode: %!O", fun));
        goto fail_args;
    }
    DUK_ASSERT(fun != NULL && DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) fun));

    duk_debug_write_reply(thr);
    n = DUK_HCOMPFUNC_GET_CONSTS_COUNT(heap, fun);
    duk_debug_write_int(thr, (duk_int32_t) n);
    tv = DUK_HCOMPFUNC_GET_CONSTS_BASE(heap, fun);
    for (i = 0; i < n; i++) {
        duk_debug_write_tval(thr, tv);
        tv++;
    }
    n = DUK_HCOMPFUNC_GET_FUNCS_COUNT(heap, fun);
    duk_debug_write_int(thr, (duk_int32_t) n);
    fn = DUK_HCOMPFUNC_GET_FUNCS_BASE(heap, fun);
    for (i = 0; i < n; i++) {
        duk_debug_write_hobject(thr, *fn);
        fn++;
    }
    duk_debug_write_string(thr,
                           (const char *) DUK_HCOMPFUNC_GET_CODE_BASE(heap, fun),
                           (duk_size_t) DUK_HCOMPFUNC_GET_CODE_SIZE(heap, fun));
    duk_debug_write_eom(thr);
    return;

 fail_args:
    duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid argument");
    return;

 fail_index:
    duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "invalid callstack index");
    return;
}

/*
 *  Object inspection commands: GetHeapObjInfo, GetObjPropDesc,
 *  GetObjPropDescRange
 */

#if defined(DUK_USE_DEBUGGER_INSPECT)

#if 0 /* pruned */
DUK_LOCAL const char * const duk__debug_getinfo_heaphdr_keys[] = {
    "reachable",
    "temproot",
    "finalizable",
    "finalized",
    "readonly"
    /* NULL not needed here */
};
DUK_LOCAL duk_uint_t duk__debug_getinfo_heaphdr_masks[] = {
    DUK_HEAPHDR_FLAG_REACHABLE,
    DUK_HEAPHDR_FLAG_TEMPROOT,
    DUK_HEAPHDR_FLAG_FINALIZABLE,
    DUK_HEAPHDR_FLAG_FINALIZED,
    DUK_HEAPHDR_FLAG_READONLY,
    0  /* terminator */
};
#endif
DUK_LOCAL const char * const duk__debug_getinfo_hstring_keys[] = {
#if 0
    "arridx",
    "symbol",
    "hidden",
    "reserved_word",
    "strict_reserved_word",
    "eval_or_arguments",
#endif
    "extdata"
    /* NULL not needed here */
};
DUK_LOCAL duk_uint_t duk__debug_getinfo_hstring_masks[] = {
#if 0
    DUK_HSTRING_FLAG_ARRIDX,
    DUK_HSTRING_FLAG_SYMBOL,
    DUK_HSTRING_FLAG_HIDDEN,
    DUK_HSTRING_FLAG_RESERVED_WORD,
    DUK_HSTRING_FLAG_STRICT_RESERVED_WORD,
    DUK_HSTRING_FLAG_EVAL_OR_ARGUMENTS,
#endif
    DUK_HSTRING_FLAG_EXTDATA,
    0  /* terminator */
};
DUK_LOCAL const char * const duk__debug_getinfo_hobject_keys[] = {
    "extensible",
    "constructable",
    "callable",
    "boundfunc",
    "compfunc",
    "natfunc",
    "bufobj",
    "fastrefs",
    "array_part",
    "strict",
    "notail",
    "newenv",
    "namebinding",
    "createargs",
    "have_finalizer",
    "exotic_array",
    "exotic_stringobj",
    "exotic_arguments",
    "exotic_proxyobj",
    "special_call"
    /* NULL not needed here */
};
DUK_LOCAL duk_uint_t duk__debug_getinfo_hobject_masks[] = {
    DUK_HOBJECT_FLAG_EXTENSIBLE,
    DUK_HOBJECT_FLAG_CONSTRUCTABLE,
    DUK_HOBJECT_FLAG_CALLABLE,
    DUK_HOBJECT_FLAG_BOUNDFUNC,
    DUK_HOBJECT_FLAG_COMPFUNC,
    DUK_HOBJECT_FLAG_NATFUNC,
    DUK_HOBJECT_FLAG_BUFOBJ,
    DUK_HOBJECT_FLAG_FASTREFS,
    DUK_HOBJECT_FLAG_ARRAY_PART,
    DUK_HOBJECT_FLAG_STRICT,
    DUK_HOBJECT_FLAG_NOTAIL,
    DUK_HOBJECT_FLAG_NEWENV,
    DUK_HOBJECT_FLAG_NAMEBINDING,
    DUK_HOBJECT_FLAG_CREATEARGS,
    DUK_HOBJECT_FLAG_HAVE_FINALIZER,
    DUK_HOBJECT_FLAG_EXOTIC_ARRAY,
    DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ,
    DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS,
    DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ,
    DUK_HOBJECT_FLAG_SPECIAL_CALL,
    0  /* terminator */
};
DUK_LOCAL const char * const duk__debug_getinfo_hbuffer_keys[] = {
    "dynamic",
    "external"
    /* NULL not needed here */
};
DUK_LOCAL duk_uint_t duk__debug_getinfo_hbuffer_masks[] = {
    DUK_HBUFFER_FLAG_DYNAMIC,
    DUK_HBUFFER_FLAG_EXTERNAL,
    0  /* terminator */
};

DUK_LOCAL void duk__debug_getinfo_flags_key(duk_hthread *thr, const char *key) {
    duk_debug_write_uint(thr, 0);
    duk_debug_write_cstring(thr, key);
}

DUK_LOCAL void duk__debug_getinfo_prop_uint(duk_hthread *thr, const char *key, duk_uint_t val) {
    duk_debug_write_uint(thr, 0);
    duk_debug_write_cstring(thr, key);
    duk_debug_write_uint(thr, val);
}

DUK_LOCAL void duk__debug_getinfo_prop_int(duk_hthread *thr, const char *key, duk_int_t val) {
    duk_debug_write_uint(thr, 0);
    duk_debug_write_cstring(thr, key);
    duk_debug_write_int(thr, val);
}

DUK_LOCAL void duk__debug_getinfo_prop_bool(duk_hthread *thr, const char *key, duk_bool_t val) {
    duk_debug_write_uint(thr, 0);
    duk_debug_write_cstring(thr, key);
    duk_debug_write_boolean(thr, val);
}

DUK_LOCAL void duk__debug_getinfo_bitmask(duk_hthread *thr, const char * const * keys, duk_uint_t *masks, duk_uint_t flags) {
    const char *key;
    duk_uint_t mask;

    for (;;) {
        mask = *masks++;
        if (mask == 0) {
            break;
        }
        key = *keys++;
        DUK_ASSERT(key != NULL);

        DUK_DD(DUK_DDPRINT("inspect bitmask: key=%s, mask=0x%08lx, flags=0x%08lx", key, (unsigned long) mask, (unsigned long) flags));
        duk__debug_getinfo_prop_bool(thr, key, flags & mask);
    }
}

/* Inspect a property using a virtual index into a conceptual property list
 * consisting of (1) all array part items from [0,a_size[ (even when above
 * .length) and (2) all entry part items from [0,e_next[.  Unused slots are
 * indicated using dvalue 'unused'.
 */
DUK_LOCAL duk_bool_t duk__debug_getprop_index(duk_hthread *thr, duk_heap *heap, duk_hobject *h_obj, duk_uint_t idx) {
    duk_uint_t a_size;
    duk_tval *tv;
    duk_hstring *h_key;
    duk_hobject *h_getset;
    duk_uint_t flags;

    DUK_UNREF(heap);

    a_size = DUK_HOBJECT_GET_ASIZE(h_obj);
    if (idx < a_size) {
        duk_debug_write_uint(thr, DUK_PROPDESC_FLAGS_WEC);
        duk_debug_write_uint(thr, idx);
        tv = DUK_HOBJECT_A_GET_VALUE_PTR(heap, h_obj, idx);
        duk_debug_write_tval(thr, tv);
        return 1;
    }

    idx -= a_size;
    if (idx >= DUK_HOBJECT_GET_ENEXT(h_obj)) {
        return 0;
    }

    h_key = DUK_HOBJECT_E_GET_KEY(heap, h_obj, idx);
    if (h_key == NULL) {
        duk_debug_write_uint(thr, 0);
        duk_debug_write_null(thr);
        duk_debug_write_unused(thr);
        return 1;
    }

    flags = DUK_HOBJECT_E_GET_FLAGS(heap, h_obj, idx);
    if (DUK_HSTRING_HAS_SYMBOL(h_key)) {
        flags |= DUK_DBG_PROPFLAG_SYMBOL;
    }
    if (DUK_HSTRING_HAS_HIDDEN(h_key)) {
        flags |= DUK_DBG_PROPFLAG_HIDDEN;
    }
    duk_debug_write_uint(thr, flags);
    duk_debug_write_hstring(thr, h_key);
    if (flags & DUK_PROPDESC_FLAG_ACCESSOR) {
        h_getset = DUK_HOBJECT_E_GET_VALUE_GETTER(heap, h_obj, idx);
        if (h_getset) {
            duk_debug_write_hobject(thr, h_getset);
        } else {
            duk_debug_write_null(thr);
        }
        h_getset = DUK_HOBJECT_E_GET_VALUE_SETTER(heap, h_obj, idx);
        if (h_getset) {
            duk_debug_write_hobject(thr, h_getset);
        } else {
            duk_debug_write_null(thr);
        }
    } else {
        tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(heap, h_obj, idx);
        duk_debug_write_tval(thr, tv);
    }
    return 1;
}

DUK_LOCAL void duk__debug_handle_get_heap_obj_info(duk_hthread *thr, duk_heap *heap) {
    duk_heaphdr *h;

    DUK_D(DUK_DPRINT("debug command GetHeapObjInfo"));
    DUK_UNREF(heap);

    DUK_ASSERT(sizeof(duk__debug_getinfo_hstring_keys) / sizeof(const char *) == sizeof(duk__debug_getinfo_hstring_masks) / sizeof(duk_uint_t) - 1);
    DUK_ASSERT(sizeof(duk__debug_getinfo_hobject_keys) / sizeof(const char *) == sizeof(duk__debug_getinfo_hobject_masks) / sizeof(duk_uint_t) - 1);
    DUK_ASSERT(sizeof(duk__debug_getinfo_hbuffer_keys) / sizeof(const char *) == sizeof(duk__debug_getinfo_hbuffer_masks) / sizeof(duk_uint_t) - 1);

    h = duk_debug_read_any_ptr(thr);
    if (!h) {
        duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid target");
        return;
    }

    duk_debug_write_reply(thr);

    /* As with all inspection code, we rely on the debug client providing
     * a valid, non-stale pointer: there's no portable way to safely
     * validate the pointer here.
     */

    duk__debug_getinfo_flags_key(thr, "heapptr");
    duk_debug_write_heapptr(thr, h);

    /* XXX: comes out as signed now */
    duk__debug_getinfo_prop_uint(thr, "heaphdr_flags", (duk_uint_t) DUK_HEAPHDR_GET_FLAGS(h));
    duk__debug_getinfo_prop_uint(thr, "heaphdr_type", (duk_uint_t) DUK_HEAPHDR_GET_TYPE(h));
#if defined(DUK_USE_REFERENCE_COUNTING)
    duk__debug_getinfo_prop_uint(thr, "refcount", (duk_uint_t) DUK_HEAPHDR_GET_REFCOUNT(h));
#endif
#if 0 /* pruned */
    duk__debug_getinfo_bitmask(thr,
                               duk__debug_getinfo_heaphdr_keys,
                               duk__debug_getinfo_heaphdr_masks,
                               DUK_HEAPHDR_GET_FLAGS_RAW(h));
#endif

    switch (DUK_HEAPHDR_GET_TYPE(h)) {
    case DUK_HTYPE_STRING: {
        duk_hstring *h_str;

        h_str = (duk_hstring *) h;
        duk__debug_getinfo_bitmask(thr,
                                   duk__debug_getinfo_hstring_keys,
                                   duk__debug_getinfo_hstring_masks,
                                   DUK_HEAPHDR_GET_FLAGS_RAW(h));
        duk__debug_getinfo_prop_uint(thr, "bytelen", (duk_uint_t) DUK_HSTRING_GET_BYTELEN(h_str));
        duk__debug_getinfo_prop_uint(thr, "charlen", (duk_uint_t) DUK_HSTRING_GET_CHARLEN(h_str));
        duk__debug_getinfo_prop_uint(thr, "hash", (duk_uint_t) DUK_HSTRING_GET_HASH(h_str));
        duk__debug_getinfo_flags_key(thr, "data");
        duk_debug_write_hstring(thr, h_str);
        break;
    }
    case DUK_HTYPE_OBJECT: {
        duk_hobject *h_obj;
        duk_hobject *h_proto;

        h_obj = (duk_hobject *) h;
        h_proto = DUK_HOBJECT_GET_PROTOTYPE(heap, h_obj);

        /* duk_hobject specific fields. */
        duk__debug_getinfo_bitmask(thr,
                                   duk__debug_getinfo_hobject_keys,
                                   duk__debug_getinfo_hobject_masks,
                                   DUK_HEAPHDR_GET_FLAGS_RAW(h));
        duk__debug_getinfo_prop_uint(thr, "class_number", DUK_HOBJECT_GET_CLASS_NUMBER(h_obj));
        duk__debug_getinfo_flags_key(thr, "class_name");
        duk_debug_write_hstring(thr, DUK_HOBJECT_GET_CLASS_STRING(heap, h_obj));
        duk__debug_getinfo_flags_key(thr, "prototype");
        if (h_proto != NULL) {
            duk_debug_write_hobject(thr, h_proto);
        } else {
            duk_debug_write_null(thr);
        }
        duk__debug_getinfo_flags_key(thr, "props");
        duk_debug_write_pointer(thr, (void *) DUK_HOBJECT_GET_PROPS(heap, h_obj));
        duk__debug_getinfo_prop_uint(thr, "e_size", (duk_uint_t) DUK_HOBJECT_GET_ESIZE(h_obj));
        duk__debug_getinfo_prop_uint(thr, "e_next", (duk_uint_t) DUK_HOBJECT_GET_ENEXT(h_obj));
        duk__debug_getinfo_prop_uint(thr, "a_size", (duk_uint_t) DUK_HOBJECT_GET_ASIZE(h_obj));
        duk__debug_getinfo_prop_uint(thr, "h_size", (duk_uint_t) DUK_HOBJECT_GET_HSIZE(h_obj));

        if (DUK_HOBJECT_IS_ARRAY(h_obj)) {
            duk_harray *h_arr;
            h_arr = (duk_harray *) h_obj;

            duk__debug_getinfo_prop_uint(thr, "length", (duk_uint_t) h_arr->length);
            duk__debug_getinfo_prop_bool(thr, "length_nonwritable", h_arr->length_nonwritable);
        }

        if (DUK_HOBJECT_IS_NATFUNC(h_obj)) {
            duk_hnatfunc *h_fun;
            h_fun = (duk_hnatfunc *) h_obj;

            duk__debug_getinfo_prop_int(thr, "nargs", h_fun->nargs);
            duk__debug_getinfo_prop_int(thr, "magic", h_fun->magic);
            duk__debug_getinfo_prop_bool(thr, "varargs", h_fun->magic == DUK_HNATFUNC_NARGS_VARARGS);
            /* Native function pointer may be different from a void pointer,
             * and we serialize it from memory directly now (no byte swapping etc).
             */
            duk__debug_getinfo_flags_key(thr, "funcptr");
            duk_debug_write_buffer(thr, (const char *) &h_fun->func, sizeof(h_fun->func));
        }

        if (DUK_HOBJECT_IS_COMPFUNC(h_obj)) {
            duk_hcompfunc *h_fun;
            duk_hbuffer *h_buf;
            duk_hobject *h_lexenv;
            duk_hobject *h_varenv;
            h_fun = (duk_hcompfunc *) h_obj;

            duk__debug_getinfo_prop_int(thr, "nregs", h_fun->nregs);
            duk__debug_getinfo_prop_int(thr, "nargs", h_fun->nargs);

            duk__debug_getinfo_flags_key(thr, "lex_env");
            h_lexenv = DUK_HCOMPFUNC_GET_LEXENV(thr->heap, h_fun);
            if (h_lexenv != NULL) {
                duk_debug_write_hobject(thr, h_lexenv);
            } else {
                duk_debug_write_null(thr);
            }
            duk__debug_getinfo_flags_key(thr, "var_env");
            h_varenv = DUK_HCOMPFUNC_GET_VARENV(thr->heap, h_fun);
            if (h_varenv != NULL) {
                duk_debug_write_hobject(thr, h_varenv);
            } else {
                duk_debug_write_null(thr);
            }

            duk__debug_getinfo_prop_uint(thr, "start_line", h_fun->start_line);
            duk__debug_getinfo_prop_uint(thr, "end_line", h_fun->end_line);
            h_buf = (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(thr->heap, h_fun);
            if (h_buf != NULL) {
                duk__debug_getinfo_flags_key(thr, "data");
                duk_debug_write_heapptr(thr, (duk_heaphdr *) h_buf);
            }
        }

        if (DUK_HOBJECT_IS_BOUNDFUNC(h_obj)) {
            duk_hboundfunc *h_bfun;
            h_bfun = (duk_hboundfunc *) (void *) h_obj;

            duk__debug_getinfo_flags_key(thr, "target");
            duk_debug_write_tval(thr, &h_bfun->target);
            duk__debug_getinfo_flags_key(thr, "this_binding");
            duk_debug_write_tval(thr, &h_bfun->this_binding);
            duk__debug_getinfo_flags_key(thr, "nargs");
            duk_debug_write_int(thr, h_bfun->nargs);
            /* h_bfun->args not exposed now */
        }

        if (DUK_HOBJECT_IS_THREAD(h_obj)) {
            /* XXX: Currently no inspection of threads, e.g. value stack, call
             * stack, catch stack, etc.
             */
            duk_hthread *h_thr;
            h_thr = (duk_hthread *) h_obj;
            DUK_UNREF(h_thr);
        }

        if (DUK_HOBJECT_IS_DECENV(h_obj)) {
            duk_hdecenv *h_env;
            h_env = (duk_hdecenv *) h_obj;

            duk__debug_getinfo_flags_key(thr, "thread");
            duk_debug_write_heapptr(thr, (duk_heaphdr *) (h_env->thread));
            duk__debug_getinfo_flags_key(thr, "varmap");
            duk_debug_write_heapptr(thr, (duk_heaphdr *) (h_env->varmap));
            duk__debug_getinfo_prop_uint(thr, "regbase", (duk_uint_t) h_env->regbase_byteoff);
        }

        if (DUK_HOBJECT_IS_OBJENV(h_obj)) {
            duk_hobjenv *h_env;
            h_env = (duk_hobjenv *) h_obj;

            duk__debug_getinfo_flags_key(thr, "target");
            duk_debug_write_heapptr(thr, (duk_heaphdr *) (h_env->target));
            duk__debug_getinfo_prop_bool(thr, "has_this", h_env->has_this);
        }

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
        if (DUK_HOBJECT_IS_BUFOBJ(h_obj)) {
            duk_hbufobj *h_bufobj;
            h_bufobj = (duk_hbufobj *) h_obj;

            duk__debug_getinfo_prop_uint(thr, "slice_offset", h_bufobj->offset);
            duk__debug_getinfo_prop_uint(thr, "slice_length", h_bufobj->length);
            duk__debug_getinfo_prop_uint(thr, "elem_shift", (duk_uint_t) h_bufobj->shift);
            duk__debug_getinfo_prop_uint(thr, "elem_type", (duk_uint_t) h_bufobj->elem_type);
            duk__debug_getinfo_prop_bool(thr, "is_typedarray", (duk_uint_t) h_bufobj->is_typedarray);
            if (h_bufobj->buf != NULL) {
                duk__debug_getinfo_flags_key(thr, "buffer");
                duk_debug_write_heapptr(thr, (duk_heaphdr *) h_bufobj->buf);
            }
        }
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
        break;
    }
    case DUK_HTYPE_BUFFER: {
        duk_hbuffer *h_buf;

        h_buf = (duk_hbuffer *) h;
        duk__debug_getinfo_bitmask(thr,
                                   duk__debug_getinfo_hbuffer_keys,
                                   duk__debug_getinfo_hbuffer_masks,
                                   DUK_HEAPHDR_GET_FLAGS_RAW(h));
        duk__debug_getinfo_prop_uint(thr, "size", (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_buf));
        duk__debug_getinfo_flags_key(thr, "dataptr");
        duk_debug_write_pointer(thr, (void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_buf));
        duk__debug_getinfo_flags_key(thr, "data");
        duk_debug_write_hbuffer(thr, h_buf);  /* tolerates NULL h_buf */
        break;
    }
    default: {
        /* Since we already started writing the reply, just emit nothing. */
        DUK_D(DUK_DPRINT("inspect target pointer has invalid heaphdr type"));
    }
    }

    duk_debug_write_eom(thr);
}

DUK_LOCAL void duk__debug_handle_get_obj_prop_desc(duk_hthread *thr, duk_heap *heap) {
    duk_heaphdr *h;
    duk_hobject *h_obj;
    duk_hstring *h_key;
    duk_propdesc desc;

    DUK_D(DUK_DPRINT("debug command GetObjPropDesc"));
    DUK_UNREF(heap);

    h = duk_debug_read_any_ptr(thr);
    if (!h) {
        duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid target");
        return;
    }
    h_key = duk_debug_read_hstring(thr);
    if (h == NULL || DUK_HEAPHDR_GET_TYPE(h) != DUK_HTYPE_OBJECT || h_key == NULL) {
        goto fail_args;
    }
    h_obj = (duk_hobject *) h;

    if (duk_hobject_get_own_propdesc(thr, h_obj, h_key, &desc, 0 /*flags*/)) {
        duk_int_t virtual_idx;
        duk_bool_t rc;

        /* To use the shared helper need the virtual index. */
        DUK_ASSERT(desc.e_idx >= 0 || desc.a_idx >= 0);
        virtual_idx = (desc.a_idx >= 0 ? desc.a_idx :
                       (duk_int_t) DUK_HOBJECT_GET_ASIZE(h_obj) + desc.e_idx);

        duk_debug_write_reply(thr);
        rc = duk__debug_getprop_index(thr, heap, h_obj, (duk_uint_t) virtual_idx);
        DUK_ASSERT(rc == 1);
        DUK_UNREF(rc);
        duk_debug_write_eom(thr);
    } else {
        duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "not found");
    }
    return;

 fail_args:
    duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid args");
}

DUK_LOCAL void duk__debug_handle_get_obj_prop_desc_range(duk_hthread *thr, duk_heap *heap) {
    duk_heaphdr *h;
    duk_hobject *h_obj;
    duk_uint_t idx, idx_start, idx_end;

    DUK_D(DUK_DPRINT("debug command GetObjPropDescRange"));
    DUK_UNREF(heap);

    h = duk_debug_read_any_ptr(thr);
    idx_start = (duk_uint_t) duk_debug_read_int(thr);
    idx_end = (duk_uint_t) duk_debug_read_int(thr);
    if (h == NULL || DUK_HEAPHDR_GET_TYPE(h) != DUK_HTYPE_OBJECT) {
        goto fail_args;
    }
    h_obj = (duk_hobject *) h;

    /* The index range space is conceptually the array part followed by the
     * entry part.  Unlike normal enumeration all slots are exposed here as
     * is and return 'unused' if the slots are not in active use.  In particular
     * the array part is included for the full a_size regardless of what the
     * array .length is.
     */

    duk_debug_write_reply(thr);
    for (idx = idx_start; idx < idx_end; idx++) {
        if (!duk__debug_getprop_index(thr, heap, h_obj, idx)) {
            break;
        }
    }
    duk_debug_write_eom(thr);
    return;

 fail_args:
    duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid args");
}

#endif  /* DUK_USE_DEBUGGER_INSPECT */

/*
 *  Process incoming debug requests
 *
 *  Individual request handlers can push temporaries on the value stack and
 *  rely on duk__debug_process_message() to restore the value stack top
 *  automatically.
 */

/* Process one debug message.  Automatically restore value stack top to its
 * entry value, so that individual message handlers don't need exact value
 * stack handling which is convenient.
 */
DUK_LOCAL void duk__debug_process_message(duk_hthread *thr) {
    duk_heap *heap;
    duk_uint8_t x;
    duk_int32_t cmd;
    duk_idx_t entry_top;

    DUK_ASSERT(thr != NULL);
    heap = thr->heap;
    DUK_ASSERT(heap != NULL);

    entry_top = duk_get_top(thr);

    x = duk_debug_read_byte(thr);
    switch (x) {
    case DUK_DBG_IB_REQUEST: {
        cmd = duk_debug_read_int(thr);
        switch (cmd) {
        case DUK_DBG_CMD_BASICINFO: {
            duk__debug_handle_basic_info(thr, heap);
            break;
        }
        case DUK_DBG_CMD_TRIGGERSTATUS: {
            duk__debug_handle_trigger_status(thr, heap);
            break;
        }
        case DUK_DBG_CMD_PAUSE: {
            duk__debug_handle_pause(thr, heap);
            break;
        }
        case DUK_DBG_CMD_RESUME: {
            duk__debug_handle_resume(thr, heap);
            break;
        }
        case DUK_DBG_CMD_STEPINTO:
        case DUK_DBG_CMD_STEPOVER:
        case DUK_DBG_CMD_STEPOUT: {
            duk__debug_handle_step(thr, heap, cmd);
            break;
        }
        case DUK_DBG_CMD_LISTBREAK: {
            duk__debug_handle_list_break(thr, heap);
            break;
        }
        case DUK_DBG_CMD_ADDBREAK: {
            duk__debug_handle_add_break(thr, heap);
            break;
        }
        case DUK_DBG_CMD_DELBREAK: {
            duk__debug_handle_del_break(thr, heap);
            break;
        }
        case DUK_DBG_CMD_GETVAR: {
            duk__debug_handle_get_var(thr, heap);
            break;
        }
        case DUK_DBG_CMD_PUTVAR: {
            duk__debug_handle_put_var(thr, heap);
            break;
        }
        case DUK_DBG_CMD_GETCALLSTACK: {
            duk__debug_handle_get_call_stack(thr, heap);
            break;
        }
        case DUK_DBG_CMD_GETLOCALS: {
            duk__debug_handle_get_locals(thr, heap);
            break;
        }
        case DUK_DBG_CMD_EVAL: {
            duk__debug_handle_eval(thr, heap);
            break;
        }
        case DUK_DBG_CMD_DETACH: {
            /* The actual detached_cb call is postponed to message loop so
             * we don't need any special precautions here (just skip to EOM
             * on the already closed connection).
             */
            duk__debug_handle_detach(thr, heap);
            break;
        }
#if defined(DUK_USE_DEBUGGER_DUMPHEAP)
        case DUK_DBG_CMD_DUMPHEAP: {
            duk__debug_handle_dump_heap(thr, heap);
            break;
        }
#endif  /* DUK_USE_DEBUGGER_DUMPHEAP */
        case DUK_DBG_CMD_GETBYTECODE: {
            duk__debug_handle_get_bytecode(thr, heap);
            break;
        }
        case DUK_DBG_CMD_APPREQUEST: {
            duk__debug_handle_apprequest(thr, heap);
            break;
        }
#if defined(DUK_USE_DEBUGGER_INSPECT)
        case DUK_DBG_CMD_GETHEAPOBJINFO: {
            duk__debug_handle_get_heap_obj_info(thr, heap);
            break;
        }
        case DUK_DBG_CMD_GETOBJPROPDESC: {
            duk__debug_handle_get_obj_prop_desc(thr, heap);
            break;
        }
        case DUK_DBG_CMD_GETOBJPROPDESCRANGE: {
            duk__debug_handle_get_obj_prop_desc_range(thr, heap);
            break;
        }
#endif  /* DUK_USE_DEBUGGER_INSPECT */
        default: {
            DUK_D(DUK_DPRINT("debug command unsupported: %d", (int) cmd));
            duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNSUPPORTED, "unsupported command");
        }
        }  /* switch cmd */
        break;
    }
    case DUK_DBG_IB_REPLY: {
        DUK_D(DUK_DPRINT("debug reply, skipping"));
        break;
    }
    case DUK_DBG_IB_ERROR: {
        DUK_D(DUK_DPRINT("debug error, skipping"));
        break;
    }
    case DUK_DBG_IB_NOTIFY: {
        DUK_D(DUK_DPRINT("debug notify, skipping"));
        break;
    }
    default: {
        DUK_D(DUK_DPRINT("invalid initial byte, drop connection: %d", (int) x));
        goto fail;
    }
    }  /* switch initial byte */

    DUK_ASSERT(duk_get_top(thr) >= entry_top);
    duk_set_top(thr, entry_top);
    duk__debug_skip_to_eom(thr);
    return;

 fail:
    DUK_ASSERT(duk_get_top(thr) >= entry_top);
    duk_set_top(thr, entry_top);
    DUK__SET_CONN_BROKEN(thr, 1);
    return;
}

DUK_LOCAL void duk__check_resend_status(duk_hthread *thr) {
    if (thr->heap->dbg_read_cb != NULL && thr->heap->dbg_state_dirty) {
        duk_debug_send_status(thr);
        thr->heap->dbg_state_dirty = 0;
    }
}

DUK_INTERNAL duk_bool_t duk_debug_process_messages(duk_hthread *thr, duk_bool_t no_block) {
#if defined(DUK_USE_ASSERTIONS)
    duk_idx_t entry_top;
#endif
    duk_bool_t retval = 0;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
#if defined(DUK_USE_ASSERTIONS)
    entry_top = duk_get_top(thr);
#endif

    DUK_D(DUK_DPRINT("process debug messages: read_cb=%s, no_block=%ld, detaching=%ld, processing=%ld",
                     thr->heap->dbg_read_cb ? "not NULL" : "NULL", (long) no_block,
                     (long) thr->heap->dbg_detaching, (long) thr->heap->dbg_processing));
    DUK_DD(DUK_DDPRINT("top at entry: %ld", (long) duk_get_top(thr)));

    /* thr->heap->dbg_detaching may be != 0 if a debugger write outside
     * the message loop caused a transport error and detach1() to run.
     */
    DUK_ASSERT(thr->heap->dbg_detaching == 0 || thr->heap->dbg_detaching == 1);
    DUK_ASSERT(thr->heap->dbg_processing == 0);
    thr->heap->dbg_processing = 1;

    /* Ensure dirty state causes a Status even if never process any
     * messages.  This is expected by the bytecode executor when in
     * the running state.
     */
    duk__check_resend_status(thr);

    for (;;) {
        /* Process messages until we're no longer paused or we peek
         * and see there's nothing to read right now.
         */
        DUK_DD(DUK_DDPRINT("top at loop top: %ld", (long) duk_get_top(thr)));
        DUK_ASSERT(thr->heap->dbg_processing == 1);

        while (thr->heap->dbg_read_cb == NULL && thr->heap->dbg_detaching) {
            /* Detach is pending; can be triggered from outside the
             * debugger loop (e.g. Status notify write error) or by
             * previous message handling.  Call detached callback
             * here, in a controlled state, to ensure a possible
             * reattach inside the detached_cb is handled correctly.
             *
             * Recheck for detach in a while loop: an immediate
             * reattach involves a call to duk_debugger_attach()
             * which writes a debugger handshake line immediately
             * inside the API call.  If the transport write fails
             * for that handshake, we can immediately end up in a
             * "transport broken, detaching" case several times here.
             * Loop back until we're either cleanly attached or
             * fully detached.
             *
             * NOTE: Reset dbg_processing = 1 forcibly, in case we
             * re-attached; duk_debugger_attach() sets dbg_processing
             * to 0 at the moment.
             */

            DUK_D(DUK_DPRINT("detach pending (dbg_read_cb == NULL, dbg_detaching != 0), call detach2"));

            duk__debug_do_detach2(thr->heap);
            thr->heap->dbg_processing = 1;  /* may be set to 0 by duk_debugger_attach() inside callback */

            DUK_D(DUK_DPRINT("after detach2 (and possible reattach): dbg_read_cb=%s, dbg_detaching=%ld",
                             thr->heap->dbg_read_cb ? "not NULL" : "NULL", (long) thr->heap->dbg_detaching));
        }
        DUK_ASSERT(thr->heap->dbg_detaching == 0);  /* true even with reattach */
        DUK_ASSERT(thr->heap->dbg_processing == 1);  /* even after a detach and possible reattach */

        if (thr->heap->dbg_read_cb == NULL) {
            DUK_D(DUK_DPRINT("debug connection broken (and not detaching), stop processing messages"));
            break;
        }

        if (!DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap) || no_block) {
            if (!duk_debug_read_peek(thr)) {
                /* Note: peek cannot currently trigger a detach
                 * so the dbg_detaching == 0 assert outside the
                 * loop is correct.
                 */
                DUK_D(DUK_DPRINT("processing debug message, peek indicated no data, stop processing messages"));
                break;
            }
            DUK_D(DUK_DPRINT("processing debug message, peek indicated there is data, handle it"));
        } else {
            DUK_D(DUK_DPRINT("paused, process debug message, blocking if necessary"));
        }

        duk__check_resend_status(thr);
        duk__debug_process_message(thr);
        duk__check_resend_status(thr);

        retval = 1;  /* processed one or more messages */
    }

    DUK_ASSERT(thr->heap->dbg_detaching == 0);
    DUK_ASSERT(thr->heap->dbg_processing == 1);
    thr->heap->dbg_processing = 0;

    /* As an initial implementation, read flush after exiting the message
     * loop.  If transport is broken, this is a no-op (with debug logs).
     */
    duk_debug_read_flush(thr);  /* this cannot initiate a detach */
    DUK_ASSERT(thr->heap->dbg_detaching == 0);

    DUK_DD(DUK_DDPRINT("top at exit: %ld", (long) duk_get_top(thr)));

#if defined(DUK_USE_ASSERTIONS)
    /* Easy to get wrong, so assert for it. */
    DUK_ASSERT(entry_top == duk_get_top(thr));
#endif

    return retval;
}

/*
 *  Halt execution helper
 */

/* Halt execution and enter a debugger message loop until execution is resumed
 * by the client.  PC for the current activation may be temporarily decremented
 * so that the "current" instruction will be shown by the client.  This helper
 * is callable from anywhere, also outside bytecode executor.
 */

DUK_INTERNAL void duk_debug_halt_execution(duk_hthread *thr, duk_bool_t use_prev_pc) {
    duk_activation *act;
    duk_hcompfunc *fun;
    duk_instr_t *old_pc = NULL;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT(duk_debug_is_attached(thr->heap));
    DUK_ASSERT(thr->heap->dbg_processing == 0);
    DUK_ASSERT(!duk_debug_is_paused(thr->heap));

    duk_debug_set_paused(thr->heap);

    act = thr->callstack_curr;

    /* NOTE: act may be NULL if an error is thrown outside of any activation,
     * which may happen in the case of, e.g. syntax errors.
     */

    /* Decrement PC if that was requested, this requires a PC sync. */
    if (act != NULL) {
        duk_hthread_sync_currpc(thr);
        old_pc = act->curr_pc;
        fun = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act);

        /* Short circuit if is safe: if act->curr_pc != NULL, 'fun' is
         * guaranteed to be a non-NULL ECMAScript function.
         */
        DUK_ASSERT(act->curr_pc == NULL ||
                   (fun != NULL && DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) fun)));
        if (use_prev_pc &&
            act->curr_pc != NULL &&
            act->curr_pc > DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, fun)) {
            act->curr_pc--;
        }
    }

    /* Process debug messages until we are no longer paused. */

    /* NOTE: This is a bit fragile.  It's important to ensure that
     * duk_debug_process_messages() never throws an error or
     * act->curr_pc will never be reset.
     */

    thr->heap->dbg_state_dirty = 1;
    while (DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap)) {
        DUK_ASSERT(duk_debug_is_attached(thr->heap));
        DUK_ASSERT(thr->heap->dbg_processing == 0);
        duk_debug_process_messages(thr, 0 /*no_block*/);
    }

    /* XXX: Decrementing and restoring act->curr_pc works now, but if the
     * debugger message loop gains the ability to adjust the current PC
     * (e.g. a forced jump) restoring the PC here will break.  Another
     * approach would be to use a state flag for the "decrement 1 from
     * topmost activation's PC" and take it into account whenever dealing
     * with PC values.
     */
    if (act != NULL) {
        act->curr_pc = old_pc;  /* restore PC */
    }
}

/*
 *  Breakpoint management
 */

DUK_INTERNAL duk_small_int_t duk_debug_add_breakpoint(duk_hthread *thr, duk_hstring *filename, duk_uint32_t line) {
    duk_heap *heap;
    duk_breakpoint *b;

    /* Caller must trigger recomputation of active breakpoint list.  To
     * ensure stale values are not used if that doesn't happen, clear the
     * active breakpoint list here.
     */

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(filename != NULL);
    heap = thr->heap;
    DUK_ASSERT(heap != NULL);

    if (heap->dbg_breakpoint_count >= DUK_HEAP_MAX_BREAKPOINTS) {
        DUK_D(DUK_DPRINT("failed to add breakpoint for %O:%ld, all breakpoint slots used",
                         (duk_heaphdr *) filename, (long) line));
        return -1;
    }
    heap->dbg_breakpoints_active[0] = (duk_breakpoint *) NULL;
    b = heap->dbg_breakpoints + (heap->dbg_breakpoint_count++);
    b->filename = filename;
    b->line = line;
    DUK_HSTRING_INCREF(thr, filename);

    return (duk_small_int_t) (heap->dbg_breakpoint_count - 1);  /* index */
}

DUK_INTERNAL duk_bool_t duk_debug_remove_breakpoint(duk_hthread *thr, duk_small_uint_t breakpoint_index) {
    duk_heap *heap;
    duk_hstring *h;
    duk_breakpoint *b;
    duk_size_t move_size;

    /* Caller must trigger recomputation of active breakpoint list.  To
     * ensure stale values are not used if that doesn't happen, clear the
     * active breakpoint list here.
     */

    DUK_ASSERT(thr != NULL);
    heap = thr->heap;
    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(duk_debug_is_attached(thr->heap));
    DUK_ASSERT_DISABLE(breakpoint_index >= 0);  /* unsigned */

    if (breakpoint_index >= heap->dbg_breakpoint_count) {
        DUK_D(DUK_DPRINT("invalid breakpoint index: %ld", (long) breakpoint_index));
        return 0;
    }
    b = heap->dbg_breakpoints + breakpoint_index;

    h = b->filename;
    DUK_ASSERT(h != NULL);

    move_size = sizeof(duk_breakpoint) * (heap->dbg_breakpoint_count - breakpoint_index - 1);
    duk_memmove((void *) b,
                (const void *) (b + 1),
                (size_t) move_size);

    heap->dbg_breakpoint_count--;
    heap->dbg_breakpoints_active[0] = (duk_breakpoint *) NULL;

    DUK_HSTRING_DECREF(thr, h);  /* side effects */
    DUK_UNREF(h);  /* w/o refcounting */

    /* Breakpoint entries above the used area are left as garbage. */

    return 1;
}

/*
 *  Misc state management
 */

DUK_INTERNAL duk_bool_t duk_debug_is_attached(duk_heap *heap) {
    return (heap->dbg_read_cb != NULL);
}

DUK_INTERNAL duk_bool_t duk_debug_is_paused(duk_heap *heap) {
    return (DUK_HEAP_HAS_DEBUGGER_PAUSED(heap) != 0);
}

DUK_INTERNAL void duk_debug_set_paused(duk_heap *heap) {
    if (duk_debug_is_paused(heap)) {
        DUK_D(DUK_DPRINT("trying to set paused state when already paused, ignoring"));
    } else {
        DUK_HEAP_SET_DEBUGGER_PAUSED(heap);
        heap->dbg_state_dirty = 1;
        duk_debug_clear_pause_state(heap);
        DUK_ASSERT(heap->ms_running == 0);  /* debugger can't be triggered within mark-and-sweep */
        heap->ms_running = 2;  /* prevent mark-and-sweep, prevent refzero queueing */
        heap->ms_prevent_count++;
        DUK_ASSERT(heap->ms_prevent_count != 0);  /* Wrap. */
        DUK_ASSERT(heap->heap_thread != NULL);
    }
}

DUK_INTERNAL void duk_debug_clear_paused(duk_heap *heap) {
    if (duk_debug_is_paused(heap)) {
        DUK_HEAP_CLEAR_DEBUGGER_PAUSED(heap);
        heap->dbg_state_dirty = 1;
        duk_debug_clear_pause_state(heap);
        DUK_ASSERT(heap->ms_running == 2);
        DUK_ASSERT(heap->ms_prevent_count > 0);
        heap->ms_prevent_count--;
        heap->ms_running = 0;
        DUK_ASSERT(heap->heap_thread != NULL);
    } else {
        DUK_D(DUK_DPRINT("trying to clear paused state when not paused, ignoring"));
    }
}

DUK_INTERNAL void duk_debug_clear_pause_state(duk_heap *heap) {
    heap->dbg_pause_flags = 0;
    heap->dbg_pause_act = NULL;
    heap->dbg_pause_startline = 0;
}

#else  /* DUK_USE_DEBUGGER_SUPPORT */

/* No debugger support. */

#endif  /* DUK_USE_DEBUGGER_SUPPORT */

/* automatic undefs */
#undef DUK__DBG_TPORT_ENTER
#undef DUK__DBG_TPORT_EXIT
#undef DUK__SET_CONN_BROKEN
//#line 1 "duk_error_augment.c"
/*
 *  Augmenting errors at their creation site and their throw site.
 *
 *  When errors are created, traceback data is added by built-in code
 *  and a user error handler (if defined) can process or replace the
 *  error.  Similarly, when errors are thrown, a user error handler
 *  (if defined) can process or replace the error.
 *
 *  Augmentation and other processing at error creation time is nice
 *  because an error is only created once, but it may be thrown and
 *  rethrown multiple times.  User error handler registered for processing
 *  an error at its throw site must be careful to handle rethrowing in
 *  a useful manner.
 *
 *  Error augmentation may throw an internal error (e.g. alloc error).
 *
 *  ECMAScript allows throwing any values, so all values cannot be
 *  augmented.  Currently, the built-in augmentation at error creation
 *  only augments error values which are Error instances (= have the
 *  built-in Error.prototype in their prototype chain) and are also
 *  extensible.  User error handlers have no limitations in this respect.
 */

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

/*
 *  Helper for calling a user error handler.
 *
 *  'thr' must be the currently active thread; the error handler is called
 *  in its context.  The valstack of 'thr' must have the error value on
 *  top, and will be replaced by another error value based on the return
 *  value of the error handler.
 *
 *  The helper calls duk_handle_call() recursively in protected mode.
 *  Before that call happens, no longjmps should happen; as a consequence,
 *  we must assume that the valstack contains enough temporary space for
 *  arguments and such.
 *
 *  While the error handler runs, any errors thrown will not trigger a
 *  recursive error handler call (this is implemented using a heap level
 *  flag which will "follow" through any coroutines resumed inside the
 *  error handler).  If the error handler is not callable or throws an
 *  error, the resulting error replaces the original error (for Duktape
 *  internal errors, duk_error_throw.c further substitutes this error with
 *  a DoubleError which is not ideal).  This would be easy to change and
 *  even signal to the caller.
 *
 *  The user error handler is stored in 'Duktape.errCreate' or
 *  'Duktape.errThrow' depending on whether we're augmenting the error at
 *  creation or throw time.  There are several alternatives to this approach,
 *  see doc/error-objects.rst for discussion.
 *
 *  Note: since further longjmp()s may occur while calling the error handler
 *  (for many reasons, e.g. a labeled 'break' inside the handler), the
 *  caller can make no assumptions on the thr->heap->lj state after the
 *  call (this affects especially duk_error_throw.c).  This is not an issue
 *  as long as the caller writes to the lj state only after the error handler
 *  finishes.
 */

#if defined(DUK_USE_ERRTHROW) || defined(DUK_USE_ERRCREATE)
DUK_LOCAL void duk__err_augment_user(duk_hthread *thr, duk_small_uint_t stridx_cb) {
    duk_tval *tv_hnd;
    duk_int_t rc;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT_STRIDX_VALID(stridx_cb);

    if (thr->heap->augmenting_error) {
        DUK_D(DUK_DPRINT("recursive call to error augmentation, ignore"));
        return;
    }

    /*
     *  Check whether or not we have an error handler.
     *
     *  We must be careful of not triggering an error when looking up the
     *  property.  For instance, if the property is a getter, we don't want
     *  to call it, only plain values are allowed.  The value, if it exists,
     *  is not checked.  If the value is not a function, a TypeError happens
     *  when it is called and that error replaces the original one.
     */

    DUK_ASSERT_VALSTACK_SPACE(thr, 4);  /* 3 entries actually needed below */

    /* [ ... errval ] */

    if (thr->builtins[DUK_BIDX_DUKTAPE] == NULL) {
        /* When creating built-ins, some of the built-ins may not be set
         * and we want to tolerate that when throwing errors.
         */
        DUK_DD(DUK_DDPRINT("error occurred when DUK_BIDX_DUKTAPE is NULL, ignoring"));
        return;
    }
    tv_hnd = duk_hobject_find_entry_tval_ptr_stridx(thr->heap,
                                                    thr->builtins[DUK_BIDX_DUKTAPE],
                                                    stridx_cb);
    if (tv_hnd == NULL) {
        DUK_DD(DUK_DDPRINT("error handler does not exist or is not a plain value: %!T",
                           (duk_tval *) tv_hnd));
        return;
    }
    DUK_DDD(DUK_DDDPRINT("error handler dump (callability not checked): %!T",
                         (duk_tval *) tv_hnd));
    duk_push_tval(thr, tv_hnd);

    /* [ ... errval errhandler ] */

    duk_insert(thr, -2);  /* -> [ ... errhandler errval ] */
    duk_push_undefined(thr);
    duk_insert(thr, -2);  /* -> [ ... errhandler undefined(= this) errval ] */

    /* [ ... errhandler undefined errval ] */

    /*
     *  heap->augmenting_error prevents recursive re-entry and also causes
     *  call handling to use a larger (but not unbounded) call stack limit
     *  for the duration of error augmentation.
     *
     *  We ignore errors now: a success return and an error value both
     *  replace the original error value.  (This would be easy to change.)
     */

    DUK_ASSERT(thr->heap->augmenting_error == 0);
    thr->heap->augmenting_error = 1;

    rc = duk_pcall_method(thr, 1);
    DUK_UNREF(rc);  /* no need to check now: both success and error are OK */

    DUK_ASSERT(thr->heap->augmenting_error == 1);
    thr->heap->augmenting_error = 0;

    /* [ ... errval ] */
}
#endif  /* DUK_USE_ERRTHROW || DUK_USE_ERRCREATE */

/*
 *  Add ._Tracedata to an error on the stack top.
 */

#if defined(DUK_USE_TRACEBACKS)
DUK_LOCAL void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_small_uint_t flags) {
    duk_activation *act;
    duk_int_t depth;
    duk_int_t arr_size;
    duk_tval *tv;
    duk_hstring *s;
    duk_uint32_t u32;
    duk_double_t d;

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

    /* [ ... error ] */

    /*
     *  The traceback format is pretty arcane in an attempt to keep it compact
     *  and cheap to create.  It may change arbitrarily from version to version.
     *  It should be decoded/accessed through version specific accessors only.
     *
     *  See doc/error-objects.rst.
     */

    DUK_DDD(DUK_DDDPRINT("adding traceback to object: %!T",
                         (duk_tval *) duk_get_tval(thr, -1)));

    /* Preallocate array to correct size, so that we can just write out
     * the _Tracedata values into the array part.
     */
    act = thr->callstack_curr;
    depth = DUK_USE_TRACEBACK_DEPTH;
    DUK_ASSERT(thr_callstack->callstack_top <= DUK_INT_MAX);  /* callstack limits */
    if (depth > (duk_int_t) thr_callstack->callstack_top) {
        depth = (duk_int_t) thr_callstack->callstack_top;
    }
    if (depth > 0) {
        if (flags & DUK_AUGMENT_FLAG_SKIP_ONE) {
            DUK_ASSERT(act != NULL);
            act = act->parent;
            depth--;
        }
    }
    arr_size = depth * 2;
    if (thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL) {
        arr_size += 2;
    }
    if (c_filename) {
        /* We need the C filename to be interned before getting the
         * array part pointer to avoid any GC interference while the
         * array part is populated.
         */
        duk_push_string(thr, c_filename);
        arr_size += 2;
    }

    /* XXX: Uninitialized would be OK.  Maybe add internal primitive to
     * push bare duk_harray with size?
     */
    DUK_D(DUK_DPRINT("preallocated _Tracedata to %ld items", (long) arr_size));
    tv = duk_push_harray_with_size_outptr(thr, (duk_uint32_t) arr_size);
    duk_clear_prototype(thr, -1);
    DUK_ASSERT(duk_is_bare_object(thr, -1));
    DUK_ASSERT(arr_size == 0 || tv != NULL);

    /* Compiler SyntaxErrors (and other errors) come first, and are
     * blamed by default (not flagged "noblame").
     */
    if (thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL) {
        s = thr->compile_ctx->h_filename;
        DUK_TVAL_SET_STRING(tv, s);
        DUK_HSTRING_INCREF(thr, s);
        tv++;

        u32 = (duk_uint32_t) thr->compile_ctx->curr_token.start_line;  /* (flags<<32) + (line), flags = 0 */
        DUK_TVAL_SET_U32(tv, u32);
        tv++;
    }

    /* Filename/line from C macros (__FILE__, __LINE__) are added as an
     * entry with a special format: (string, number).  The number contains
     * the line and flags.
     */

    /* [ ... error c_filename? arr ] */

    if (c_filename) {
        DUK_ASSERT(DUK_TVAL_IS_STRING(thr->valstack_top - 2));
        s = DUK_TVAL_GET_STRING(thr->valstack_top - 2);  /* interned c_filename */
        DUK_ASSERT(s != NULL);
        DUK_TVAL_SET_STRING(tv, s);
        DUK_HSTRING_INCREF(thr, s);
        tv++;

        d = ((flags & DUK_AUGMENT_FLAG_NOBLAME_FILELINE) ? ((duk_double_t) DUK_TB_FLAG_NOBLAME_FILELINE) * DUK_DOUBLE_2TO32 : 0.0) +
            (duk_double_t) c_line;
        DUK_TVAL_SET_DOUBLE(tv, d);
        tv++;
    }

    /* Traceback depth doesn't take into account the filename/line
     * special handling above (intentional).
     */
    for (; depth-- > 0; act = act->parent) {
        duk_uint32_t pc;
        duk_tval *tv_src;

        /* [... arr] */

        DUK_ASSERT(act != NULL);  /* depth check above, assumes book-keeping is correct */
        DUK_ASSERT_DISABLE(act->pc >= 0);  /* unsigned */

        /* Add function object. */
        tv_src = &act->tv_func;  /* object (function) or lightfunc */
        DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv_src) || DUK_TVAL_IS_LIGHTFUNC(tv_src));
        DUK_TVAL_SET_TVAL(tv, tv_src);
        DUK_TVAL_INCREF(thr, tv);
        tv++;

        /* Add a number containing: pc, activation flags.
         *
         * PC points to next instruction, find offending PC.  Note that
         * PC == 0 for native code.
         */
        pc = (duk_uint32_t) duk_hthread_get_act_prev_pc(thr_callstack, act);
        DUK_ASSERT_DISABLE(pc >= 0);  /* unsigned */
        DUK_ASSERT((duk_double_t) pc < DUK_DOUBLE_2TO32);  /* assume PC is at most 32 bits and non-negative */
        d = ((duk_double_t) act->flags) * DUK_DOUBLE_2TO32 + (duk_double_t) pc;
        DUK_TVAL_SET_DOUBLE(tv, d);
        tv++;
    }

#if defined(DUK_USE_ASSERTIONS)
    {
        duk_harray *a;
        a = (duk_harray *) duk_known_hobject(thr, -1);
        DUK_ASSERT(a != NULL);
        DUK_ASSERT((duk_uint32_t) (tv - DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) a)) == a->length);
        DUK_ASSERT(a->length == (duk_uint32_t) arr_size);
        DUK_ASSERT(duk_is_bare_object(thr, -1));
    }
#endif

    /* [ ... error c_filename? arr ] */

    if (c_filename) {
        duk_remove_m2(thr);
    }

    /* [ ... error arr ] */

    duk_xdef_prop_stridx_short_wec(thr, -2, DUK_STRIDX_INT_TRACEDATA);  /* -> [ ... error ] */
}
#endif  /* DUK_USE_TRACEBACKS */

/*
 *  Add .fileName and .lineNumber to an error on the stack top.
 */

#if defined(DUK_USE_AUGMENT_ERROR_CREATE) && !defined(DUK_USE_TRACEBACKS)
DUK_LOCAL void duk__add_fileline(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_small_uint_t flags) {
#if defined(DUK_USE_ASSERTIONS)
    duk_int_t entry_top;
#endif

#if defined(DUK_USE_ASSERTIONS)
    entry_top = duk_get_top(thr);
#endif

    /*
     *  If tracebacks are disabled, 'fileName' and 'lineNumber' are added
     *  as plain own properties.  Since Error.prototype has accessors of
     *  the same name, we need to define own properties directly (cannot
     *  just use e.g. duk_put_prop_stridx).  Existing properties are not
     *  overwritten in case they already exist.
     */

    if (thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL) {
        /* Compiler SyntaxError (or other error) gets the primary blame.
         * Currently no flag to prevent blaming.
         */
        duk_push_uint(thr, (duk_uint_t) thr->compile_ctx->curr_token.start_line);
        duk_push_hstring(thr, thr->compile_ctx->h_filename);
    } else if (c_filename && (flags & DUK_AUGMENT_FLAG_NOBLAME_FILELINE) == 0) {
        /* C call site gets blamed next, unless flagged not to do so.
         * XXX: file/line is disabled in minimal builds, so disable this
         * too when appropriate.
         */
        duk_push_int(thr, c_line);
        duk_push_string(thr, c_filename);
    } else {
        /* Finally, blame the innermost callstack entry which has a
         * .fileName property.
         */
        duk_small_uint_t depth;
        duk_uint32_t ecma_line;
        duk_activation *act;

        DUK_ASSERT(thr_callstack->callstack_top <= DUK_INT_MAX);  /* callstack limits */
        depth = DUK_USE_TRACEBACK_DEPTH;
        if (depth > thr_callstack->callstack_top) {
            depth = thr_callstack->callstack_top;
        }
        for (act = thr_callstack->callstack_curr; depth-- > 0; act = act->parent) {
            duk_hobject *func;
            duk_uint32_t pc;

            DUK_ASSERT(act != NULL);
            func = DUK_ACT_GET_FUNC(act);
            if (func == NULL) {
                /* Lightfunc, not blamed now. */
                continue;
            }

            /* PC points to next instruction, find offending PC,
             * PC == 0 for native code.
             */
            pc = duk_hthread_get_act_prev_pc(thr, act);  /* thr argument only used for thr->heap, so specific thread doesn't matter */
            DUK_UNREF(pc);
            DUK_ASSERT_DISABLE(pc >= 0);  /* unsigned */
            DUK_ASSERT((duk_double_t) pc < DUK_DOUBLE_2TO32);  /* assume PC is at most 32 bits and non-negative */

            duk_push_hobject(thr, func);

            /* [ ... error func ] */

            duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_FILE_NAME);
            if (!duk_is_string_notsymbol(thr, -1)) {
                duk_pop_2(thr);
                continue;
            }

            /* [ ... error func fileName ] */

            ecma_line = 0;
#if defined(DUK_USE_PC2LINE)
            if (DUK_HOBJECT_IS_COMPFUNC(func)) {
                ecma_line = duk_hobject_pc2line_query(thr, -2, (duk_uint_fast32_t) pc);
            } else {
                /* Native function, no relevant lineNumber. */
            }
#endif  /* DUK_USE_PC2LINE */
            duk_push_u32(thr, ecma_line);

            /* [ ... error func fileName lineNumber ] */

            duk_replace(thr, -3);

            /* [ ... error lineNumber fileName ] */
            goto define_props;
        }

        /* No activation matches, use undefined for both .fileName and
         * .lineNumber (matches what we do with a _Tracedata based
         * no-match lookup.
         */
        duk_push_undefined(thr);
        duk_push_undefined(thr);
    }

 define_props:
    /* [ ... error lineNumber fileName ] */
#if defined(DUK_USE_ASSERTIONS)
    DUK_ASSERT(duk_get_top(thr) == entry_top + 2);
#endif
    duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_C | DUK_PROPDESC_FLAG_NO_OVERWRITE);
    duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_C | DUK_PROPDESC_FLAG_NO_OVERWRITE);
}
#endif  /* DUK_USE_AUGMENT_ERROR_CREATE && !DUK_USE_TRACEBACKS */

/*
 *  Add line number to a compiler error.
 */

#if defined(DUK_USE_AUGMENT_ERROR_CREATE)
DUK_LOCAL void duk__add_compiler_error_line(duk_hthread *thr) {

    /* Append a "(line NNN)" to the "message" property of any error
     * thrown during compilation.  Usually compilation errors are
     * SyntaxErrors but they can also be out-of-memory errors and
     * the like.
     */

    /* [ ... error ] */

    DUK_ASSERT(duk_is_object(thr, -1));

    if (!(thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL)) {
        return;
    }

    DUK_DDD(DUK_DDDPRINT("compile error, before adding line info: %!T",
                         (duk_tval *) duk_get_tval(thr, -1)));

    if (duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_MESSAGE)) {
        duk_bool_t at_end;

        /* Best guesstimate that error occurred at end of input, token
         * truncated by end of input, etc.
         */
#if 0
        at_end = (thr->compile_ctx->curr_token.start_offset + 1 >= thr->compile_ctx->lex.input_length);
        at_end = (thr->compile_ctx->lex.window[0].codepoint < 0 || thr->compile_ctx->lex.window[1].codepoint < 0);
#endif
        at_end = (thr->compile_ctx->lex.window[0].codepoint < 0);

        DUK_D(DUK_DPRINT("syntax error, determined at_end=%ld; curr_token.start_offset=%ld, "
                         "lex.input_length=%ld, window[0].codepoint=%ld, window[1].codepoint=%ld",
                         (long) at_end,
                         (long) thr->compile_ctx->curr_token.start_offset,
                         (long) thr->compile_ctx->lex.input_length,
                         (long) thr->compile_ctx->lex.window[0].codepoint,
                         (long) thr->compile_ctx->lex.window[1].codepoint));

        duk_push_sprintf(thr, " (line %ld%s)",
                         (long) thr->compile_ctx->curr_token.start_line,
                         at_end ? ", end of input" : "");
        duk_concat(thr, 2);
        duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_MESSAGE);
    } else {
        duk_pop(thr);
    }

    DUK_DDD(DUK_DDDPRINT("compile error, after adding line info: %!T",
                         (duk_tval *) duk_get_tval(thr, -1)));
}
#endif  /* DUK_USE_AUGMENT_ERROR_CREATE */

/*
 *  Augment an error being created using Duktape specific properties
 *  like _Tracedata or .fileName/.lineNumber.
 */

#if defined(DUK_USE_AUGMENT_ERROR_CREATE)
DUK_LOCAL void duk__err_augment_builtin_create(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_hobject *obj, duk_small_uint_t flags) {
#if defined(DUK_USE_ASSERTIONS)
    duk_int_t entry_top;
#endif

#if defined(DUK_USE_ASSERTIONS)
    entry_top = duk_get_top(thr);
#endif
    DUK_ASSERT(obj != NULL);

    DUK_UNREF(obj);  /* unreferenced w/o tracebacks */

    duk__add_compiler_error_line(thr);

#if defined(DUK_USE_TRACEBACKS)
    /* If tracebacks are enabled, the '_Tracedata' property is the only
     * thing we need: 'fileName' and 'lineNumber' are virtual properties
     * which use '_Tracedata'.  (Check _Tracedata only as own property.)
     */
    if (duk_hobject_find_entry_tval_ptr_stridx(thr->heap, obj, DUK_STRIDX_INT_TRACEDATA) != NULL) {
        DUK_DDD(DUK_DDDPRINT("error value already has a '_Tracedata' property, not modifying it"));
    } else {
        duk__add_traceback(thr, thr_callstack, c_filename, c_line, flags);
    }
#else
    /* Without tracebacks the concrete .fileName and .lineNumber need
     * to be added directly.
     */
    duk__add_fileline(thr, thr_callstack, c_filename, c_line, flags);
#endif

#if defined(DUK_USE_ASSERTIONS)
    DUK_ASSERT(duk_get_top(thr) == entry_top);
#endif
}
#endif  /* DUK_USE_AUGMENT_ERROR_CREATE */

/*
 *  Augment an error at creation time with _Tracedata/fileName/lineNumber
 *  and allow a user error handler (if defined) to process/replace the error.
 *  The error to be augmented is at the stack top.
 *
 *  thr: thread containing the error value
 *  thr_callstack: thread which should be used for generating callstack etc.
 *  c_filename: C __FILE__ related to the error
 *  c_line: C __LINE__ related to the error
 *  flags & DUK_AUGMENT_FLAG_NOBLAME_FILELINE:
 *      if true, don't fileName/line as error source, otherwise use traceback
 *      (needed because user code filename/line are reported but internal ones
 *      are not)
 */

#if defined(DUK_USE_AUGMENT_ERROR_CREATE)
DUK_INTERNAL void duk_err_augment_error_create(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_small_uint_t flags) {
    duk_hobject *obj;

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

    /* [ ... error ] */

    /*
     *  Criteria for augmenting:
     *
     *   - augmentation enabled in build (naturally)
     *   - error value internal prototype chain contains the built-in
     *     Error prototype object (i.e. 'val instanceof Error')
     *
     *  Additional criteria for built-in augmenting:
     *
     *   - error value is an extensible object
     */

    obj = duk_get_hobject(thr, -1);
    if (!obj) {
        DUK_DDD(DUK_DDDPRINT("value is not an object, skip both built-in and user augment"));
        return;
    }
    if (!duk_hobject_prototype_chain_contains(thr, obj, thr->builtins[DUK_BIDX_ERROR_PROTOTYPE], 1 /*ignore_loop*/)) {
        /* If the value has a prototype loop, it's critical not to
         * throw here.  Instead, assume the value is not to be
         * augmented.
         */
        DUK_DDD(DUK_DDDPRINT("value is not an error instance, skip both built-in and user augment"));
        return;
    }
    if (DUK_HOBJECT_HAS_EXTENSIBLE(obj)) {
        DUK_DDD(DUK_DDDPRINT("error meets criteria, built-in augment"));
        duk__err_augment_builtin_create(thr, thr_callstack, c_filename, c_line, obj, flags);
    } else {
        DUK_DDD(DUK_DDDPRINT("error does not meet criteria, no built-in augment"));
    }

    /* [ ... error ] */

#if defined(DUK_USE_ERRCREATE)
    duk__err_augment_user(thr, DUK_STRIDX_ERR_CREATE);
#endif
}
#endif  /* DUK_USE_AUGMENT_ERROR_CREATE */

/*
 *  Augment an error at throw time; allow a user error handler (if defined)
 *  to process/replace the error.  The error to be augmented is at the
 *  stack top.
 */

#if defined(DUK_USE_AUGMENT_ERROR_THROW)
DUK_INTERNAL void duk_err_augment_error_throw(duk_hthread *thr) {
#if defined(DUK_USE_ERRTHROW)
    duk__err_augment_user(thr, DUK_STRIDX_ERR_THROW);
#endif  /* DUK_USE_ERRTHROW */
}
#endif  /* DUK_USE_AUGMENT_ERROR_THROW */
//#line 1 "duk_error_longjmp.c"
/*
 *  Do a longjmp call, calling the fatal error handler if no
 *  catchpoint exists.
 */

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

#if defined(DUK_USE_PREFER_SIZE)
DUK_NORETURN(DUK_LOCAL_DECL void duk__uncaught_minimal(duk_hthread *thr));
DUK_LOCAL void duk__uncaught_minimal(duk_hthread *thr) {
    (void) duk_fatal(thr, "uncaught error");
    DUK_WO_NORETURN(return;);
}
#endif

#if 0
DUK_NORETURN(DUK_LOCAL_DECL void duk__uncaught_readable(duk_hthread *thr));
DUK_LOCAL void duk__uncaught_readable(duk_hthread *thr) {
    const char *summary;
    char buf[DUK_USE_FATAL_MAXLEN];

    summary = duk_push_string_tval_readable(thr, &thr->heap->lj.value1);
    DUK_SNPRINTF(buf, sizeof(buf), "uncaught: %s", summary);
    buf[sizeof(buf) - 1] = (char) 0;
    (void) duk_fatal(thr, (const char *) buf);
    DUK_WO_NORETURN(return;);
}
#endif

#if !defined(DUK_USE_PREFER_SIZE)
DUK_NORETURN(DUK_LOCAL_DECL void duk__uncaught_error_aware(duk_hthread *thr));
DUK_LOCAL void duk__uncaught_error_aware(duk_hthread *thr) {
    const char *summary;
    char buf[DUK_USE_FATAL_MAXLEN];

    summary = duk_push_string_tval_readable_error(thr, &thr->heap->lj.value1);
    DUK_ASSERT(summary != NULL);
    DUK_SNPRINTF(buf, sizeof(buf), "uncaught: %s", summary);
    buf[sizeof(buf) - 1] = (char) 0;
    (void) duk_fatal(thr, (const char *) buf);
    DUK_WO_NORETURN(return;);
}
#endif

DUK_INTERNAL void duk_err_longjmp(duk_hthread *thr) {
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);

    DUK_DD(DUK_DDPRINT("longjmp error: type=%d iserror=%d value1=%!T value2=%!T",
                       (int) thr->heap->lj.type, (int) thr->heap->lj.iserror,
                       &thr->heap->lj.value1, &thr->heap->lj.value2));

    /* Prevent finalizer execution during error handling.  All error
     * handling sites will process pending finalizers once error handling
     * is complete and we're ready for the side effects.  Does not prevent
     * refzero freeing or mark-and-sweep during error handling.
     *
     * NOTE: when we come here some calling code may have used DECREF
     * NORZ macros without an explicit DUK_REFZERO_CHECK_xxx() call.
     * We don't want to do it here because it would just check for
     * pending finalizers and we prevent that explicitly.  Instead,
     * the error catcher will run the finalizers once error handling
     * is complete.
     */

    DUK_ASSERT_LJSTATE_SET(thr->heap);

    thr->heap->pf_prevent_count++;
    DUK_ASSERT(thr->heap->pf_prevent_count != 0);  /* Wrap. */

#if defined(DUK_USE_ASSERTIONS)
    /* XXX: set this immediately when longjmp state is set */
    DUK_ASSERT(thr->heap->error_not_allowed == 0);  /* Detect error within critical section. */
    thr->heap->error_not_allowed = 1;
#endif

    DUK_DD(DUK_DDPRINT("about to longjmp, pf_prevent_count=%ld", (long) thr->heap->pf_prevent_count));

    /* If we don't have a jmpbuf_ptr, there is little we can do except
     * cause a fatal error.  The caller's expectation is that we never
     * return.
     */
    if (!thr->heap->lj.jmpbuf_ptr) {
        DUK_D(DUK_DPRINT("uncaught error: type=%d iserror=%d value1=%!T value2=%!T",
                         (int) thr->heap->lj.type, (int) thr->heap->lj.iserror,
                         &thr->heap->lj.value1, &thr->heap->lj.value2));

#if defined(DUK_USE_PREFER_SIZE)
        duk__uncaught_minimal(thr);
#else
        duk__uncaught_error_aware(thr);
#endif
        DUK_UNREACHABLE();
    }

#if defined(DUK_USE_CPP_EXCEPTIONS)
    throw duk_internal_exception();  /* dummy */
#else
    DUK_LONGJMP(thr->heap->lj.jmpbuf_ptr->jb);
#endif

    DUK_UNREACHABLE();
}
//#line 1 "duk_error_misc.c"
/*
 *  Error helpers
 */

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

/*
 *  Helper to walk the thread chain and see if there is an active error
 *  catcher.  Protected calls or finally blocks aren't considered catching.
 */

#if defined(DUK_USE_DEBUGGER_SUPPORT)
DUK_LOCAL duk_bool_t duk__have_active_catcher(duk_hthread *thr) {
    /* As noted above, a protected API call won't be counted as a
     * catcher.  This is usually convenient, e.g. in the case of a top-
     * level duk_pcall(), but may not always be desirable.  Perhaps add
     * an argument to treat them as catchers?
     */

    duk_activation *act;
    duk_catcher *cat;

    DUK_ASSERT(thr != NULL);

    for (; thr != NULL; thr = thr->resumer) {
        for (act = thr->callstack_curr; act != NULL; act = act->parent) {
            for (cat = act->cat; cat != NULL; cat = cat->parent) {
                if (DUK_CAT_HAS_CATCH_ENABLED(cat)) {
                    return 1;  /* all we need to know */
                }
            }
        }
    }
    return 0;
}
#endif  /* DUK_USE_DEBUGGER_SUPPORT */

/*
 *  Get prototype object for an integer error code.
 */

DUK_INTERNAL duk_hobject *duk_error_prototype_from_code(duk_hthread *thr, duk_errcode_t code) {
    switch (code) {
    case DUK_ERR_EVAL_ERROR:
        return thr->builtins[DUK_BIDX_EVAL_ERROR_PROTOTYPE];
    case DUK_ERR_RANGE_ERROR:
        return thr->builtins[DUK_BIDX_RANGE_ERROR_PROTOTYPE];
    case DUK_ERR_REFERENCE_ERROR:
        return thr->builtins[DUK_BIDX_REFERENCE_ERROR_PROTOTYPE];
    case DUK_ERR_SYNTAX_ERROR:
        return thr->builtins[DUK_BIDX_SYNTAX_ERROR_PROTOTYPE];
    case DUK_ERR_TYPE_ERROR:
        return thr->builtins[DUK_BIDX_TYPE_ERROR_PROTOTYPE];
    case DUK_ERR_URI_ERROR:
        return thr->builtins[DUK_BIDX_URI_ERROR_PROTOTYPE];
    case DUK_ERR_ERROR:
    default:
        return thr->builtins[DUK_BIDX_ERROR_PROTOTYPE];
    }
}

/*
 *  Helper for debugger throw notify and pause-on-uncaught integration.
 */

#if defined(DUK_USE_DEBUGGER_SUPPORT)
DUK_INTERNAL void duk_err_check_debugger_integration(duk_hthread *thr) {
    duk_bool_t uncaught;
    duk_tval *tv_obj;

    /* If something is thrown with the debugger attached and nobody will
     * catch it, execution is paused before the longjmp, turning over
     * control to the debug client.  This allows local state to be examined
     * before the stack is unwound.  Errors are not intercepted when debug
     * message loop is active (e.g. for Eval).
     */

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);

    /* XXX: Allow customizing the pause and notify behavior at runtime
     * using debugger runtime flags.  For now the behavior is fixed using
     * config options.
     */

    if (!duk_debug_is_attached(thr->heap) ||
        thr->heap->dbg_processing ||
        thr->heap->lj.type != DUK_LJ_TYPE_THROW ||
        thr->heap->creating_error) {
        DUK_D(DUK_DPRINT("skip debugger error integration; not attached, debugger processing, not THROW, or error thrown while creating error"));
        return;
    }

    /* Don't intercept a DoubleError, we may have caused the initial double
     * fault and attempting to intercept it will cause us to be called
     * recursively and exhaust the C stack.  (This should no longer happen
     * for the initial throw because DoubleError path doesn't do a debugger
     * integration check, but it might happen for rethrows.)
     */
    tv_obj = &thr->heap->lj.value1;
    if (DUK_TVAL_IS_OBJECT(tv_obj) && DUK_TVAL_GET_OBJECT(tv_obj) == thr->builtins[DUK_BIDX_DOUBLE_ERROR]) {
        DUK_D(DUK_DPRINT("built-in DoubleError instance (re)thrown, not intercepting"));
        return;
    }

    uncaught = !duk__have_active_catcher(thr);

    /* Debugger code expects the value at stack top.  This also serves
     * as a backup: we need to store/restore the longjmp state because
     * when the debugger is paused Eval commands may be executed and
     * they can arbitrarily clobber the longjmp state.
     */
    duk_push_tval(thr, tv_obj);

    /* Store and reset longjmp state. */
    DUK_ASSERT_LJSTATE_SET(thr->heap);
    DUK_TVAL_DECREF_NORZ(thr, tv_obj);
    DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2));  /* Always for THROW type. */
    DUK_TVAL_SET_UNDEFINED(tv_obj);
    thr->heap->lj.type = DUK_LJ_TYPE_UNKNOWN;
    DUK_ASSERT_LJSTATE_UNSET(thr->heap);

#if defined(DUK_USE_DEBUGGER_THROW_NOTIFY)
    /* Report it to the debug client */
    DUK_D(DUK_DPRINT("throw with debugger attached, report to client"));
    duk_debug_send_throw(thr, uncaught);
#endif

    if (uncaught) {
        if (thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_UNCAUGHT_ERROR) {
            DUK_D(DUK_DPRINT("PAUSE TRIGGERED by uncaught error"));
            duk_debug_halt_execution(thr, 1 /*use_prev_pc*/);
        }
    } else {
        if (thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_CAUGHT_ERROR) {
            DUK_D(DUK_DPRINT("PAUSE TRIGGERED by caught error"));
            duk_debug_halt_execution(thr, 1 /*use_prev_pc*/);
        }
    }

    /* Restore longjmp state. */
    DUK_ASSERT_LJSTATE_UNSET(thr->heap);
    thr->heap->lj.type = DUK_LJ_TYPE_THROW;
    tv_obj = DUK_GET_TVAL_NEGIDX(thr, -1);
    DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value1));
    DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2));
    DUK_TVAL_SET_TVAL(&thr->heap->lj.value1, tv_obj);
    DUK_TVAL_INCREF(thr, tv_obj);
    DUK_ASSERT_LJSTATE_SET(thr->heap);

    duk_pop(thr);
}
#endif  /* DUK_USE_DEBUGGER_SUPPORT */

/*
 *  Helpers for setting up heap longjmp state.
 */

DUK_INTERNAL void duk_err_setup_ljstate1(duk_hthread *thr, duk_small_uint_t lj_type, duk_tval *tv_val) {
    duk_heap *heap;

    DUK_ASSERT(thr != NULL);
    heap = thr->heap;
    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(tv_val != NULL);

    DUK_ASSERT_LJSTATE_UNSET(heap);

    heap->lj.type = lj_type;
    DUK_TVAL_SET_TVAL(&heap->lj.value1, tv_val);
    DUK_TVAL_INCREF(thr, tv_val);

    DUK_ASSERT_LJSTATE_SET(heap);
}
//#line 1 "duk_error_throw.c"
/*
 *  Create and throw an ECMAScript error object based on a code and a message.
 *
 *  Used when we throw errors internally.  ECMAScript generated error objects
 *  are created by ECMAScript code, and the throwing is handled by the bytecode
 *  executor.
 */

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

/*
 *  Create and throw an error (originating from Duktape internally)
 *
 *  Push an error object on top of the stack, possibly throw augmenting
 *  the error, and finally longjmp.
 *
 *  If an error occurs while we're dealing with the current error, we might
 *  enter an infinite recursion loop.  This is prevented by detecting a
 *  "double fault" through the heap->creating_error flag; the recursion
 *  then stops at the second level.
 */

#if defined(DUK_USE_VERBOSE_ERRORS)
DUK_INTERNAL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code, const char *msg, const char *filename, duk_int_t line) {
#else
DUK_INTERNAL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code) {
#endif
#if defined(DUK_USE_VERBOSE_ERRORS)
    DUK_DD(DUK_DDPRINT("duk_err_create_and_throw(): code=%ld, msg=%s, filename=%s, line=%ld",
                       (long) code, (const char *) msg,
                       (const char *) filename, (long) line));
#else
    DUK_DD(DUK_DDPRINT("duk_err_create_and_throw(): code=%ld", (long) code));
#endif

    DUK_ASSERT(thr != NULL);

    /* Even though nested call is possible because we throw an error when
     * trying to create an error, the potential errors must happen before
     * the longjmp state is configured.
     */
    DUK_ASSERT_LJSTATE_UNSET(thr->heap);

    /* Sync so that augmentation sees up-to-date activations, NULL
     * thr->ptr_curr_pc so that it's not used if side effects occur
     * in augmentation or longjmp handling.
     */
    duk_hthread_sync_and_null_currpc(thr);

    /*
     *  Create and push an error object onto the top of stack.
     *  The error is potentially augmented before throwing.
     *
     *  If a "double error" occurs, use a fixed error instance
     *  to avoid further trouble.
     */

    if (thr->heap->creating_error) {
        duk_tval tv_val;
        duk_hobject *h_err;

        thr->heap->creating_error = 0;

        h_err = thr->builtins[DUK_BIDX_DOUBLE_ERROR];
        if (h_err != NULL) {
            DUK_D(DUK_DPRINT("double fault detected -> use built-in fixed 'double error' instance"));
            DUK_TVAL_SET_OBJECT(&tv_val, h_err);
        } else {
            DUK_D(DUK_DPRINT("double fault detected; there is no built-in fixed 'double error' instance "
                             "-> use the error code as a number"));
            DUK_TVAL_SET_I32(&tv_val, (duk_int32_t) code);
        }

        duk_err_setup_ljstate1(thr, DUK_LJ_TYPE_THROW, &tv_val);

        /* No augmentation to avoid any allocations or side effects. */
    } else {
        /* Prevent infinite recursion.  Extra call stack and C
         * recursion headroom (see GH-191) is added for augmentation.
         * That is now signalled by heap->augmenting error and taken
         * into account in call handling without an explicit limit bump.
         */
        thr->heap->creating_error = 1;

        duk_require_stack(thr, 1);

        /* XXX: usually unnecessary '%s' formatting here, but cannot
         * use 'msg' as a format string directly.
         */
#if defined(DUK_USE_VERBOSE_ERRORS)
        duk_push_error_object_raw(thr,
                                  code | DUK_ERRCODE_FLAG_NOBLAME_FILELINE,
                                  filename,
                                  line,
                                  "%s",
                                  (const char *) msg);
#else
        duk_push_error_object_raw(thr,
                                  code | DUK_ERRCODE_FLAG_NOBLAME_FILELINE,
                                  NULL,
                                  0,
                                  NULL);
#endif

        /* Note that an alloc error may happen during error augmentation.
         * This may happen both when the original error is an alloc error
         * and when it's something else.  Because any error in augmentation
         * must be handled correctly anyway, there's no special check for
         * avoiding it for alloc errors (this differs from Duktape 1.x).
         */
#if defined(DUK_USE_AUGMENT_ERROR_THROW)
        DUK_DDD(DUK_DDDPRINT("THROW ERROR (INTERNAL): %!iT (before throw augment)",
                             (duk_tval *) duk_get_tval(thr, -1)));
        duk_err_augment_error_throw(thr);
#endif

        duk_err_setup_ljstate1(thr, DUK_LJ_TYPE_THROW, DUK_GET_TVAL_NEGIDX(thr, -1));
        thr->heap->creating_error = 0;

        /* Error is now created and we assume no errors can occur any
         * more.  Check for debugger Throw integration only when the
         * error is complete.  If we enter debugger message loop,
         * creating_error must be 0 so that errors can be thrown in
         * the paused state, e.g. in Eval commands.
         */
#if defined(DUK_USE_DEBUGGER_SUPPORT)
        duk_err_check_debugger_integration(thr);
#endif
    }

    /*
     *  Finally, longjmp
     */

    DUK_DDD(DUK_DDDPRINT("THROW ERROR (INTERNAL): %!iT, %!iT (after throw augment)",
                         (duk_tval *) &thr->heap->lj.value1, (duk_tval *) &thr->heap->lj.value2));

    duk_err_longjmp(thr);
    DUK_UNREACHABLE();
}

/*
 *  Helper for C function call negative return values.
 */

DUK_INTERNAL void duk_error_throw_from_negative_rc(duk_hthread *thr, duk_ret_t rc) {
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(rc < 0);

    /*
     *  The __FILE__ and __LINE__ information is intentionally not used in the
     *  creation of the error object, as it isn't useful in the tracedata.  The
     *  tracedata still contains the function which returned the negative return
     *  code, and having the file/line of this function isn't very useful.
     *
     *  The error messages for DUK_RET_xxx shorthand are intentionally very
     *  minimal: they're only really useful for low memory targets.
     */

    duk_error_raw(thr, -rc, NULL, 0, "error (rc %ld)", (long) rc);
    DUK_WO_NORETURN(return;);
}
//#line 1 "duk_hbuffer_alloc.c"
/*
 *  duk_hbuffer allocation and freeing.
 */

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

/* Allocate a new duk_hbuffer of a certain type and return a pointer to it
 * (NULL on error).  Write buffer data pointer to 'out_bufdata' (only if
 * allocation successful).
 */
DUK_INTERNAL duk_hbuffer *duk_hbuffer_alloc(duk_heap *heap, duk_size_t size, duk_small_uint_t flags, void **out_bufdata) {
    duk_hbuffer *res = NULL;
    duk_size_t header_size;
    duk_size_t alloc_size;

    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(out_bufdata != NULL);

    DUK_DDD(DUK_DDDPRINT("allocate hbuffer"));

    /* Size sanity check.  Should not be necessary because caller is
     * required to check this, but we don't want to cause a segfault
     * if the size wraps either in duk_size_t computation or when
     * storing the size in a 16-bit field.
     */
    if (size > DUK_HBUFFER_MAX_BYTELEN) {
        DUK_D(DUK_DPRINT("hbuffer alloc failed: size too large: %ld", (long) size));
        return NULL;  /* no need to write 'out_bufdata' */
    }

    if (flags & DUK_BUF_FLAG_EXTERNAL) {
        header_size = sizeof(duk_hbuffer_external);
        alloc_size = sizeof(duk_hbuffer_external);
    } else if (flags & DUK_BUF_FLAG_DYNAMIC) {
        header_size = sizeof(duk_hbuffer_dynamic);
        alloc_size = sizeof(duk_hbuffer_dynamic);
    } else {
        header_size = sizeof(duk_hbuffer_fixed);
        alloc_size = sizeof(duk_hbuffer_fixed) + size;
        DUK_ASSERT(alloc_size >= sizeof(duk_hbuffer_fixed));  /* no wrapping */
    }

    res = (duk_hbuffer *) DUK_ALLOC(heap, alloc_size);
    if (DUK_UNLIKELY(res == NULL)) {
        goto alloc_error;
    }

    /* zero everything unless requested not to do so */
#if defined(DUK_USE_ZERO_BUFFER_DATA)
    duk_memzero((void *) res,
                (flags & DUK_BUF_FLAG_NOZERO) ? header_size : alloc_size);
#else
    duk_memzero((void *) res, header_size);
#endif

    if (flags & DUK_BUF_FLAG_EXTERNAL) {
        duk_hbuffer_external *h;
        h = (duk_hbuffer_external *) res;
        DUK_UNREF(h);
        *out_bufdata = NULL;
#if defined(DUK_USE_EXPLICIT_NULL_INIT)
#if defined(DUK_USE_HEAPPTR16)
/* the compressed pointer is zeroed which maps to NULL, so nothing to do. */
#else
        DUK_HBUFFER_EXTERNAL_SET_DATA_PTR(heap, h, NULL);
#endif
#endif
        DUK_ASSERT(DUK_HBUFFER_EXTERNAL_GET_DATA_PTR(heap, h) == NULL);
    } else if (flags & DUK_BUF_FLAG_DYNAMIC) {
        duk_hbuffer_dynamic *h = (duk_hbuffer_dynamic *) res;
        void *ptr;

        if (size > 0) {
            DUK_ASSERT(!(flags & DUK_BUF_FLAG_EXTERNAL));  /* alloc external with size zero */
            DUK_DDD(DUK_DDDPRINT("dynamic buffer with nonzero size, alloc actual buffer"));
#if defined(DUK_USE_ZERO_BUFFER_DATA)
            ptr = DUK_ALLOC_ZEROED(heap, size);
#else
            ptr = DUK_ALLOC(heap, size);
#endif
            if (DUK_UNLIKELY(ptr == NULL)) {
                /* Because size > 0, NULL check is correct */
                goto alloc_error;
            }
            *out_bufdata = ptr;

            DUK_HBUFFER_DYNAMIC_SET_DATA_PTR(heap, h, ptr);
        } else {
            *out_bufdata = NULL;
#if defined(DUK_USE_EXPLICIT_NULL_INIT)
#if defined(DUK_USE_HEAPPTR16)
/* the compressed pointer is zeroed which maps to NULL, so nothing to do. */
#else
            DUK_HBUFFER_DYNAMIC_SET_DATA_PTR(heap, h, NULL);
#endif
#endif
            DUK_ASSERT(DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap, h) == NULL);
        }
    } else {
        *out_bufdata = (void *) ((duk_hbuffer_fixed *) (void *) res + 1);
    }

    DUK_HBUFFER_SET_SIZE(res, size);

    DUK_HEAPHDR_SET_TYPE(&res->hdr, DUK_HTYPE_BUFFER);
    if (flags & DUK_BUF_FLAG_DYNAMIC) {
        DUK_HBUFFER_SET_DYNAMIC(res);
        if (flags & DUK_BUF_FLAG_EXTERNAL) {
            DUK_HBUFFER_SET_EXTERNAL(res);
        }
    } else {
        DUK_ASSERT(!(flags & DUK_BUF_FLAG_EXTERNAL));
    }
        DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap, &res->hdr);

    DUK_DDD(DUK_DDDPRINT("allocated hbuffer: %p", (void *) res));
    return res;

 alloc_error:
    DUK_DD(DUK_DDPRINT("hbuffer allocation failed"));

    DUK_FREE(heap, res);
    return NULL;  /* no need to write 'out_bufdata' */
}

/* For indirect allocs. */

DUK_INTERNAL void *duk_hbuffer_get_dynalloc_ptr(duk_heap *heap, void *ud) {
    duk_hbuffer_dynamic *buf = (duk_hbuffer_dynamic *) ud;
    DUK_UNREF(heap);
    return (void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap, buf);
}
//#line 1 "duk_hbuffer_assert.c"
/*
 *  duk_hbuffer assertion helpers
 */

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

#if defined(DUK_USE_ASSERTIONS)

DUK_INTERNAL void duk_hbuffer_assert_valid(duk_hbuffer *h) {
    DUK_ASSERT(h != NULL);
}

#endif  /* DUK_USE_ASSERTIONS */
//#line 1 "duk_hbuffer_ops.c"
/*
 *  duk_hbuffer operations such as resizing and inserting/appending data to
 *  a dynamic buffer.
 */

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

/*
 *  Resizing
 */

DUK_INTERNAL void duk_hbuffer_resize(duk_hthread *thr, duk_hbuffer_dynamic *buf, duk_size_t new_size) {
    void *res;
    duk_size_t prev_size;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(buf != NULL);
    DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf));
    DUK_ASSERT(!DUK_HBUFFER_HAS_EXTERNAL(buf));

    /*
     *  Maximum size check
     */

    if (new_size > DUK_HBUFFER_MAX_BYTELEN) {
        DUK_ERROR_RANGE(thr, "buffer too long");
        DUK_WO_NORETURN(return;);
    }

    /*
     *  Note: use indirect realloc variant just in case mark-and-sweep
     *  (finalizers) might resize this same buffer during garbage
     *  collection.
     */

    res = DUK_REALLOC_INDIRECT(thr->heap, duk_hbuffer_get_dynalloc_ptr, (void *) buf, new_size);
    if (DUK_LIKELY(res != NULL || new_size == 0)) {
        /* 'res' may be NULL if new allocation size is 0. */

        DUK_DDD(DUK_DDDPRINT("resized dynamic buffer %p:%ld -> %p:%ld",
                             (void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, buf),
                             (long) DUK_HBUFFER_DYNAMIC_GET_SIZE(buf),
                             (void *) res,
                             (long) new_size));

        /*
         *  The entire allocated buffer area, regardless of actual used
         *  size, is kept zeroed in resizes for simplicity.  If the buffer
         *  is grown, zero the new part.
         */

        prev_size = DUK_HBUFFER_DYNAMIC_GET_SIZE(buf);
        if (new_size > prev_size) {
            DUK_ASSERT(new_size - prev_size > 0);
#if defined(DUK_USE_ZERO_BUFFER_DATA)
            duk_memzero((void *) ((char *) res + prev_size),
                        (duk_size_t) (new_size - prev_size));
#endif
        }

        DUK_HBUFFER_DYNAMIC_SET_SIZE(buf, new_size);
        DUK_HBUFFER_DYNAMIC_SET_DATA_PTR(thr->heap, buf, res);
    } else {
        DUK_ERROR_ALLOC_FAILED(thr);
        DUK_WO_NORETURN(return;);
    }

    DUK_ASSERT(res != NULL || new_size == 0);
}

DUK_INTERNAL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic *buf) {
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(buf != NULL);
    DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf));
    DUK_ASSERT(!DUK_HBUFFER_HAS_EXTERNAL(buf));

    duk_hbuffer_resize(thr, buf, 0);
}
/* #include duk_internal.h -> already included */
//#line 2 "duk_hbufobj_misc.c"

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL duk_uint_t duk_hbufobj_clamp_bytelength(duk_hbufobj *h_bufobj, duk_uint_t len) {
    duk_uint_t buf_size;
    duk_uint_t buf_avail;

    DUK_ASSERT(h_bufobj != NULL);
    DUK_ASSERT(h_bufobj->buf != NULL);

    buf_size = (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_bufobj->buf);
    if (h_bufobj->offset > buf_size) {
        /* Slice starting point is beyond current length. */
        return 0;
    }
    buf_avail = buf_size - h_bufobj->offset;

    return buf_avail >= len ? len : buf_avail;
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
//#line 1 "duk_heap_alloc.c"
/*
 *  duk_heap allocation and freeing.
 */

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

#if defined(DUK_USE_ROM_STRINGS)
/* Fixed seed value used with ROM strings. */
#define DUK__FIXED_HASH_SEED       0xabcd1234
#endif

/*
 *  Free a heap object.
 *
 *  Free heap object and its internal (non-heap) pointers.  Assumes that
 *  caller has removed the object from heap allocated list or the string
 *  intern table, and any weak references (which strings may have) have
 *  been already dealt with.
 */

DUK_INTERNAL void duk_free_hobject(duk_heap *heap, duk_hobject *h) {
    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(h != NULL);

    DUK_FREE(heap, DUK_HOBJECT_GET_PROPS(heap, h));

    if (DUK_HOBJECT_IS_COMPFUNC(h)) {
        duk_hcompfunc *f = (duk_hcompfunc *) h;
        DUK_UNREF(f);
        /* Currently nothing to free; 'data' is a heap object */
    } else if (DUK_HOBJECT_IS_NATFUNC(h)) {
        duk_hnatfunc *f = (duk_hnatfunc *) h;
        DUK_UNREF(f);
        /* Currently nothing to free */
    } else if (DUK_HOBJECT_IS_THREAD(h)) {
        duk_hthread *t = (duk_hthread *) h;
        duk_activation *act;

        DUK_FREE(heap, t->valstack);

        /* Don't free h->resumer because it exists in the heap.
         * Callstack entries also contain function pointers which
         * are not freed for the same reason.  They are decref
         * finalized and the targets are freed if necessary based
         * on their refcount (or reachability).
         */
        for (act = t->callstack_curr; act != NULL;) {
            duk_activation *act_next;
            duk_catcher *cat;

            for (cat = act->cat; cat != NULL;) {
                duk_catcher *cat_next;

                cat_next = cat->parent;
                DUK_FREE(heap, (void *) cat);
                cat = cat_next;
            }

            act_next = act->parent;
            DUK_FREE(heap, (void *) act);
            act = act_next;
        }

        /* XXX: with 'caller' property the callstack would need
         * to be unwound to update the 'caller' properties of
         * functions in the callstack.
         */
    } else if (DUK_HOBJECT_IS_BOUNDFUNC(h)) {
        duk_hboundfunc *f = (duk_hboundfunc *) (void *) h;

        DUK_FREE(heap, f->args);
    }

    DUK_FREE(heap, (void *) h);
}

DUK_INTERNAL void duk_free_hbuffer(duk_heap *heap, duk_hbuffer *h) {
    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(h != NULL);

    if (DUK_HBUFFER_HAS_DYNAMIC(h) && !DUK_HBUFFER_HAS_EXTERNAL(h)) {
        duk_hbuffer_dynamic *g = (duk_hbuffer_dynamic *) h;
        DUK_DDD(DUK_DDDPRINT("free dynamic buffer %p", (void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap, g)));
        DUK_FREE(heap, DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap, g));
    }
    DUK_FREE(heap, (void *) h);
}

DUK_INTERNAL void duk_free_hstring(duk_heap *heap, duk_hstring *h) {
    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(h != NULL);

    DUK_UNREF(heap);
    DUK_UNREF(h);

#if defined(DUK_USE_HSTRING_EXTDATA) && defined(DUK_USE_EXTSTR_FREE)
    if (DUK_HSTRING_HAS_EXTDATA(h)) {
        DUK_DDD(DUK_DDDPRINT("free extstr: hstring %!O, extdata: %p",
                             h, DUK_HSTRING_GET_EXTDATA((duk_hstring_external *) h)));
        DUK_USE_EXTSTR_FREE(heap->heap_udata, (const void *) DUK_HSTRING_GET_EXTDATA((duk_hstring_external *) h));
    }
#endif
    DUK_FREE(heap, (void *) h);
}

DUK_INTERNAL void duk_heap_free_heaphdr_raw(duk_heap *heap, duk_heaphdr *hdr) {
    DUK_ASSERT(heap);
    DUK_ASSERT(hdr);

    DUK_DDD(DUK_DDDPRINT("free heaphdr %p, htype %ld", (void *) hdr, (long) DUK_HEAPHDR_GET_TYPE(hdr)));

    switch (DUK_HEAPHDR_GET_TYPE(hdr)) {
    case DUK_HTYPE_STRING:
        duk_free_hstring(heap, (duk_hstring *) hdr);
        break;
    case DUK_HTYPE_OBJECT:
        duk_free_hobject(heap, (duk_hobject *) hdr);
        break;
    default:
        DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(hdr) == DUK_HTYPE_BUFFER);
        duk_free_hbuffer(heap, (duk_hbuffer *) hdr);
    }

}

/*
 *  Free the heap.
 *
 *  Frees heap-related non-heap-tracked allocations such as the
 *  string intern table; then frees the heap allocated objects;
 *  and finally frees the heap structure itself.  Reference counts
 *  and GC markers are ignored (and not updated) in this process,
 *  and finalizers won't be called.
 *
 *  The heap pointer and heap object pointers must not be used
 *  after this call.
 */

#if defined(DUK_USE_CACHE_ACTIVATION)
DUK_LOCAL duk_size_t duk__heap_free_activation_freelist(duk_heap *heap) {
    duk_activation *act;
    duk_activation *act_next;
    duk_size_t count_act = 0;

    for (act = heap->activation_free; act != NULL;) {
        act_next = act->parent;
        DUK_FREE(heap, (void *) act);
        act = act_next;
#if defined(DUK_USE_DEBUG)
        count_act++;
#endif
    }
    heap->activation_free = NULL;  /* needed when called from mark-and-sweep */
    return count_act;
}
#endif  /* DUK_USE_CACHE_ACTIVATION */

#if defined(DUK_USE_CACHE_CATCHER)
DUK_LOCAL duk_size_t duk__heap_free_catcher_freelist(duk_heap *heap) {
    duk_catcher *cat;
    duk_catcher *cat_next;
    duk_size_t count_cat = 0;

    for (cat = heap->catcher_free; cat != NULL;) {
        cat_next = cat->parent;
        DUK_FREE(heap, (void *) cat);
        cat = cat_next;
#if defined(DUK_USE_DEBUG)
        count_cat++;
#endif
    }
    heap->catcher_free = NULL;  /* needed when called from mark-and-sweep */

    return count_cat;
}
#endif  /* DUK_USE_CACHE_CATCHER */

DUK_INTERNAL void duk_heap_free_freelists(duk_heap *heap) {
    duk_size_t count_act = 0;
    duk_size_t count_cat = 0;

#if defined(DUK_USE_CACHE_ACTIVATION)
    count_act = duk__heap_free_activation_freelist(heap);
#endif
#if defined(DUK_USE_CACHE_CATCHER)
    count_cat = duk__heap_free_catcher_freelist(heap);
#endif
    DUK_UNREF(heap);
    DUK_UNREF(count_act);
    DUK_UNREF(count_cat);

    DUK_D(DUK_DPRINT("freed %ld activation freelist entries, %ld catcher freelist entries",
                     (long) count_act, (long) count_cat));
}

DUK_LOCAL void duk__free_allocated(duk_heap *heap) {
    duk_heaphdr *curr;
    duk_heaphdr *next;

    curr = heap->heap_allocated;
    while (curr) {
        /* We don't log or warn about freeing zero refcount objects
         * because they may happen with finalizer processing.
         */

        DUK_DDD(DUK_DDDPRINT("FINALFREE (allocated): %!iO",
                             (duk_heaphdr *) curr));
        next = DUK_HEAPHDR_GET_NEXT(heap, curr);
        duk_heap_free_heaphdr_raw(heap, curr);
        curr = next;
    }
}

#if defined(DUK_USE_FINALIZER_SUPPORT)
DUK_LOCAL void duk__free_finalize_list(duk_heap *heap) {
    duk_heaphdr *curr;
    duk_heaphdr *next;

    curr = heap->finalize_list;
    while (curr) {
        DUK_DDD(DUK_DDDPRINT("FINALFREE (finalize_list): %!iO",
                             (duk_heaphdr *) curr));
        next = DUK_HEAPHDR_GET_NEXT(heap, curr);
        duk_heap_free_heaphdr_raw(heap, curr);
        curr = next;
    }
}
#endif  /* DUK_USE_FINALIZER_SUPPORT */

DUK_LOCAL void duk__free_stringtable(duk_heap *heap) {
    /* strings are only tracked by stringtable */
    duk_heap_strtable_free(heap);
}

#if defined(DUK_USE_FINALIZER_SUPPORT)
DUK_LOCAL void duk__free_run_finalizers(duk_heap *heap) {
    duk_heaphdr *curr;
    duk_uint_t round_no;
    duk_size_t count_all;
    duk_size_t count_finalized;
    duk_size_t curr_limit;

    DUK_ASSERT(heap != NULL);

#if defined(DUK_USE_REFERENCE_COUNTING)
    DUK_ASSERT(heap->refzero_list == NULL);  /* refzero not running -> must be empty */
#endif
    DUK_ASSERT(heap->finalize_list == NULL);  /* mark-and-sweep last pass */

    if (heap->heap_thread == NULL) {
        /* May happen when heap allocation fails right off.  There
         * cannot be any finalizable objects in this case.
         */
        DUK_D(DUK_DPRINT("no heap_thread in heap destruct, assume no finalizable objects"));
        return;
    }

    /* Prevent finalize_list processing and mark-and-sweep entirely.
     * Setting ms_running != 0 also prevents refzero handling from moving
     * objects away from the heap_allocated list.  The flag name is a bit
     * misleading here.
     *
     * Use a distinct value for ms_running here (== 2) so that assertions
     * can detect this situation separate from the normal runtime
     * mark-and-sweep case.  This allows better assertions (GH-2030).
     */
    DUK_ASSERT(heap->pf_prevent_count == 0);
    DUK_ASSERT(heap->ms_running == 0);
    DUK_ASSERT(heap->ms_prevent_count == 0);
    heap->pf_prevent_count = 1;
    heap->ms_running = 2;  /* Use distinguishable value. */
    heap->ms_prevent_count = 1;  /* Bump, because mark-and-sweep assumes it's bumped when ms_running is set. */

    curr_limit = 0;  /* suppress warning, not used */
    for (round_no = 0; ; round_no++) {
        curr = heap->heap_allocated;
        count_all = 0;
        count_finalized = 0;
        while (curr) {
            count_all++;
            if (DUK_HEAPHDR_IS_OBJECT(curr)) {
                /* Only objects in heap_allocated may have finalizers.  Check that
                 * the object itself has a _Finalizer property (own or inherited)
                 * so that we don't execute finalizers for e.g. Proxy objects.
                 */
                DUK_ASSERT(curr != NULL);

                if (DUK_HOBJECT_HAS_FINALIZER_FAST(heap, (duk_hobject *) curr)) {
                    if (!DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) curr)) {
                        DUK_ASSERT(DUK_HEAP_HAS_FINALIZER_NORESCUE(heap));  /* maps to finalizer 2nd argument */
                        duk_heap_run_finalizer(heap, (duk_hobject *) curr);
                        count_finalized++;
                    }
                }
            }
            curr = DUK_HEAPHDR_GET_NEXT(heap, curr);
        }

        /* Each round of finalizer execution may spawn new finalizable objects
         * which is normal behavior for some applications.  Allow multiple
         * rounds of finalization, but use a shrinking limit based on the
         * first round to detect the case where a runaway finalizer creates
         * an unbounded amount of new finalizable objects.  Finalizer rescue
         * is not supported: the semantics are unclear because most of the
         * objects being finalized here are already reachable.  The finalizer
         * is given a boolean to indicate that rescue is not possible.
         *
         * See discussion in: https://github.com/svaarala/duktape/pull/473
         */

        if (round_no == 0) {
            /* Cannot wrap: each object is at least 8 bytes so count is
             * at most 1/8 of that.
             */
            curr_limit = count_all * 2;
        } else {
            curr_limit = (curr_limit * 3) / 4;   /* Decrease by 25% every round */
        }
        DUK_D(DUK_DPRINT("finalizer round %ld complete, %ld objects, tried to execute %ld finalizers, current limit is %ld",
                         (long) round_no, (long) count_all, (long) count_finalized, (long) curr_limit));

        if (count_finalized == 0) {
            DUK_D(DUK_DPRINT("no more finalizable objects, forced finalization finished"));
            break;
        }
        if (count_finalized >= curr_limit) {
            DUK_D(DUK_DPRINT("finalizer count above limit, potentially runaway finalizer; skip remaining finalizers"));
            break;
        }
    }

    DUK_ASSERT(heap->ms_running == 2);
    DUK_ASSERT(heap->pf_prevent_count == 1);
    heap->ms_running = 0;
    heap->pf_prevent_count = 0;
}
#endif  /* DUK_USE_FINALIZER_SUPPORT */

DUK_INTERNAL void duk_heap_free(duk_heap *heap) {
    DUK_D(DUK_DPRINT("free heap: %p", (void *) heap));

#if defined(DUK_USE_DEBUG)
    duk_heap_strtable_dump(heap);
#endif

#if defined(DUK_USE_DEBUGGER_SUPPORT)
    /* Detach a debugger if attached (can be called multiple times)
     * safely.
     */
    /* XXX: Add a flag to reject an attempt to re-attach?  Otherwise
     * the detached callback may immediately reattach.
     */
    duk_debug_do_detach(heap);
#endif

    /* Execute finalizers before freeing the heap, even for reachable
     * objects.  This gives finalizers the chance to free any native
     * resources like file handles, allocations made outside Duktape,
     * etc.  This is quite tricky to get right, so that all finalizer
     * guarantees are honored.
     *
     * Run mark-and-sweep a few times just in case (unreachable object
     * finalizers run already here).  The last round must rescue objects
     * from the previous round without running any more finalizers.  This
     * ensures rescued objects get their FINALIZED flag cleared so that
     * their finalizer is called once more in forced finalization to
     * satisfy finalizer guarantees.  However, we don't want to run any
     * more finalizers because that'd required one more loop, and so on.
     *
     * XXX: this perhaps requires an execution time limit.
     */
    DUK_D(DUK_DPRINT("execute finalizers before freeing heap"));
    DUK_ASSERT(heap->pf_skip_finalizers == 0);
    DUK_D(DUK_DPRINT("forced gc #1 in heap destruction"));
    duk_heap_mark_and_sweep(heap, 0);
    DUK_D(DUK_DPRINT("forced gc #2 in heap destruction"));
    duk_heap_mark_and_sweep(heap, 0);
    DUK_D(DUK_DPRINT("forced gc #3 in heap destruction (don't run finalizers)"));
    heap->pf_skip_finalizers = 1;
    duk_heap_mark_and_sweep(heap, 0);  /* Skip finalizers; queue finalizable objects to heap_allocated. */

    /* There are never objects in refzero_list at this point, or at any
     * point beyond a DECREF (even a DECREF_NORZ).  Since Duktape 2.1
     * refzero_list processing is side effect free, so it is always
     * processed to completion by a DECREF initially triggering a zero
     * refcount.
     */
#if defined(DUK_USE_REFERENCE_COUNTING)
    DUK_ASSERT(heap->refzero_list == NULL);  /* Always processed to completion inline. */
#endif
#if defined(DUK_USE_FINALIZER_SUPPORT)
    DUK_ASSERT(heap->finalize_list == NULL);  /* Last mark-and-sweep with skip_finalizers. */
#endif

#if defined(DUK_USE_FINALIZER_SUPPORT)
    DUK_D(DUK_DPRINT("run finalizers for remaining finalizable objects"));
    DUK_HEAP_SET_FINALIZER_NORESCUE(heap);  /* Rescue no longer supported. */
    duk__free_run_finalizers(heap);
#endif  /* DUK_USE_FINALIZER_SUPPORT */

    /* Note: heap->heap_thread, heap->curr_thread, and heap->heap_object
     * are on the heap allocated list.
     */

    DUK_D(DUK_DPRINT("freeing temporary freelists"));
    duk_heap_free_freelists(heap);

    DUK_D(DUK_DPRINT("freeing heap_allocated of heap: %p", (void *) heap));
    duk__free_allocated(heap);

#if defined(DUK_USE_REFERENCE_COUNTING)
    DUK_ASSERT(heap->refzero_list == NULL);  /* Always processed to completion inline. */
#endif

#if defined(DUK_USE_FINALIZER_SUPPORT)
    DUK_D(DUK_DPRINT("freeing finalize_list of heap: %p", (void *) heap));
    duk__free_finalize_list(heap);
#endif

    DUK_D(DUK_DPRINT("freeing string table of heap: %p", (void *) heap));
    duk__free_stringtable(heap);

    DUK_D(DUK_DPRINT("freeing heap structure: %p", (void *) heap));
    heap->free_func(heap->heap_udata, heap);
}

/*
 *  Allocate a heap.
 *
 *  String table is initialized with built-in strings from genbuiltins.py,
 *  either by dynamically creating the strings or by referring to ROM strings.
 */

#if defined(DUK_USE_ROM_STRINGS)
DUK_LOCAL duk_bool_t duk__init_heap_strings(duk_heap *heap) {
#if defined(DUK_USE_ASSERTIONS)
    duk_small_uint_t i;
#endif

    DUK_UNREF(heap);

    /* With ROM-based strings, heap->strs[] and thr->strs[] are omitted
     * so nothing to initialize for strs[].
     */

#if defined(DUK_USE_ASSERTIONS)
    for (i = 0; i < sizeof(duk_rom_strings_lookup) / sizeof(const duk_hstring *); i++) {
        const duk_hstring *h;
        duk_uint32_t hash;

        h = duk_rom_strings_lookup[i];
        while (h != NULL) {
            hash = duk_heap_hashstring(heap, (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h));
            DUK_DD(DUK_DDPRINT("duk_rom_strings_lookup[%d] -> hash 0x%08lx, computed 0x%08lx",
                               (int) i, (unsigned long) DUK_HSTRING_GET_HASH(h), (unsigned long) hash));
            DUK_ASSERT(hash == (duk_uint32_t) DUK_HSTRING_GET_HASH(h));

            h = (const duk_hstring *) h->hdr.h_next;
        }
    }
#endif
    return 1;
}
#else  /* DUK_USE_ROM_STRINGS */

DUK_LOCAL duk_bool_t duk__init_heap_strings(duk_heap *heap) {
    duk_bitdecoder_ctx bd_ctx;
    duk_bitdecoder_ctx *bd = &bd_ctx;  /* convenience */
    duk_small_uint_t i;

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

    for (i = 0; i < DUK_HEAP_NUM_STRINGS; i++) {
        duk_uint8_t tmp[DUK_STRDATA_MAX_STRLEN];
        duk_small_uint_t len;
        duk_hstring *h;

        len = duk_bd_decode_bitpacked_string(bd, tmp);

        /* No need to length check string: it will never exceed even
         * the 16-bit length maximum.
         */
        DUK_ASSERT(len <= 0xffffUL);
        DUK_DDD(DUK_DDDPRINT("intern built-in string %ld", (long) i));
        h = duk_heap_strtable_intern(heap, tmp, len);
        if (!h) {
            goto failed;
        }
        DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) h));

        /* Special flags checks.  Since these strings are always
         * reachable and a string cannot appear twice in the string
         * table, there's no need to check/set these flags elsewhere.
         * The 'internal' flag is set by string intern code.
         */
        if (i == DUK_STRIDX_EVAL || i == DUK_STRIDX_LC_ARGUMENTS) {
            DUK_HSTRING_SET_EVAL_OR_ARGUMENTS(h);
        }
        if (i >= DUK_STRIDX_START_RESERVED && i < DUK_STRIDX_END_RESERVED) {
            DUK_HSTRING_SET_RESERVED_WORD(h);
            if (i >= DUK_STRIDX_START_STRICT_RESERVED) {
                DUK_HSTRING_SET_STRICT_RESERVED_WORD(h);
            }
        }

        DUK_DDD(DUK_DDDPRINT("interned: %!O", (duk_heaphdr *) h));

        /* XXX: The incref macro takes a thread pointer but doesn't
         * use it right now.
         */
        DUK_HSTRING_INCREF(_never_referenced_, h);

#if defined(DUK_USE_HEAPPTR16)
        heap->strs16[i] = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h);
#else
        heap->strs[i] = h;
#endif
    }

    return 1;

 failed:
    return 0;
}
#endif  /* DUK_USE_ROM_STRINGS */

DUK_LOCAL duk_bool_t duk__init_heap_thread(duk_heap *heap) {
    duk_hthread *thr;

    DUK_D(DUK_DPRINT("heap init: alloc heap thread"));
    thr = duk_hthread_alloc_unchecked(heap,
                                      DUK_HOBJECT_FLAG_EXTENSIBLE |
                                      DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_THREAD));
    if (thr == NULL) {
        DUK_D(DUK_DPRINT("failed to alloc heap_thread"));
        return 0;
    }
    thr->state = DUK_HTHREAD_STATE_INACTIVE;
#if defined(DUK_USE_ROM_STRINGS)
    /* No strs[] pointer. */
#else  /* DUK_USE_ROM_STRINGS */
#if defined(DUK_USE_HEAPPTR16)
    thr->strs16 = heap->strs16;
#else
    thr->strs = heap->strs;
#endif
#endif  /* DUK_USE_ROM_STRINGS */

    heap->heap_thread = thr;
    DUK_HTHREAD_INCREF(thr, thr);  /* Note: first argument not really used */

    /* 'thr' is now reachable */

    DUK_D(DUK_DPRINT("heap init: init heap thread stacks"));
    if (!duk_hthread_init_stacks(heap, thr)) {
        return 0;
    }

    /* XXX: this may now fail, and is not handled correctly */
    duk_hthread_create_builtin_objects(thr);

    /* default prototype */
    DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) thr, thr->builtins[DUK_BIDX_THREAD_PROTOTYPE]);

    return 1;
}

#if defined(DUK_USE_DEBUG)
#define DUK__DUMPSZ(t)  do { \
        DUK_D(DUK_DPRINT("" #t "=%ld", (long) sizeof(t))); \
    } while (0)

/* These is not 100% because format would need to be non-portable "long long".
 * Also print out as doubles to catch cases where the "long" type is not wide
 * enough; the limits will then not be printed accurately but the magnitude
 * will be correct.
 */
#define DUK__DUMPLM_SIGNED_RAW(t,a,b)  do { \
        DUK_D(DUK_DPRINT(t "=[%ld,%ld]=[%lf,%lf]", \
                         (long) (a), (long) (b), \
                         (double) (a), (double) (b))); \
    } while (0)
#define DUK__DUMPLM_UNSIGNED_RAW(t,a,b)  do { \
        DUK_D(DUK_DPRINT(t "=[%lu,%lu]=[%lf,%lf]", \
                         (unsigned long) (a), (unsigned long) (b), \
                         (double) (a), (double) (b))); \
    } while (0)
#define DUK__DUMPLM_SIGNED(t)  do { \
        DUK__DUMPLM_SIGNED_RAW("DUK_" #t "_{MIN,MAX}", DUK_##t##_MIN, DUK_##t##_MAX); \
    } while (0)
#define DUK__DUMPLM_UNSIGNED(t)  do { \
        DUK__DUMPLM_UNSIGNED_RAW("DUK_" #t "_{MIN,MAX}", DUK_##t##_MIN, DUK_##t##_MAX); \
    } while (0)

DUK_LOCAL void duk__dump_type_sizes(void) {
    DUK_D(DUK_DPRINT("sizeof()"));

    /* basic platform types */
    DUK__DUMPSZ(char);
    DUK__DUMPSZ(short);
    DUK__DUMPSZ(int);
    DUK__DUMPSZ(long);
    DUK__DUMPSZ(double);
    DUK__DUMPSZ(void *);
    DUK__DUMPSZ(size_t);

    /* basic types from duk_features.h */
    DUK__DUMPSZ(duk_uint8_t);
    DUK__DUMPSZ(duk_int8_t);
    DUK__DUMPSZ(duk_uint16_t);
    DUK__DUMPSZ(duk_int16_t);
    DUK__DUMPSZ(duk_uint32_t);
    DUK__DUMPSZ(duk_int32_t);
    DUK__DUMPSZ(duk_uint64_t);
    DUK__DUMPSZ(duk_int64_t);
    DUK__DUMPSZ(duk_uint_least8_t);
    DUK__DUMPSZ(duk_int_least8_t);
    DUK__DUMPSZ(duk_uint_least16_t);
    DUK__DUMPSZ(duk_int_least16_t);
    DUK__DUMPSZ(duk_uint_least32_t);
    DUK__DUMPSZ(duk_int_least32_t);
#if defined(DUK_USE_64BIT_OPS)
    DUK__DUMPSZ(duk_uint_least64_t);
    DUK__DUMPSZ(duk_int_least64_t);
#endif
    DUK__DUMPSZ(duk_uint_fast8_t);
    DUK__DUMPSZ(duk_int_fast8_t);
    DUK__DUMPSZ(duk_uint_fast16_t);
    DUK__DUMPSZ(duk_int_fast16_t);
    DUK__DUMPSZ(duk_uint_fast32_t);
    DUK__DUMPSZ(duk_int_fast32_t);
#if defined(DUK_USE_64BIT_OPS)
    DUK__DUMPSZ(duk_uint_fast64_t);
    DUK__DUMPSZ(duk_int_fast64_t);
#endif
    DUK__DUMPSZ(duk_uintptr_t);
    DUK__DUMPSZ(duk_intptr_t);
    DUK__DUMPSZ(duk_uintmax_t);
    DUK__DUMPSZ(duk_intmax_t);
    DUK__DUMPSZ(duk_double_t);

    /* important chosen base types */
    DUK__DUMPSZ(duk_int_t);
    DUK__DUMPSZ(duk_uint_t);
    DUK__DUMPSZ(duk_int_fast_t);
    DUK__DUMPSZ(duk_uint_fast_t);
    DUK__DUMPSZ(duk_small_int_t);
    DUK__DUMPSZ(duk_small_uint_t);
    DUK__DUMPSZ(duk_small_int_fast_t);
    DUK__DUMPSZ(duk_small_uint_fast_t);

    /* some derived types */
    DUK__DUMPSZ(duk_codepoint_t);
    DUK__DUMPSZ(duk_ucodepoint_t);
    DUK__DUMPSZ(duk_idx_t);
    DUK__DUMPSZ(duk_errcode_t);
    DUK__DUMPSZ(duk_uarridx_t);

    /* tval */
    DUK__DUMPSZ(duk_double_union);
    DUK__DUMPSZ(duk_tval);

    /* structs from duk_forwdecl.h */
    DUK__DUMPSZ(duk_jmpbuf);  /* just one 'int' for C++ exceptions */
    DUK__DUMPSZ(duk_heaphdr);
    DUK__DUMPSZ(duk_heaphdr_string);
    DUK__DUMPSZ(duk_hstring);
    DUK__DUMPSZ(duk_hstring_external);
    DUK__DUMPSZ(duk_hobject);
    DUK__DUMPSZ(duk_harray);
    DUK__DUMPSZ(duk_hcompfunc);
    DUK__DUMPSZ(duk_hnatfunc);
    DUK__DUMPSZ(duk_hdecenv);
    DUK__DUMPSZ(duk_hobjenv);
    DUK__DUMPSZ(duk_hthread);
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
    DUK__DUMPSZ(duk_hbufobj);
#endif
    DUK__DUMPSZ(duk_hproxy);
    DUK__DUMPSZ(duk_hbuffer);
    DUK__DUMPSZ(duk_hbuffer_fixed);
    DUK__DUMPSZ(duk_hbuffer_dynamic);
    DUK__DUMPSZ(duk_hbuffer_external);
    DUK__DUMPSZ(duk_propaccessor);
    DUK__DUMPSZ(duk_propvalue);
    DUK__DUMPSZ(duk_propdesc);
    DUK__DUMPSZ(duk_heap);
    DUK__DUMPSZ(duk_activation);
    DUK__DUMPSZ(duk_catcher);
    DUK__DUMPSZ(duk_strcache_entry);
    DUK__DUMPSZ(duk_litcache_entry);
    DUK__DUMPSZ(duk_ljstate);
    DUK__DUMPSZ(duk_fixedbuffer);
    DUK__DUMPSZ(duk_bitdecoder_ctx);
    DUK__DUMPSZ(duk_bitencoder_ctx);
    DUK__DUMPSZ(duk_token);
    DUK__DUMPSZ(duk_re_token);
    DUK__DUMPSZ(duk_lexer_point);
    DUK__DUMPSZ(duk_lexer_ctx);
    DUK__DUMPSZ(duk_compiler_instr);
    DUK__DUMPSZ(duk_compiler_func);
    DUK__DUMPSZ(duk_compiler_ctx);
    DUK__DUMPSZ(duk_re_matcher_ctx);
    DUK__DUMPSZ(duk_re_compiler_ctx);
}
DUK_LOCAL void duk__dump_type_limits(void) {
    DUK_D(DUK_DPRINT("limits"));

    /* basic types */
    DUK__DUMPLM_SIGNED(INT8);
    DUK__DUMPLM_UNSIGNED(UINT8);
    DUK__DUMPLM_SIGNED(INT_FAST8);
    DUK__DUMPLM_UNSIGNED(UINT_FAST8);
    DUK__DUMPLM_SIGNED(INT_LEAST8);
    DUK__DUMPLM_UNSIGNED(UINT_LEAST8);
    DUK__DUMPLM_SIGNED(INT16);
    DUK__DUMPLM_UNSIGNED(UINT16);
    DUK__DUMPLM_SIGNED(INT_FAST16);
    DUK__DUMPLM_UNSIGNED(UINT_FAST16);
    DUK__DUMPLM_SIGNED(INT_LEAST16);
    DUK__DUMPLM_UNSIGNED(UINT_LEAST16);
    DUK__DUMPLM_SIGNED(INT32);
    DUK__DUMPLM_UNSIGNED(UINT32);
    DUK__DUMPLM_SIGNED(INT_FAST32);
    DUK__DUMPLM_UNSIGNED(UINT_FAST32);
    DUK__DUMPLM_SIGNED(INT_LEAST32);
    DUK__DUMPLM_UNSIGNED(UINT_LEAST32);
#if defined(DUK_USE_64BIT_OPS)
    DUK__DUMPLM_SIGNED(INT64);
    DUK__DUMPLM_UNSIGNED(UINT64);
    DUK__DUMPLM_SIGNED(INT_FAST64);
    DUK__DUMPLM_UNSIGNED(UINT_FAST64);
    DUK__DUMPLM_SIGNED(INT_LEAST64);
    DUK__DUMPLM_UNSIGNED(UINT_LEAST64);
#endif
    DUK__DUMPLM_SIGNED(INTPTR);
    DUK__DUMPLM_UNSIGNED(UINTPTR);
    DUK__DUMPLM_SIGNED(INTMAX);
    DUK__DUMPLM_UNSIGNED(UINTMAX);

    /* derived types */
    DUK__DUMPLM_SIGNED(INT);
    DUK__DUMPLM_UNSIGNED(UINT);
    DUK__DUMPLM_SIGNED(INT_FAST);
    DUK__DUMPLM_UNSIGNED(UINT_FAST);
    DUK__DUMPLM_SIGNED(SMALL_INT);
    DUK__DUMPLM_UNSIGNED(SMALL_UINT);
    DUK__DUMPLM_SIGNED(SMALL_INT_FAST);
    DUK__DUMPLM_UNSIGNED(SMALL_UINT_FAST);
}

DUK_LOCAL void duk__dump_misc_options(void) {
    DUK_D(DUK_DPRINT("DUK_VERSION: %ld", (long) DUK_VERSION));
    DUK_D(DUK_DPRINT("DUK_GIT_DESCRIBE: %s", DUK_GIT_DESCRIBE));
    DUK_D(DUK_DPRINT("OS string: %s", DUK_USE_OS_STRING));
    DUK_D(DUK_DPRINT("architecture string: %s", DUK_USE_ARCH_STRING));
    DUK_D(DUK_DPRINT("compiler string: %s", DUK_USE_COMPILER_STRING));
    DUK_D(DUK_DPRINT("debug level: %ld", (long) DUK_USE_DEBUG_LEVEL));
#if defined(DUK_USE_PACKED_TVAL)
    DUK_D(DUK_DPRINT("DUK_USE_PACKED_TVAL: yes"));
#else
    DUK_D(DUK_DPRINT("DUK_USE_PACKED_TVAL: no"));
#endif
#if defined(DUK_USE_VARIADIC_MACROS)
    DUK_D(DUK_DPRINT("DUK_USE_VARIADIC_MACROS: yes"));
#else
    DUK_D(DUK_DPRINT("DUK_USE_VARIADIC_MACROS: no"));
#endif
#if defined(DUK_USE_INTEGER_LE)
    DUK_D(DUK_DPRINT("integer endianness: little"));
#elif defined(DUK_USE_INTEGER_ME)
    DUK_D(DUK_DPRINT("integer endianness: mixed"));
#elif defined(DUK_USE_INTEGER_BE)
    DUK_D(DUK_DPRINT("integer endianness: big"));
#else
    DUK_D(DUK_DPRINT("integer endianness: ???"));
#endif
#if defined(DUK_USE_DOUBLE_LE)
    DUK_D(DUK_DPRINT("IEEE double endianness: little"));
#elif defined(DUK_USE_DOUBLE_ME)
    DUK_D(DUK_DPRINT("IEEE double endianness: mixed"));
#elif defined(DUK_USE_DOUBLE_BE)
    DUK_D(DUK_DPRINT("IEEE double endianness: big"));
#else
    DUK_D(DUK_DPRINT("IEEE double endianness: ???"));
#endif
}
#endif  /* DUK_USE_DEBUG */

DUK_INTERNAL
duk_heap *duk_heap_alloc(duk_alloc_function alloc_func,
                         duk_realloc_function realloc_func,
                         duk_free_function free_func,
                         void *heap_udata,
                         duk_fatal_function fatal_func) {
    duk_heap *res = NULL;
    duk_uint32_t st_initsize;

    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;
#endif
#if defined(DUK_USE_CACHE_CATCHER)
    res->catcher_free = NULL;
#endif
    res->heap_thread = NULL;
    res->curr_thread = NULL;
    res->heap_object = NULL;
#if defined(DUK_USE_STRTAB_PTRCOMP)
    res->strtable16 = NULL;
#else
    res->strtable = NULL;
#endif
#if defined(DUK_USE_ROM_STRINGS)
    /* no res->strs[] */
#else  /* DUK_USE_ROM_STRINGS */
#if defined(DUK_USE_HEAPPTR16)
    /* res->strs16[] is zeroed and zero decodes to NULL, so no NULL inits. */
#else
    {
        duk_small_uint_t i;
            for (i = 0; i < DUK_HEAP_NUM_STRINGS; i++) {
            res->strs[i] = NULL;
            }
    }
#endif
#endif  /* DUK_USE_ROM_STRINGS */
#if defined(DUK_USE_DEBUGGER_SUPPORT)
    res->dbg_read_cb = NULL;
    res->dbg_write_cb = NULL;
    res->dbg_peek_cb = NULL;
    res->dbg_read_flush_cb = NULL;
    res->dbg_write_flush_cb = NULL;
    res->dbg_request_cb = NULL;
    res->dbg_udata = NULL;
    res->dbg_pause_act = NULL;
#endif
#endif  /* DUK_USE_EXPLICIT_NULL_INIT */

    res->alloc_func = alloc_func;
    res->realloc_func = realloc_func;
    res->free_func = free_func;
    res->heap_udata = heap_udata;
    res->fatal_func = fatal_func;

    /* XXX: for now there's a pointer packing zero assumption, i.e.
     * NULL <=> compressed pointer 0.  If this is removed, may need
     * to precompute e.g. null16 here.
     */

    /* res->ms_trigger_counter == 0 -> now causes immediate GC; which is OK */

    /* Prevent mark-and-sweep and finalizer execution until heap is completely
     * initialized.
     */
    DUK_ASSERT(res->ms_prevent_count == 0);
    DUK_ASSERT(res->pf_prevent_count == 0);
    res->ms_prevent_count = 1;
    res->pf_prevent_count = 1;
    DUK_ASSERT(res->ms_running == 0);

    res->call_recursion_depth = 0;
    res->call_recursion_limit = DUK_USE_NATIVE_CALL_RECLIMIT;

    /* XXX: use the pointer as a seed for now: mix in time at least */

    /* The casts through duk_uintptr_t is to avoid the following GCC warning:
     *
     *   warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
     *
     * This still generates a /Wp64 warning on VS2010 when compiling for x86.
     */
#if defined(DUK_USE_ROM_STRINGS)
    /* XXX: make a common DUK_USE_ option, and allow custom fixed seed? */
    DUK_D(DUK_DPRINT("using rom strings, force heap hash_seed to fixed value 0x%08lx", (long) DUK__FIXED_HASH_SEED));
    res->hash_seed = (duk_uint32_t) DUK__FIXED_HASH_SEED;
#else  /* DUK_USE_ROM_STRINGS */
    res->hash_seed = (duk_uint32_t) (duk_uintptr_t) res;
#if !defined(DUK_USE_STRHASH_DENSE)
    res->hash_seed ^= 5381;  /* Bernstein hash init value is normally 5381; XOR it in in case pointer low bits are 0 */
#endif
#endif  /* DUK_USE_ROM_STRINGS */

#if defined(DUK_USE_EXPLICIT_NULL_INIT)
    res->lj.jmpbuf_ptr = NULL;
#endif
    DUK_ASSERT(res->lj.type == DUK_LJ_TYPE_UNKNOWN);  /* zero */
    DUK_ASSERT(res->lj.iserror == 0);
    DUK_TVAL_SET_UNDEFINED(&res->lj.value1);
    DUK_TVAL_SET_UNDEFINED(&res->lj.value2);

    DUK_ASSERT_LJSTATE_UNSET(res);

    /*
     *  Init stringtable: fixed variant
     */

    st_initsize = DUK_USE_STRTAB_MINSIZE;
#if defined(DUK_USE_STRTAB_PTRCOMP)
    res->strtable16 = (duk_uint16_t *) alloc_func(heap_udata, sizeof(duk_uint16_t) * st_initsize);
    if (res->strtable16 == NULL) {
        goto failed;
    }
#else
    res->strtable = (duk_hstring **) alloc_func(heap_udata, sizeof(duk_hstring *) * st_initsize);
    if (res->strtable == NULL) {
        goto failed;
    }
#endif
    res->st_size = st_initsize;
    res->st_mask = st_initsize - 1;
#if (DUK_USE_STRTAB_MINSIZE != DUK_USE_STRTAB_MAXSIZE)
    DUK_ASSERT(res->st_count == 0);
#endif

#if defined(DUK_USE_STRTAB_PTRCOMP)
    /* zero assumption */
    duk_memzero(res->strtable16, sizeof(duk_uint16_t) * st_initsize);
#else
#if defined(DUK_USE_EXPLICIT_NULL_INIT)
    {
        duk_uint32_t i;
            for (i = 0; i < st_initsize; i++) {
            res->strtable[i] = NULL;
            }
    }
#else
    duk_memzero(res->strtable, sizeof(duk_hstring *) * st_initsize);
#endif  /* DUK_USE_EXPLICIT_NULL_INIT */
#endif  /* DUK_USE_STRTAB_PTRCOMP */

    /*
     *  Init stringcache
     */

#if defined(DUK_USE_EXPLICIT_NULL_INIT)
    {
        duk_uint_t i;
        for (i = 0; i < DUK_HEAP_STRCACHE_SIZE; i++) {
            res->strcache[i].h = NULL;
        }
    }
#endif

    /*
     *  Init litcache
     */
#if defined(DUK_USE_LITCACHE_SIZE)
    DUK_ASSERT(DUK_USE_LITCACHE_SIZE > 0);
    DUK_ASSERT(DUK_IS_POWER_OF_TWO((duk_uint_t) DUK_USE_LITCACHE_SIZE));
#if defined(DUK_USE_EXPLICIT_NULL_INIT)
    {
        duk_uint_t i;
        for (i = 0; i < DUK_USE_LITCACHE_SIZE; i++) {
            res->litcache[i].addr = NULL;
            res->litcache[i].h = NULL;
        }
    }
#endif
#endif  /* DUK_USE_LITCACHE_SIZE */

    /* XXX: error handling is incomplete.  It would be cleanest if
     * there was a setjmp catchpoint, so that all init code could
     * freely throw errors.  If that were the case, the return code
     * passing here could be removed.
     */

    /*
     *  Init built-in strings
     */

#if defined(DUK_USE_INJECT_HEAP_ALLOC_ERROR) && (DUK_USE_INJECT_HEAP_ALLOC_ERROR == 2)
    goto failed;
#endif
    DUK_D(DUK_DPRINT("heap init: initialize heap strings"));
    if (!duk__init_heap_strings(res)) {
        goto failed;
    }

    /*
     *  Init the heap thread
     */

#if defined(DUK_USE_INJECT_HEAP_ALLOC_ERROR) && (DUK_USE_INJECT_HEAP_ALLOC_ERROR == 3)
    goto failed;
#endif
    DUK_D(DUK_DPRINT("heap init: initialize heap thread"));
    if (!duk__init_heap_thread(res)) {
        goto failed;
    }

    /*
     *  Init the heap object
     */

#if defined(DUK_USE_INJECT_HEAP_ALLOC_ERROR) && (DUK_USE_INJECT_HEAP_ALLOC_ERROR == 4)
    goto failed;
#endif
    DUK_D(DUK_DPRINT("heap init: initialize heap object"));
    DUK_ASSERT(res->heap_thread != NULL);
    res->heap_object = duk_hobject_alloc_unchecked(res, DUK_HOBJECT_FLAG_EXTENSIBLE |
                                                        DUK_HOBJECT_FLAG_FASTREFS |
                                                        DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT));
    if (res->heap_object == NULL) {
        goto failed;
    }
    DUK_HOBJECT_INCREF(res->heap_thread, res->heap_object);

    /*
     *  Odds and ends depending on the heap thread
     */

#if !defined(DUK_USE_GET_RANDOM_DOUBLE)
#if defined(DUK_USE_PREFER_SIZE) || !defined(DUK_USE_64BIT_OPS)
    res->rnd_state = (duk_uint32_t) duk_time_get_ecmascript_time(res->heap_thread);
    duk_util_tinyrandom_prepare_seed(res->heap_thread);
#else
    res->rnd_state[0] = (duk_uint64_t) duk_time_get_ecmascript_time(res->heap_thread);
    DUK_ASSERT(res->rnd_state[1] == 0);  /* Not filled here, filled in by seed preparation. */
#if 0  /* Manual test values matching misc/xoroshiro128plus_test.c. */
    res->rnd_state[0] = DUK_U64_CONSTANT(0xdeadbeef12345678);
    res->rnd_state[1] = DUK_U64_CONSTANT(0xcafed00d12345678);
#endif
    duk_util_tinyrandom_prepare_seed(res->heap_thread);
    /* Mix in heap pointer: this ensures that if two Duktape heaps are
     * created on the same millisecond, they get a different PRNG
     * sequence (unless e.g. virtual memory addresses cause also the
     * heap object pointer to be the same).
     */
    {
        duk_uint64_t tmp_u64;
        tmp_u64 = 0;
        duk_memcpy((void *) &tmp_u64,
                   (const void *) &res,
                   (size_t) (sizeof(void *) >= sizeof(duk_uint64_t) ? sizeof(duk_uint64_t) : sizeof(void *)));
        res->rnd_state[1] ^= tmp_u64;
    }
    do {
        duk_small_uint_t i;
        for (i = 0; i < 10; i++) {
            /* Throw away a few initial random numbers just in
             * case.  Probably unnecessary due to SplitMix64
             * preparation.
             */
            (void) duk_util_tinyrandom_get_double(res->heap_thread);
        }
    } while (0);
#endif
#endif

    /*
     *  Allow finalizer and mark-and-sweep processing.
     */

    DUK_D(DUK_DPRINT("heap init: allow finalizer/mark-and-sweep processing"));
    DUK_ASSERT(res->ms_prevent_count == 1);
    DUK_ASSERT(res->pf_prevent_count == 1);
    res->ms_prevent_count = 0;
    res->pf_prevent_count = 0;
    DUK_ASSERT(res->ms_running == 0);
#if defined(DUK_USE_ASSERTIONS)
    res->heap_initializing = 0;
#endif

    /*
     *  All done.
     */

    DUK_D(DUK_DPRINT("allocated heap: %p", (void *) res));
    return res;

 failed:
    DUK_D(DUK_DPRINT("heap allocation failed"));

    if (res != NULL) {
        /* Assumes that allocated pointers and alloc funcs are valid
         * if res exists.
         */
        DUK_ASSERT(res->ms_prevent_count == 1);
        DUK_ASSERT(res->pf_prevent_count == 1);
        DUK_ASSERT(res->ms_running == 0);
        if (res->heap_thread != NULL) {
            res->ms_prevent_count = 0;
            res->pf_prevent_count = 0;
        }
#if defined(DUK_USE_ASSERTIONS)
        res->heap_initializing = 0;
#endif

        DUK_ASSERT(res->alloc_func != NULL);
        DUK_ASSERT(res->realloc_func != NULL);
        DUK_ASSERT(res->free_func != NULL);
        duk_heap_free(res);
    }

    return NULL;
}

/* automatic undefs */
#undef DUK__DUMPLM_SIGNED
#undef DUK__DUMPLM_SIGNED_RAW
#undef DUK__DUMPLM_UNSIGNED
#undef DUK__DUMPLM_UNSIGNED_RAW
#undef DUK__DUMPSZ
#undef DUK__FIXED_HASH_SEED
//#line 1 "duk_heap_finalize.c"
/*
 *  Finalizer handling.
 */

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

#if defined(DUK_USE_FINALIZER_SUPPORT)

/*
 *  Fake torture finalizer.
 */

#if defined(DUK_USE_FINALIZER_TORTURE)
DUK_LOCAL duk_ret_t duk__fake_global_finalizer(duk_hthread *thr) {
    DUK_DD(DUK_DDPRINT("fake global torture finalizer executed"));

    /* Require a lot of stack to force a value stack grow/shrink. */
    duk_require_stack(thr, 100000);

    /* Force a reallocation with pointer change for value stack
     * to maximize side effects.
     */
    duk_hthread_valstack_torture_realloc(thr);

    /* Inner function call, error throw. */
    duk_eval_string_noresult(thr,
        "(function dummy() {\n"
        "    dummy.prototype = null;  /* break reference loop */\n"
        "    try {\n"
        "        throw 'fake-finalizer-dummy-error';\n"
        "    } catch (e) {\n"
        "        void e;\n"
        "    }\n"
        "})()");

    /* The above creates garbage (e.g. a function instance).  Because
     * the function/prototype reference loop is broken, it gets collected
     * immediately by DECREF.  If Function.prototype has a _Finalizer
     * property (happens in some test cases), the garbage gets queued to
     * finalize_list.  This still won't cause an infinite loop because
     * the torture finalizer is called once per finalize_list run and
     * the garbage gets handled in the same run.  (If the garbage needs
     * mark-and-sweep collection, an infinite loop might ensue.)
     */
    return 0;
}

DUK_LOCAL void duk__run_global_torture_finalizer(duk_hthread *thr) {
    DUK_ASSERT(thr != NULL);

    /* Avoid fake finalization when callstack limit is near.  Otherwise
     * a callstack limit error will be created, then refzero'ed.  The
     * +5 headroom is conservative.
     */
    if (thr->heap->call_recursion_depth + 5 >= thr->heap->call_recursion_limit ||
        thr->callstack_top + 5 >= DUK_USE_CALLSTACK_LIMIT) {
        DUK_D(DUK_DPRINT("skip global torture finalizer, too little headroom for call recursion or call stack size"));
        return;
    }

    /* Run fake finalizer.  Avoid creating unnecessary garbage. */
    duk_push_c_function(thr, duk__fake_global_finalizer, 0 /*nargs*/);
    (void) duk_pcall(thr, 0 /*nargs*/);
    duk_pop(thr);
}
#endif  /* DUK_USE_FINALIZER_TORTURE */

/*
 *  Process the finalize_list to completion.
 *
 *  An object may be placed on finalize_list by either refcounting or
 *  mark-and-sweep.  The refcount of objects placed by refcounting will be
 *  zero; the refcount of objects placed by mark-and-sweep is > 0.  In both
 *  cases the refcount is bumped by 1 artificially so that a REFZERO event
 *  can never happen while an object is waiting for finalization.  Without
 *  this bump a REFZERO could now happen because user code may call
 *  duk_push_heapptr() and then pop a value even when it's on finalize_list.
 *
 *  List processing assumes refcounts are kept up-to-date at all times, so
 *  that once the finalizer returns, a zero refcount is a reliable reason to
 *  free the object immediately rather than place it back to the heap.  This
 *  is the case because we run outside of refzero_list processing so that
 *  DECREF cascades are handled fully inline.
 *
 *  For mark-and-sweep queued objects (had_zero_refcount false) the object
 *  may be freed immediately if its refcount is zero after the finalizer call
 *  (i.e. finalizer removed the reference loop for the object).  If not, the
 *  next mark-and-sweep will collect the object unless it has become reachable
 *  (i.e. rescued) by that time and its refcount hasn't fallen to zero before
 *  that.  Mark-and-sweep detects these objects because their FINALIZED flag
 *  is set.
 *
 *  There's an inherent limitation for mark-and-sweep finalizer rescuing: an
 *  object won't get refinalized if (1) it's rescued, but (2) becomes
 *  unreachable before mark-and-sweep has had time to notice it.  The next
 *  mark-and-sweep round simply doesn't have any information of whether the
 *  object has been unreachable the whole time or not (the only way to get
 *  that information would be a mark-and-sweep pass for *every finalized
 *  object*).  This is awkward for the application because the mark-and-sweep
 *  round is not generally visible or under full application control.
 *
 *  For refcount queued objects (had_zero_refcount true) the object is either
 *  immediately freed or rescued, and waiting for a mark-and-sweep round is not
 *  necessary (or desirable); FINALIZED is cleared when a rescued object is
 *  queued back to heap_allocated.  The object is eligible for finalization
 *  again (either via refcounting or mark-and-sweep) immediately after being
 *  rescued.  If a refcount finalized object is placed into an unreachable
 *  reference loop by its finalizer, it will get collected by mark-and-sweep
 *  and currently the finalizer will execute again.
 *
 *  There's a special case where:
 *
 *    - Mark-and-sweep queues an object to finalize_list for finalization.
 *    - The finalizer is executed, FINALIZED is set, and object is queued
 *      back to heap_allocated, waiting for a new mark-and-sweep round.
 *    - The object's refcount drops to zero before mark-and-sweep has a
 *      chance to run another round and make a rescue/free decision.
 *
 *  This is now handled by refzero code: if an object has a finalizer but
 *  FINALIZED is already set, the object is freed without finalizer processing.
 *  The outcome is the same as if mark-and-sweep was executed at that point;
 *  mark-and-sweep would also free the object without another finalizer run.
 *  This could also be changed so that the refzero-triggered finalizer *IS*
 *  executed: being refzero collected implies someone has operated on the
 *  object so it hasn't been totally unreachable the whole time.  This would
 *  risk a finalizer loop however.
 */

DUK_INTERNAL void duk_heap_process_finalize_list(duk_heap *heap) {
    duk_heaphdr *curr;
#if defined(DUK_USE_DEBUG)
    duk_size_t count = 0;
#endif

    DUK_DDD(DUK_DDDPRINT("duk_heap_process_finalize_list: %p", (void *) heap));

    if (heap->pf_prevent_count != 0) {
        DUK_DDD(DUK_DDDPRINT("skip finalize_list processing: pf_prevent_count != 0"));
        return;
    }

    /* Heap alloc prevents mark-and-sweep before heap_thread is ready. */
    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(heap->heap_thread != NULL);
    DUK_ASSERT(heap->heap_thread->valstack != NULL);
#if defined(DUK_USE_REFERENCE_COUNTING)
    DUK_ASSERT(heap->refzero_list == NULL);
#endif

    DUK_ASSERT(heap->pf_prevent_count == 0);
    heap->pf_prevent_count = 1;

    /* Mark-and-sweep no longer needs to be prevented when running
     * finalizers: mark-and-sweep skips any rescue decisions if there
     * are any objects in finalize_list when mark-and-sweep is entered.
     * This protects finalized objects from incorrect rescue decisions
     * caused by finalize_list being a reachability root and only
     * partially processed.  Freeing decisions are not postponed.
     */

    /* When finalizer torture is enabled, make a fake finalizer call with
     * maximum side effects regardless of whether finalize_list is empty.
     */
#if defined(DUK_USE_FINALIZER_TORTURE)
    duk__run_global_torture_finalizer(heap->heap_thread);
#endif

    /* Process finalize_list until it becomes empty.  There's currently no
     * protection against a finalizer always creating more garbage.
     */
    while ((curr = heap->finalize_list) != NULL) {
#if defined(DUK_USE_REFERENCE_COUNTING)
        duk_bool_t queue_back;
#endif

        DUK_DD(DUK_DDPRINT("processing finalize_list entry: %p -> %!iO", (void *) curr, curr));

        DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT);  /* Only objects have finalizers. */
        DUK_ASSERT(!DUK_HEAPHDR_HAS_REACHABLE(curr));
        DUK_ASSERT(!DUK_HEAPHDR_HAS_TEMPROOT(curr));
        DUK_ASSERT(DUK_HEAPHDR_HAS_FINALIZABLE(curr));  /* All objects on finalize_list will have this flag (except object being finalized right now). */
        DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr));   /* Queueing code ensures. */
        DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(curr));  /* ROM objects never get freed (or finalized). */

#if defined(DUK_USE_ASSERTIONS)
        DUK_ASSERT(heap->currently_finalizing == NULL);
        heap->currently_finalizing = curr;
#endif

        /* Clear FINALIZABLE for object being finalized, so that
         * duk_push_heapptr() can properly ignore the object.
         */
        DUK_HEAPHDR_CLEAR_FINALIZABLE(curr);

        if (DUK_LIKELY(!heap->pf_skip_finalizers)) {
            /* Run the finalizer, duk_heap_run_finalizer() sets
             * and checks for FINALIZED to prevent the finalizer
             * from executing multiple times per finalization cycle.
             * (This safeguard shouldn't be actually needed anymore).
             */

#if defined(DUK_USE_REFERENCE_COUNTING)
            duk_bool_t had_zero_refcount;
#endif

            /* The object's refcount is >0 throughout so it won't be
             * refzero processed prematurely.
             */
#if defined(DUK_USE_REFERENCE_COUNTING)
            DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(curr) >= 1);
            had_zero_refcount = (DUK_HEAPHDR_GET_REFCOUNT(curr) == 1);  /* Preincremented on finalize_list insert. */
#endif

            DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr));
            duk_heap_run_finalizer(heap, (duk_hobject *) curr);  /* must never longjmp */
            DUK_ASSERT(DUK_HEAPHDR_HAS_FINALIZED(curr));
            /* XXX: assert that object is still in finalize_list
             * when duk_push_heapptr() allows automatic rescue.
             */

#if defined(DUK_USE_REFERENCE_COUNTING)
            DUK_DD(DUK_DDPRINT("refcount after finalizer (includes bump): %ld", (long) DUK_HEAPHDR_GET_REFCOUNT(curr)));
            if (DUK_HEAPHDR_GET_REFCOUNT(curr) == 1) {  /* Only artificial bump in refcount? */
#if defined(DUK_USE_DEBUG)
                if (had_zero_refcount) {
                    DUK_DD(DUK_DDPRINT("finalized object's refcount is zero -> free immediately (refcount queued)"));
                } else {
                    DUK_DD(DUK_DDPRINT("finalized object's refcount is zero -> free immediately (mark-and-sweep queued)"));
                }
#endif
                queue_back = 0;
            } else
#endif
            {
#if defined(DUK_USE_REFERENCE_COUNTING)
                queue_back = 1;
                if (had_zero_refcount) {
                    /* When finalization is triggered
                     * by refzero and we queue the object
                     * back, clear FINALIZED right away
                     * so that the object can be refinalized
                     * immediately if necessary.
                     */
                    DUK_HEAPHDR_CLEAR_FINALIZED(curr);
                }
#endif
            }
        } else {
            /* Used during heap destruction: don't actually run finalizers
             * because we're heading into forced finalization.  Instead,
             * queue finalizable objects back to the heap_allocated list.
             */
            DUK_D(DUK_DPRINT("skip finalizers flag set, queue object to heap_allocated without finalizing"));
            DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr));
#if defined(DUK_USE_REFERENCE_COUNTING)
            queue_back = 1;
#endif
        }

        /* Dequeue object from finalize_list.  Note that 'curr' may no
         * longer be finalize_list head because new objects may have
         * been queued to the list.  As a result we can't optimize for
         * the single-linked heap case and must scan the list for
         * removal, typically the scan is very short however.
         */
        DUK_HEAP_REMOVE_FROM_FINALIZE_LIST(heap, curr);

        /* Queue back to heap_allocated or free immediately. */
#if defined(DUK_USE_REFERENCE_COUNTING)
        if (queue_back) {
            /* FINALIZED is only cleared if object originally
             * queued for finalization by refcounting.  For
             * mark-and-sweep FINALIZED is left set, so that
             * next mark-and-sweep round can make a rescue/free
             * decision.
             */
            DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(curr) >= 1);
            DUK_HEAPHDR_PREDEC_REFCOUNT(curr);  /* Remove artificial refcount bump. */
            DUK_HEAPHDR_CLEAR_FINALIZABLE(curr);
            DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap, curr);
        } else {
            /* No need to remove the refcount bump here. */
            DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT);  /* currently, always the case */
            DUK_DD(DUK_DDPRINT("refcount finalize after finalizer call: %!O", curr));
            duk_hobject_refcount_finalize_norz(heap, (duk_hobject *) curr);
            duk_free_hobject(heap, (duk_hobject *) curr);
            DUK_DD(DUK_DDPRINT("freed hobject after finalization: %p", (void *) curr));
        }
#else  /* DUK_USE_REFERENCE_COUNTING */
        DUK_HEAPHDR_CLEAR_FINALIZABLE(curr);
        DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap, curr);
#endif  /* DUK_USE_REFERENCE_COUNTING */

#if defined(DUK_USE_DEBUG)
        count++;
#endif

#if defined(DUK_USE_ASSERTIONS)
        DUK_ASSERT(heap->currently_finalizing != NULL);
        heap->currently_finalizing = NULL;
#endif
    }

    /* finalize_list will always be processed completely. */
    DUK_ASSERT(heap->finalize_list == NULL);

#if 0
    /* While NORZ macros are used above, this is unnecessary because the
     * only pending side effects are now finalizers, and finalize_list is
     * empty.
     */
    DUK_REFZERO_CHECK_SLOW(heap->heap_thread);
#endif

    /* Prevent count may be bumped while finalizers run, but should always
     * be reliably unbumped by the time we get here.
     */
    DUK_ASSERT(heap->pf_prevent_count == 1);
    heap->pf_prevent_count = 0;

#if defined(DUK_USE_DEBUG)
    DUK_DD(DUK_DDPRINT("duk_heap_process_finalize_list: %ld finalizers called", (long) count));
#endif
}

/*
 *  Run an duk_hobject finalizer.  Must never throw an uncaught error
 *  (but may throw caught errors).
 *
 *  There is no return value.  Any return value or error thrown by
 *  the finalizer is ignored (although errors are debug logged).
 *
 *  Notes:
 *
 *    - The finalizer thread 'top' assertions are there because it is
 *      critical that strict stack policy is observed (i.e. no cruft
 *      left on the finalizer stack).
 */

DUK_LOCAL duk_ret_t duk__finalize_helper(duk_hthread *thr, void *udata) {
    DUK_ASSERT(thr != NULL);
    DUK_UNREF(udata);

    DUK_DDD(DUK_DDDPRINT("protected finalization helper running"));

    /* [... obj] */

    /* _Finalizer property is read without checking if the value is
     * callable or even exists.  This is intentional, and handled
     * by throwing an error which is caught by the safe call wrapper.
     *
     * XXX: Finalizer lookup should traverse the prototype chain (to allow
     * inherited finalizers) but should not invoke accessors or proxy object
     * behavior.  At the moment this lookup will invoke proxy behavior, so
     * caller must ensure that this function is not called if the target is
     * a Proxy.
     */
    duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INT_FINALIZER);  /* -> [... obj finalizer] */
    duk_dup_m2(thr);
    duk_push_boolean(thr, DUK_HEAP_HAS_FINALIZER_NORESCUE(thr->heap));
    DUK_DDD(DUK_DDDPRINT("calling finalizer"));
    duk_call(thr, 2);  /* [ ... obj finalizer obj heapDestruct ]  -> [ ... obj retval ] */
    DUK_DDD(DUK_DDDPRINT("finalizer returned successfully"));
    return 0;

    /* Note: we rely on duk_safe_call() to fix up the stack for the caller,
     * so we don't need to pop stuff here.  There is no return value;
     * caller determines rescued status based on object refcount.
     */
}

DUK_INTERNAL void duk_heap_run_finalizer(duk_heap *heap, duk_hobject *obj) {
    duk_hthread *thr;
    duk_ret_t rc;
#if defined(DUK_USE_ASSERTIONS)
    duk_idx_t entry_top;
#endif

    DUK_DD(DUK_DDPRINT("running duk_hobject finalizer for object: %p", (void *) obj));

    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(heap->heap_thread != NULL);
    thr = heap->heap_thread;
    DUK_ASSERT(obj != NULL);
    DUK_ASSERT_VALSTACK_SPACE(heap->heap_thread, 1);

#if defined(DUK_USE_ASSERTIONS)
    entry_top = duk_get_top(thr);
#endif
    /*
     *  Get and call the finalizer.  All of this must be wrapped
     *  in a protected call, because even getting the finalizer
     *  may trigger an error (getter may throw one, for instance).
     */

    /* ROM objects could inherit a finalizer, but they are never deemed
     * unreachable by mark-and-sweep, and their refcount never falls to 0.
     */
    DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj));

    /* Duktape 2.1: finalize_list never contains objects with FINALIZED
     * set, so no need to check here.
     */
    DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) obj));
#if 0
    if (DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) obj)) {
        DUK_D(DUK_DPRINT("object already finalized, avoid running finalizer twice: %!O", obj));
        return;
    }
#endif
    DUK_HEAPHDR_SET_FINALIZED((duk_heaphdr *) obj);  /* ensure never re-entered until rescue cycle complete */

#if defined(DUK_USE_ES6_PROXY)
    if (DUK_HOBJECT_IS_PROXY(obj)) {
        /* This may happen if duk_set_finalizer() or Duktape.fin() is
         * called for a Proxy object.  In such cases the fast finalizer
         * flag will be set on the Proxy, not the target, and neither
         * will be finalized.
         */
        DUK_D(DUK_DPRINT("object is a Proxy, skip finalizer call"));
        return;
    }
#endif  /* DUK_USE_ES6_PROXY */

    duk_push_hobject(thr, obj);  /* this also increases refcount by one */
    rc = duk_safe_call(thr, duk__finalize_helper, NULL /*udata*/, 0 /*nargs*/, 1 /*nrets*/);  /* -> [... obj retval/error] */
    DUK_ASSERT_TOP(thr, entry_top + 2);  /* duk_safe_call discipline */

    if (rc != DUK_EXEC_SUCCESS) {
        /* Note: we ask for one return value from duk_safe_call to get this
         * error debugging here.
         */
        DUK_D(DUK_DPRINT("wrapped finalizer call failed for object %p (ignored); error: %!T",
                         (void *) obj, (duk_tval *) duk_get_tval(thr, -1)));
    }
    duk_pop_2(thr);  /* -> [...] */

    DUK_ASSERT_TOP(thr, entry_top);
}

#else  /* DUK_USE_FINALIZER_SUPPORT */

/* nothing */

#endif  /* DUK_USE_FINALIZER_SUPPORT */
//#line 1 "duk_heap_hashstring.c"
/*
 *  String hash computation (interning).
 *
 *  String hashing is performance critical because a string hash is computed
 *  for all new strings which are candidates to be added to the string table.
 *  However, strings actually added to the string table go through a codepoint
 *  length calculation which dominates performance because it goes through
 *  every byte of the input string (but only for strings added).
 *
 *  The string hash algorithm should be fast, but on the other hand provide
 *  good enough hashes to ensure both string table and object property table
 *  hash tables work reasonably well (i.e., there aren't too many collisions
 *  with real world inputs).  Unless the hash is cryptographic, it's always
 *  possible to craft inputs with maximal hash collisions.
 *
 *  NOTE: The hash algorithms must match tools/dukutil.py:duk_heap_hashstring()
 *  for ROM string support!
 */

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

#if defined(DUK_USE_STRHASH_DENSE)
/* Constants for duk_hashstring(). */
#define DUK__STRHASH_SHORTSTRING   4096L
#define DUK__STRHASH_MEDIUMSTRING  (256L * 1024L)
#define DUK__STRHASH_BLOCKSIZE     256L

DUK_INTERNAL duk_uint32_t duk_heap_hashstring(duk_heap *heap, const duk_uint8_t *str, duk_size_t len) {
    duk_uint32_t hash;

    /* Use Murmurhash2 directly for short strings, and use "block skipping"
     * for long strings: hash an initial part and then sample the rest of
     * the string with reasonably sized chunks.  An initial offset for the
     * sampling is computed based on a hash of the initial part of the string;
     * this is done to (usually) avoid the case where all long strings have
     * certain offset ranges which are never sampled.
     *
     * Skip should depend on length and bound the total time to roughly
     * logarithmic.  With current values:
     *
     *   1M string => 256 * 241 = 61696 bytes (0.06M) of hashing
     *   1G string => 256 * 16321 = 4178176 bytes (3.98M) of hashing
     *
     * XXX: It would be better to compute the skip offset more "smoothly"
     * instead of having a few boundary values.
     */

    /* note: mixing len into seed improves hashing when skipping */
    duk_uint32_t str_seed = heap->hash_seed ^ ((duk_uint32_t) len);

    if (len <= DUK__STRHASH_SHORTSTRING) {
        hash = duk_util_hashbytes(str, len, str_seed);
    } else {
        duk_size_t off;
        duk_size_t skip;

        if (len <= DUK__STRHASH_MEDIUMSTRING) {
            skip = (duk_size_t) (16 * DUK__STRHASH_BLOCKSIZE + DUK__STRHASH_BLOCKSIZE);
        } else {
            skip = (duk_size_t) (256 * DUK__STRHASH_BLOCKSIZE + DUK__STRHASH_BLOCKSIZE);
        }

        hash = duk_util_hashbytes(str, (duk_size_t) DUK__STRHASH_SHORTSTRING, str_seed);
        off = DUK__STRHASH_SHORTSTRING + (skip * (hash % 256)) / 256;

        /* XXX: inefficient loop */
        while (off < len) {
            duk_size_t left = len - off;
            duk_size_t now = (duk_size_t) (left > DUK__STRHASH_BLOCKSIZE ? DUK__STRHASH_BLOCKSIZE : left);
            hash ^= duk_util_hashbytes(str + off, now, str_seed);
            off += skip;
        }
    }

#if defined(DUK_USE_STRHASH16)
    /* Truncate to 16 bits here, so that a computed hash can be compared
     * against a hash stored in a 16-bit field.
     */
    hash &= 0x0000ffffUL;
#endif
    return hash;
}
#else  /* DUK_USE_STRHASH_DENSE */
DUK_INTERNAL duk_uint32_t duk_heap_hashstring(duk_heap *heap, const duk_uint8_t *str, duk_size_t len) {
    duk_uint32_t hash;
    duk_size_t step;
    duk_size_t off;

    /* Slightly modified "Bernstein hash" from:
     *
     *     http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
     *
     * Modifications: string skipping and reverse direction similar to
     * Lua 5.1.5, and different hash initializer.
     *
     * The reverse direction ensures last byte it always included in the
     * hash which is a good default as changing parts of the string are
     * more often in the suffix than in the prefix.
     */

    hash = heap->hash_seed ^ ((duk_uint32_t) len);  /* Bernstein hash init value is normally 5381 */
    step = (len >> DUK_USE_STRHASH_SKIP_SHIFT) + 1;
    for (off = len; off >= step; off -= step) {
        DUK_ASSERT(off >= 1);  /* off >= step, and step >= 1 */
        hash = (hash * 33) + str[off - 1];
    }

#if defined(DUK_USE_STRHASH16)
    /* Truncate to 16 bits here, so that a computed hash can be compared
     * against a hash stored in a 16-bit field.
     */
    hash &= 0x0000ffffUL;
#endif
    return hash;
}
#endif  /* DUK_USE_STRHASH_DENSE */

/* automatic undefs */
#undef DUK__STRHASH_BLOCKSIZE
#undef DUK__STRHASH_MEDIUMSTRING
#undef DUK__STRHASH_SHORTSTRING
//#line 1 "duk_heap_markandsweep.c"
/*
 *  Mark-and-sweep garbage collection.
 */

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

DUK_LOCAL_DECL void duk__mark_heaphdr(duk_heap *heap, duk_heaphdr *h);
DUK_LOCAL_DECL void duk__mark_heaphdr_nonnull(duk_heap *heap, duk_heaphdr *h);
DUK_LOCAL_DECL void duk__mark_tval(duk_heap *heap, duk_tval *tv);
DUK_LOCAL_DECL void duk__mark_tvals(duk_heap *heap, duk_tval *tv, duk_idx_t count);

/*
 *  Marking functions for heap types: mark children recursively.
 */

DUK_LOCAL void duk__mark_hstring(duk_heap *heap, duk_hstring *h) {
    DUK_UNREF(heap);
    DUK_UNREF(h);

    DUK_DDD(DUK_DDDPRINT("duk__mark_hstring: %p", (void *) h));
    DUK_ASSERT(h);
    DUK_HSTRING_ASSERT_VALID(h);

    /* nothing to process */
}

DUK_LOCAL void duk__mark_hobject(duk_heap *heap, duk_hobject *h) {
    duk_uint_fast32_t i;

    DUK_DDD(DUK_DDDPRINT("duk__mark_hobject: %p", (void *) h));

    DUK_ASSERT(h);
    DUK_HOBJECT_ASSERT_VALID(h);

    /* XXX: use advancing pointers instead of index macros -> faster and smaller? */

    for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(h); i++) {
        duk_hstring *key = DUK_HOBJECT_E_GET_KEY(heap, h, i);
        if (key == NULL) {
            continue;
        }
        duk__mark_heaphdr_nonnull(heap, (duk_heaphdr *) key);
        if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(heap, h, i)) {
            duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->a.get);
            duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->a.set);
        } else {
            duk__mark_tval(heap, &DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->v);
        }
    }

    for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(h); i++) {
        duk__mark_tval(heap, DUK_HOBJECT_A_GET_VALUE_PTR(heap, h, i));
    }

    /* Hash part is a 'weak reference' and does not contribute. */

    duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HOBJECT_GET_PROTOTYPE(heap, h));

    /* Fast path for objects which don't have a subclass struct, or have a
     * subclass struct but nothing that needs marking in the subclass struct.
     */
    if (DUK_HOBJECT_HAS_FASTREFS(h)) {
        DUK_ASSERT(DUK_HOBJECT_ALLOWS_FASTREFS(h));
        return;
    }
    DUK_ASSERT(DUK_HOBJECT_PROHIBITS_FASTREFS(h));

    /* XXX: reorg, more common first */
    if (DUK_HOBJECT_IS_COMPFUNC(h)) {
        duk_hcompfunc *f = (duk_hcompfunc *) h;
        duk_tval *tv, *tv_end;
        duk_hobject **fn, **fn_end;

        DUK_HCOMPFUNC_ASSERT_VALID(f);

        /* 'data' is reachable through every compiled function which
         * contains a reference.
         */

        duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HCOMPFUNC_GET_DATA(heap, f));
        duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HCOMPFUNC_GET_LEXENV(heap, f));
        duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HCOMPFUNC_GET_VARENV(heap, f));

        if (DUK_HCOMPFUNC_GET_DATA(heap, f) != NULL) {
            tv = DUK_HCOMPFUNC_GET_CONSTS_BASE(heap, f);
            tv_end = DUK_HCOMPFUNC_GET_CONSTS_END(heap, f);
            while (tv < tv_end) {
                duk__mark_tval(heap, tv);
                tv++;
            }

            fn = DUK_HCOMPFUNC_GET_FUNCS_BASE(heap, f);
            fn_end = DUK_HCOMPFUNC_GET_FUNCS_END(heap, f);
            while (fn < fn_end) {
                duk__mark_heaphdr_nonnull(heap, (duk_heaphdr *) *fn);
                fn++;
            }
        } else {
            /* May happen in some out-of-memory corner cases. */
            DUK_D(DUK_DPRINT("duk_hcompfunc 'data' is NULL, skipping marking"));
        }
    } else if (DUK_HOBJECT_IS_DECENV(h)) {
        duk_hdecenv *e = (duk_hdecenv *) h;
        DUK_HDECENV_ASSERT_VALID(e);
        duk__mark_heaphdr(heap, (duk_heaphdr *) e->thread);
        duk__mark_heaphdr(heap, (duk_heaphdr *) e->varmap);
    } else if (DUK_HOBJECT_IS_OBJENV(h)) {
        duk_hobjenv *e = (duk_hobjenv *) h;
        DUK_HOBJENV_ASSERT_VALID(e);
        duk__mark_heaphdr_nonnull(heap, (duk_heaphdr *) e->target);
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
    } else if (DUK_HOBJECT_IS_BUFOBJ(h)) {
        duk_hbufobj *b = (duk_hbufobj *) h;
        DUK_HBUFOBJ_ASSERT_VALID(b);
        duk__mark_heaphdr(heap, (duk_heaphdr *) b->buf);
        duk__mark_heaphdr(heap, (duk_heaphdr *) b->buf_prop);
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
    } else if (DUK_HOBJECT_IS_BOUNDFUNC(h)) {
        duk_hboundfunc *f = (duk_hboundfunc *) (void *) h;
        DUK_HBOUNDFUNC_ASSERT_VALID(f);
        duk__mark_tval(heap, &f->target);
        duk__mark_tval(heap, &f->this_binding);
        duk__mark_tvals(heap, f->args, f->nargs);
#if defined(DUK_USE_ES6_PROXY)
    } else if (DUK_HOBJECT_IS_PROXY(h)) {
        duk_hproxy *p = (duk_hproxy *) h;
        DUK_HPROXY_ASSERT_VALID(p);
        duk__mark_heaphdr_nonnull(heap, (duk_heaphdr *) p->target);
        duk__mark_heaphdr_nonnull(heap, (duk_heaphdr *) p->handler);
#endif  /* DUK_USE_ES6_PROXY */
    } else if (DUK_HOBJECT_IS_THREAD(h)) {
        duk_hthread *t = (duk_hthread *) h;
        duk_activation *act;
        duk_tval *tv;

        DUK_HTHREAD_ASSERT_VALID(t);

        tv = t->valstack;
        while (tv < t->valstack_top) {
            duk__mark_tval(heap, tv);
            tv++;
        }

        for (act = t->callstack_curr; act != NULL; act = act->parent) {
            duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_ACT_GET_FUNC(act));
            duk__mark_heaphdr(heap, (duk_heaphdr *) act->var_env);
            duk__mark_heaphdr(heap, (duk_heaphdr *) act->lex_env);
#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
            duk__mark_heaphdr(heap, (duk_heaphdr *) act->prev_caller);
#endif
#if 0  /* nothing now */
            for (cat = act->cat; cat != NULL; cat = cat->parent) {
            }
#endif
        }

        duk__mark_heaphdr(heap, (duk_heaphdr *) t->resumer);

        for (i = 0; i < DUK_NUM_BUILTINS; i++) {
            duk__mark_heaphdr(heap, (duk_heaphdr *) t->builtins[i]);
        }
    } else {
        /* We may come here if the object should have a FASTREFS flag
         * but it's missing for some reason.  Assert for never getting
         * here; however, other than performance, this is harmless.
         */
        DUK_D(DUK_DPRINT("missing FASTREFS flag for: %!iO", h));
        DUK_ASSERT(0);
    }
}

/* Mark any duk_heaphdr type.  Recursion tracking happens only here. */
DUK_LOCAL void duk__mark_heaphdr(duk_heap *heap, duk_heaphdr *h) {
    DUK_DDD(DUK_DDDPRINT("duk__mark_heaphdr %p, type %ld",
                         (void *) h,
                         (h != NULL ? (long) DUK_HEAPHDR_GET_TYPE(h) : (long) -1)));

    /* XXX: add non-null variant? */
    if (h == NULL) {
        return;
    }

    DUK_HEAPHDR_ASSERT_VALID(h);
    DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(h) || DUK_HEAPHDR_HAS_REACHABLE(h));

#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING)
    if (!DUK_HEAPHDR_HAS_READONLY(h)) {
        h->h_assert_refcount++;  /* Comparison refcount: bump even if already reachable. */
    }
#endif
    if (DUK_HEAPHDR_HAS_REACHABLE(h)) {
        DUK_DDD(DUK_DDDPRINT("already marked reachable, skip"));
        return;
    }
#if defined(DUK_USE_ROM_OBJECTS)
    /* READONLY objects always have REACHABLE set, so the check above
     * will prevent READONLY objects from being marked here.
     */
    DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(h));
#endif

    DUK_HEAPHDR_SET_REACHABLE(h);

    if (heap->ms_recursion_depth >= DUK_USE_MARK_AND_SWEEP_RECLIMIT) {
        DUK_D(DUK_DPRINT("mark-and-sweep recursion limit reached, marking as temproot: %p", (void *) h));
        DUK_HEAP_SET_MARKANDSWEEP_RECLIMIT_REACHED(heap);
        DUK_HEAPHDR_SET_TEMPROOT(h);
        return;
    }

    heap->ms_recursion_depth++;
    DUK_ASSERT(heap->ms_recursion_depth != 0);  /* Wrap. */

    switch (DUK_HEAPHDR_GET_TYPE(h)) {
    case DUK_HTYPE_STRING:
        duk__mark_hstring(heap, (duk_hstring *) h);
        break;
    case DUK_HTYPE_OBJECT:
        duk__mark_hobject(heap, (duk_hobject *) h);
        break;
    case DUK_HTYPE_BUFFER:
        /* nothing to mark */
        break;
    default:
        DUK_D(DUK_DPRINT("attempt to mark heaphdr %p with invalid htype %ld", (void *) h, (long) DUK_HEAPHDR_GET_TYPE(h)));
        DUK_UNREACHABLE();
    }

    DUK_ASSERT(heap->ms_recursion_depth > 0);
    heap->ms_recursion_depth--;
}

DUK_LOCAL void duk__mark_tval(duk_heap *heap, duk_tval *tv) {
    DUK_DDD(DUK_DDDPRINT("duk__mark_tval %p", (void *) tv));
    if (tv == NULL) {
        return;
    }
    DUK_TVAL_ASSERT_VALID(tv);
    if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) {
        duk_heaphdr *h;
        h = DUK_TVAL_GET_HEAPHDR(tv);
        DUK_ASSERT(h != NULL);
        duk__mark_heaphdr_nonnull(heap, h);
    }
}

DUK_LOCAL void duk__mark_tvals(duk_heap *heap, duk_tval *tv, duk_idx_t count) {
    DUK_ASSERT(count == 0 || tv != NULL);

    while (count-- > 0) {
        DUK_TVAL_ASSERT_VALID(tv);
        if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) {
            duk_heaphdr *h;
            h = DUK_TVAL_GET_HEAPHDR(tv);
            DUK_ASSERT(h != NULL);
            duk__mark_heaphdr_nonnull(heap, h);
        }
        tv++;
    }
}

/* Mark any duk_heaphdr type, caller guarantees a non-NULL pointer. */
DUK_LOCAL void duk__mark_heaphdr_nonnull(duk_heap *heap, duk_heaphdr *h) {
    /* For now, just call the generic handler.  Change when call sites
     * are changed too.
     */
    duk__mark_heaphdr(heap, h);
}

/*
 *  Mark the heap.
 */

DUK_LOCAL void duk__mark_roots_heap(duk_heap *heap) {
    duk_small_uint_t i;

    DUK_DD(DUK_DDPRINT("duk__mark_roots_heap: %p", (void *) heap));

    duk__mark_heaphdr(heap, (duk_heaphdr *) heap->heap_thread);
    duk__mark_heaphdr(heap, (duk_heaphdr *) heap->heap_object);

    for (i = 0; i < DUK_HEAP_NUM_STRINGS; i++) {
        duk_hstring *h = DUK_HEAP_GET_STRING(heap, i);
        duk__mark_heaphdr(heap, (duk_heaphdr *) h);
    }

    duk__mark_tval(heap, &heap->lj.value1);
    duk__mark_tval(heap, &heap->lj.value2);

#if defined(DUK_USE_DEBUGGER_SUPPORT)
    for (i = 0; i < heap->dbg_breakpoint_count; i++) {
        duk__mark_heaphdr(heap, (duk_heaphdr *) heap->dbg_breakpoints[i].filename);
    }
#endif
}

/*
 *  Mark unreachable, finalizable objects.
 *
 *  Such objects will be moved aside and their finalizers run later.  They
 *  have to be treated as reachability roots for their properties etc to
 *  remain allocated.  This marking is only done for unreachable values which
 *  would be swept later.
 *
 *  Objects are first marked FINALIZABLE and only then marked as reachability
 *  roots; otherwise circular references might be handled inconsistently.
 */

#if defined(DUK_USE_FINALIZER_SUPPORT)
DUK_LOCAL void duk__mark_finalizable(duk_heap *heap) {
    duk_heaphdr *hdr;
    duk_size_t count_finalizable = 0;

    DUK_DD(DUK_DDPRINT("duk__mark_finalizable: %p", (void *) heap));

    DUK_ASSERT(heap->heap_thread != NULL);

    hdr = heap->heap_allocated;
    while (hdr != NULL) {
        /* A finalizer is looked up from the object and up its
         * prototype chain (which allows inherited finalizers).
         * The finalizer is checked for using a duk_hobject flag
         * which is kept in sync with the presence and callability
         * of a _Finalizer hidden symbol.
         */

        if (!DUK_HEAPHDR_HAS_REACHABLE(hdr) &&
            DUK_HEAPHDR_IS_OBJECT(hdr) &&
            !DUK_HEAPHDR_HAS_FINALIZED(hdr) &&
            DUK_HOBJECT_HAS_FINALIZER_FAST(heap, (duk_hobject *) hdr)) {
            /* heaphdr:
             *  - is not reachable
             *  - is an object
             *  - is not a finalized object waiting for rescue/keep decision
             *  - has a finalizer
             */

            DUK_DD(DUK_DDPRINT("unreachable heap object will be "
                               "finalized -> mark as finalizable "
                               "and treat as a reachability root: %p",
                               (void *) hdr));
            DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(hdr));
            DUK_HEAPHDR_SET_FINALIZABLE(hdr);
            count_finalizable++;
        }

        hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr);
    }

    if (count_finalizable == 0) {
        return;
    }

    DUK_DD(DUK_DDPRINT("marked %ld heap objects as finalizable, now mark them reachable",
                       (long) count_finalizable));

    hdr = heap->heap_allocated;
    while (hdr != NULL) {
        if (DUK_HEAPHDR_HAS_FINALIZABLE(hdr)) {
            duk__mark_heaphdr_nonnull(heap, hdr);
        }

        hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr);
    }

    /* Caller will finish the marking process if we hit a recursion limit. */
}
#endif  /* DUK_USE_FINALIZER_SUPPORT */

/*
 *  Mark objects on finalize_list.
 */

#if defined(DUK_USE_FINALIZER_SUPPORT)
DUK_LOCAL void duk__mark_finalize_list(duk_heap *heap) {
    duk_heaphdr *hdr;
#if defined(DUK_USE_DEBUG)
    duk_size_t count_finalize_list = 0;
#endif

    DUK_DD(DUK_DDPRINT("duk__mark_finalize_list: %p", (void *) heap));

    hdr = heap->finalize_list;
    while (hdr != NULL) {
        duk__mark_heaphdr_nonnull(heap, hdr);
        hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr);
#if defined(DUK_USE_DEBUG)
        count_finalize_list++;
#endif
    }

#if defined(DUK_USE_DEBUG)
    if (count_finalize_list > 0) {
        DUK_D(DUK_DPRINT("marked %ld objects on the finalize_list as reachable (previous finalizer run skipped)",
                         (long) count_finalize_list));
    }
#endif
}
#endif  /* DUK_USE_FINALIZER_SUPPORT */

/*
 *  Fallback marking handler if recursion limit is reached.
 *
 *  Iterates 'temproots' until recursion limit is no longer hit.  Temproots
 *  can be in heap_allocated or finalize_list; refzero_list is now always
 *  empty for mark-and-sweep.  A temproot may occur in finalize_list now if
 *  there are objects on the finalize_list and user code creates a reference
 *  from an object in heap_allocated to the object in finalize_list (which is
 *  now allowed), and it happened to coincide with the recursion depth limit.
 *
 *  This is a slow scan, but guarantees that we finish with a bounded C stack.
 *
 *  Note that nodes may have been marked as temproots before this scan begun,
 *  OR they may have been marked during the scan (as we process nodes
 *  recursively also during the scan).  This is intended behavior.
 */

#if defined(DUK_USE_DEBUG)
DUK_LOCAL void duk__handle_temproot(duk_heap *heap, duk_heaphdr *hdr, duk_size_t *count) {
#else
DUK_LOCAL void duk__handle_temproot(duk_heap *heap, duk_heaphdr *hdr) {
#endif
    DUK_ASSERT(hdr != NULL);

    if (!DUK_HEAPHDR_HAS_TEMPROOT(hdr)) {
        DUK_DDD(DUK_DDDPRINT("not a temp root: %p", (void *) hdr));
        return;
    }

    DUK_DDD(DUK_DDDPRINT("found a temp root: %p", (void *) hdr));
    DUK_HEAPHDR_CLEAR_TEMPROOT(hdr);
    DUK_HEAPHDR_CLEAR_REACHABLE(hdr);  /* Done so that duk__mark_heaphdr() works correctly. */
#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING)
    hdr->h_assert_refcount--;  /* Same node visited twice. */
#endif
    duk__mark_heaphdr_nonnull(heap, hdr);

#if defined(DUK_USE_DEBUG)
    (*count)++;
#endif
}

DUK_LOCAL void duk__mark_temproots_by_heap_scan(duk_heap *heap) {
    duk_heaphdr *hdr;
#if defined(DUK_USE_DEBUG)
    duk_size_t count;
#endif

    DUK_DD(DUK_DDPRINT("duk__mark_temproots_by_heap_scan: %p", (void *) heap));

    while (DUK_HEAP_HAS_MARKANDSWEEP_RECLIMIT_REACHED(heap)) {
        DUK_DD(DUK_DDPRINT("recursion limit reached, doing heap scan to continue from temproots"));

#if defined(DUK_USE_DEBUG)
        count = 0;
#endif
        DUK_HEAP_CLEAR_MARKANDSWEEP_RECLIMIT_REACHED(heap);

        hdr = heap->heap_allocated;
        while (hdr) {
#if defined(DUK_USE_DEBUG)
            duk__handle_temproot(heap, hdr, &count);
#else
            duk__handle_temproot(heap, hdr);
#endif
            hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr);
        }

#if defined(DUK_USE_FINALIZER_SUPPORT)
        hdr = heap->finalize_list;
        while (hdr) {
#if defined(DUK_USE_DEBUG)
            duk__handle_temproot(heap, hdr, &count);
#else
            duk__handle_temproot(heap, hdr);
#endif
            hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr);
        }
#endif

#if defined(DUK_USE_DEBUG)
        DUK_DD(DUK_DDPRINT("temproot mark heap scan processed %ld temp roots", (long) count));
#endif
    }
}

/*
 *  Finalize refcounts for heap elements just about to be freed.
 *  This must be done for all objects before freeing to avoid any
 *  stale pointer dereferences.
 *
 *  Note that this must deduce the set of objects to be freed
 *  identically to duk__sweep_heap().
 */

#if defined(DUK_USE_REFERENCE_COUNTING)
DUK_LOCAL void duk__finalize_refcounts(duk_heap *heap) {
    duk_heaphdr *hdr;

    DUK_ASSERT(heap->heap_thread != NULL);

    DUK_DD(DUK_DDPRINT("duk__finalize_refcounts: heap=%p", (void *) heap));

    hdr = heap->heap_allocated;
    while (hdr) {
        if (!DUK_HEAPHDR_HAS_REACHABLE(hdr)) {
            /*
             *  Unreachable object about to be swept.  Finalize target refcounts
             *  (objects which the unreachable object points to) without doing
             *  refzero processing.  Recursive decrefs are also prevented when
             *  refzero processing is disabled.
             *
             *  Value cannot be a finalizable object, as they have been made
             *  temporarily reachable for this round.
             */

            DUK_DDD(DUK_DDDPRINT("unreachable object, refcount finalize before sweeping: %p", (void *) hdr));

            /* Finalize using heap->heap_thread; DECREF has a
             * suppress check for mark-and-sweep which is based
             * on heap->ms_running.
             */
            duk_heaphdr_refcount_finalize_norz(heap, hdr);
        }

        hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr);
    }
}
#endif  /* DUK_USE_REFERENCE_COUNTING */

/*
 *  Clear (reachable) flags of finalize_list.
 *
 *  We could mostly do in the sweep phase when we move objects from the
 *  heap into the finalize_list.  However, if a finalizer run is skipped
 *  during a mark-and-sweep, the objects on the finalize_list will be marked
 *  reachable during the next mark-and-sweep.  Since they're already on the
 *  finalize_list, no-one will be clearing their REACHABLE flag so we do it
 *  here.  (This now overlaps with the sweep handling in a harmless way.)
 */

#if defined(DUK_USE_FINALIZER_SUPPORT)
DUK_LOCAL void duk__clear_finalize_list_flags(duk_heap *heap) {
    duk_heaphdr *hdr;

    DUK_DD(DUK_DDPRINT("duk__clear_finalize_list_flags: %p", (void *) heap));

    hdr = heap->finalize_list;
    while (hdr) {
        DUK_HEAPHDR_CLEAR_REACHABLE(hdr);
#if defined(DUK_USE_ASSERTIONS)
        DUK_ASSERT(DUK_HEAPHDR_HAS_FINALIZABLE(hdr) || \
                   (heap->currently_finalizing == hdr));
#endif
        /* DUK_HEAPHDR_FLAG_FINALIZED may be set. */
        DUK_ASSERT(!DUK_HEAPHDR_HAS_TEMPROOT(hdr));
        hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr);
    }
}
#endif  /* DUK_USE_FINALIZER_SUPPORT */

/*
 *  Sweep stringtable.
 */

DUK_LOCAL void duk__sweep_stringtable(duk_heap *heap, duk_size_t *out_count_keep) {
    duk_hstring *h;
    duk_hstring *prev;
    duk_uint32_t i;
#if defined(DUK_USE_DEBUG)
    duk_size_t count_free = 0;
#endif
    duk_size_t count_keep = 0;

    DUK_DD(DUK_DDPRINT("duk__sweep_stringtable: %p", (void *) heap));

#if defined(DUK_USE_STRTAB_PTRCOMP)
    if (heap->strtable16 == NULL) {
#else
    if (heap->strtable == NULL) {
#endif
        goto done;
    }

    for (i = 0; i < heap->st_size; i++) {
#if defined(DUK_USE_STRTAB_PTRCOMP)
        h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, heap->strtable16[i]);
#else
        h = heap->strtable[i];
#endif
        prev = NULL;
        while (h != NULL) {
            duk_hstring *next;
            next = h->hdr.h_next;

            if (DUK_HEAPHDR_HAS_REACHABLE((duk_heaphdr *) h))
            {
                DUK_HEAPHDR_CLEAR_REACHABLE((duk_heaphdr *) h);
                count_keep++;
                prev = h;
            } else {
#if defined(DUK_USE_DEBUG)
                count_free++;
#endif

                /* For pinned strings the refcount has been
                 * bumped.  We could unbump it here before
                 * freeing, but that's actually not necessary
                 * except for assertions.
                 */
#if 0
                if (DUK_HSTRING_HAS_PINNED_LITERAL(h)) {
                    DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) > 0U);
                    DUK_HSTRING_DECREF_NORZ(heap->heap_thread, h);
                    DUK_HSTRING_CLEAR_PINNED_LITERAL(h);
                }
#endif
#if defined(DUK_USE_REFERENCE_COUNTING)
                /* Non-zero refcounts should not happen for unreachable strings,
                 * because we refcount finalize all unreachable objects which
                 * should have decreased unreachable string refcounts to zero
                 * (even for cycles).  However, pinned strings have a +1 bump.
                 */
                DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) ==
                           DUK_HSTRING_HAS_PINNED_LITERAL(h) ? 1U : 0U);
#endif

                /* Deal with weak references first. */
                duk_heap_strcache_string_remove(heap, (duk_hstring *) h);

                /* Remove the string from the string table. */
                duk_heap_strtable_unlink_prev(heap, (duk_hstring *) h, (duk_hstring *) prev);

                /* Free inner references (these exist e.g. when external
                 * strings are enabled) and the struct itself.
                 */
                duk_free_hstring(heap, (duk_hstring *) h);

                /* Don't update 'prev'; it should be last string kept. */
            }

            h = next;
        }
    }

 done:
#if defined(DUK_USE_DEBUG)
    DUK_D(DUK_DPRINT("mark-and-sweep sweep stringtable: %ld freed, %ld kept",
                     (long) count_free, (long) count_keep));
#endif
    *out_count_keep = count_keep;
}

/*
 *  Sweep heap.
 */

DUK_LOCAL void duk__sweep_heap(duk_heap *heap, duk_small_uint_t flags, duk_size_t *out_count_keep) {
    duk_heaphdr *prev;  /* last element that was left in the heap */
    duk_heaphdr *curr;
    duk_heaphdr *next;
#if defined(DUK_USE_DEBUG)
    duk_size_t count_free = 0;
    duk_size_t count_finalize = 0;
    duk_size_t count_rescue = 0;
#endif
    duk_size_t count_keep = 0;

    DUK_DD(DUK_DDPRINT("duk__sweep_heap: %p", (void *) heap));

    prev = NULL;
    curr = heap->heap_allocated;
    heap->heap_allocated = NULL;
    while (curr) {
        /* Strings and ROM objects are never placed on the heap allocated list. */
        DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) != DUK_HTYPE_STRING);
        DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(curr));

        next = DUK_HEAPHDR_GET_NEXT(heap, curr);

        if (DUK_HEAPHDR_HAS_REACHABLE(curr)) {
            /*
             *  Reachable object:
             *    - If FINALIZABLE -> actually unreachable (but marked
             *      artificially reachable), queue to finalize_list.
             *    - If !FINALIZABLE but FINALIZED -> rescued after
             *      finalizer execution.
             *    - Otherwise just a normal, reachable object.
             *
             *  Objects which are kept are queued to heap_allocated
             *  tail (we're essentially filtering heap_allocated in
             *  practice).
             */

#if defined(DUK_USE_FINALIZER_SUPPORT)
            if (DUK_UNLIKELY(DUK_HEAPHDR_HAS_FINALIZABLE(curr))) {
                DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr));
                DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT);
                DUK_DD(DUK_DDPRINT("sweep; reachable, finalizable --> move to finalize_list: %p", (void *) curr));

#if defined(DUK_USE_REFERENCE_COUNTING)
                DUK_HEAPHDR_PREINC_REFCOUNT(curr);  /* Bump refcount so that refzero never occurs when pending a finalizer call. */
#endif
                DUK_HEAP_INSERT_INTO_FINALIZE_LIST(heap, curr);
#if defined(DUK_USE_DEBUG)
                count_finalize++;
#endif
            }
            else
#endif  /* DUK_USE_FINALIZER_SUPPORT */
            {
                if (DUK_UNLIKELY(DUK_HEAPHDR_HAS_FINALIZED(curr))) {
                    DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(curr));
                    DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT);

                    if (flags & DUK_MS_FLAG_POSTPONE_RESCUE) {
                        DUK_DD(DUK_DDPRINT("sweep; reachable, finalized, but postponing rescue decisions --> keep object (with FINALIZED set): %!iO", curr));
                        count_keep++;
                    } else {
                        DUK_DD(DUK_DDPRINT("sweep; reachable, finalized --> rescued after finalization: %p", (void *) curr));
#if defined(DUK_USE_FINALIZER_SUPPORT)
                        DUK_HEAPHDR_CLEAR_FINALIZED(curr);
#endif
#if defined(DUK_USE_DEBUG)
                        count_rescue++;
#endif
                    }
                } else {
                    DUK_DD(DUK_DDPRINT("sweep; reachable --> keep: %!iO", curr));
                    count_keep++;
                }

                if (prev != NULL) {
                    DUK_ASSERT(heap->heap_allocated != NULL);
                    DUK_HEAPHDR_SET_NEXT(heap, prev, curr);
                } else {
                    DUK_ASSERT(heap->heap_allocated == NULL);
                    heap->heap_allocated = curr;
                }
#if defined(DUK_USE_DOUBLE_LINKED_HEAP)
                DUK_HEAPHDR_SET_PREV(heap, curr, prev);
#endif
                DUK_HEAPHDR_ASSERT_LINKS(heap, prev);
                DUK_HEAPHDR_ASSERT_LINKS(heap, curr);
                prev = curr;
            }

            /*
             *  Shrink check for value stacks here.  We're inside
             *  ms_prevent_count protection which prevents recursive
             *  mark-and-sweep and refzero finalizers, so there are
             *  no side effects that would affect the heap lists.
             */
            if (DUK_HEAPHDR_IS_OBJECT(curr) && DUK_HOBJECT_IS_THREAD((duk_hobject *) curr)) {
                duk_hthread *thr_curr = (duk_hthread *) curr;
                DUK_DD(DUK_DDPRINT("value stack shrink check for thread: %!O", curr));
                duk_valstack_shrink_check_nothrow(thr_curr, flags & DUK_MS_FLAG_EMERGENCY /*snug*/);
            }

            DUK_HEAPHDR_CLEAR_REACHABLE(curr);
            /* Keep FINALIZED if set, used if rescue decisions are postponed. */
            /* Keep FINALIZABLE for objects on finalize_list. */
            DUK_ASSERT(!DUK_HEAPHDR_HAS_REACHABLE(curr));
        } else {
            /*
             *  Unreachable object:
             *    - If FINALIZED, object was finalized but not
             *      rescued.  This doesn't affect freeing.
             *    - Otherwise normal unreachable object.
             *
             *  There's no guard preventing a FINALIZED object
             *  from being freed while finalizers execute: the
             *  artificial finalize_list reachability roots can't
             *  cause an incorrect free decision (but can cause
             *  an incorrect rescue decision).
             */

#if defined(DUK_USE_REFERENCE_COUNTING)
            /* Non-zero refcounts should not happen because we refcount
             * finalize all unreachable objects which should cancel out
             * refcounts (even for cycles).
             */
            DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(curr) == 0);
#endif
            DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(curr));

#if defined(DUK_USE_DEBUG)
            if (DUK_HEAPHDR_HAS_FINALIZED(curr)) {
                DUK_DD(DUK_DDPRINT("sweep; unreachable, finalized --> finalized object not rescued: %p", (void *) curr));
            } else {
                DUK_DD(DUK_DDPRINT("sweep; not reachable --> free: %p", (void *) curr));
            }

#endif

            /* Note: object cannot be a finalizable unreachable object, as
             * they have been marked temporarily reachable for this round,
             * and are handled above.
             */

#if defined(DUK_USE_DEBUG)
            count_free++;
#endif

            /* Weak refs should be handled here, but no weak refs for
             * any non-string objects exist right now.
             */

            /* Free object and all auxiliary (non-heap) allocs. */
            duk_heap_free_heaphdr_raw(heap, curr);
        }

        curr = next;
    }

    if (prev != NULL) {
        DUK_HEAPHDR_SET_NEXT(heap, prev, NULL);
    }
    DUK_HEAPHDR_ASSERT_LINKS(heap, prev);

#if defined(DUK_USE_DEBUG)
    DUK_D(DUK_DPRINT("mark-and-sweep sweep objects (non-string): %ld freed, %ld kept, %ld rescued, %ld queued for finalization",
                     (long) count_free, (long) count_keep, (long) count_rescue, (long) count_finalize));
#endif
    *out_count_keep = count_keep;
}

/*
 *  Litcache helpers.
 */

#if defined(DUK_USE_LITCACHE_SIZE)
DUK_LOCAL void duk__wipe_litcache(duk_heap *heap) {
    duk_uint_t i;
    duk_litcache_entry *e;

    e = heap->litcache;
    for (i = 0; i < DUK_USE_LITCACHE_SIZE; i++) {
        e->addr = NULL;
        /* e->h does not need to be invalidated: when e->addr is
         * NULL, e->h is considered garbage.
         */
        e++;
    }
}
#endif  /* DUK_USE_LITCACHE_SIZE */

/*
 *  Object compaction.
 *
 *  Compaction is assumed to never throw an error.
 */

DUK_LOCAL int duk__protected_compact_object(duk_hthread *thr, void *udata) {
    duk_hobject *obj;
    /* XXX: for threads, compact stacks? */

    DUK_UNREF(udata);
    obj = duk_known_hobject(thr, -1);
    duk_hobject_compact_props(thr, obj);
    return 0;
}

#if defined(DUK_USE_DEBUG)
DUK_LOCAL void duk__compact_object_list(duk_heap *heap, duk_hthread *thr, duk_heaphdr *start, duk_size_t *p_count_check, duk_size_t *p_count_compact, duk_size_t *p_count_bytes_saved) {
#else
DUK_LOCAL void duk__compact_object_list(duk_heap *heap, duk_hthread *thr, duk_heaphdr *start) {
#endif
    duk_heaphdr *curr;
#if defined(DUK_USE_DEBUG)
    duk_size_t old_size, new_size;
#endif
    duk_hobject *obj;

    DUK_UNREF(heap);

    curr = start;
    while (curr) {
        DUK_DDD(DUK_DDDPRINT("mark-and-sweep compact: %p", (void *) curr));

        if (DUK_HEAPHDR_GET_TYPE(curr) != DUK_HTYPE_OBJECT) {
            goto next;
        }
        obj = (duk_hobject *) curr;

#if defined(DUK_USE_DEBUG)
        old_size = DUK_HOBJECT_P_COMPUTE_SIZE(DUK_HOBJECT_GET_ESIZE(obj),
                                              DUK_HOBJECT_GET_ASIZE(obj),
                                              DUK_HOBJECT_GET_HSIZE(obj));
#endif

        DUK_DD(DUK_DDPRINT("compact object: %p", (void *) obj));
        duk_push_hobject(thr, obj);
        /* XXX: disable error handlers for duration of compaction? */
        duk_safe_call(thr, duk__protected_compact_object, NULL, 1, 0);

#if defined(DUK_USE_DEBUG)
        new_size = DUK_HOBJECT_P_COMPUTE_SIZE(DUK_HOBJECT_GET_ESIZE(obj),
                                              DUK_HOBJECT_GET_ASIZE(obj),
                                              DUK_HOBJECT_GET_HSIZE(obj));
#endif

#if defined(DUK_USE_DEBUG)
        (*p_count_compact)++;
        (*p_count_bytes_saved) += (duk_size_t) (old_size - new_size);
#endif

     next:
        curr = DUK_HEAPHDR_GET_NEXT(heap, curr);
#if defined(DUK_USE_DEBUG)
        (*p_count_check)++;
#endif
    }
}

DUK_LOCAL void duk__compact_objects(duk_heap *heap) {
    /* XXX: which lists should participate?  to be finalized? */
#if defined(DUK_USE_DEBUG)
    duk_size_t count_check = 0;
    duk_size_t count_compact = 0;
    duk_size_t count_bytes_saved = 0;
#endif

    DUK_DD(DUK_DDPRINT("duk__compact_objects: %p", (void *) heap));

    DUK_ASSERT(heap->heap_thread != NULL);

#if defined(DUK_USE_DEBUG)
    duk__compact_object_list(heap, heap->heap_thread, heap->heap_allocated, &count_check, &count_compact, &count_bytes_saved);
#if defined(DUK_USE_FINALIZER_SUPPORT)
    duk__compact_object_list(heap, heap->heap_thread, heap->finalize_list, &count_check, &count_compact, &count_bytes_saved);
#endif
#else
    duk__compact_object_list(heap, heap->heap_thread, heap->heap_allocated);
#if defined(DUK_USE_FINALIZER_SUPPORT)
    duk__compact_object_list(heap, heap->heap_thread, heap->finalize_list);
#endif
#endif
#if defined(DUK_USE_REFERENCE_COUNTING)
    DUK_ASSERT(heap->refzero_list == NULL);  /* Always handled to completion inline in DECREF. */
#endif

#if defined(DUK_USE_DEBUG)
    DUK_D(DUK_DPRINT("mark-and-sweep compact objects: %ld checked, %ld compaction attempts, %ld bytes saved by compaction",
                     (long) count_check, (long) count_compact, (long) count_bytes_saved));
#endif
}

/*
 *  Assertion helpers.
 */

#if defined(DUK_USE_ASSERTIONS)
typedef void (*duk__gc_heaphdr_assert)(duk_heap *heap, duk_heaphdr *h);
typedef void (*duk__gc_hstring_assert)(duk_heap *heap, duk_hstring *h);

DUK_LOCAL void duk__assert_walk_list(duk_heap *heap, duk_heaphdr *start, duk__gc_heaphdr_assert func) {
    duk_heaphdr *curr;
    for (curr = start; curr != NULL; curr = DUK_HEAPHDR_GET_NEXT(heap, curr)) {
        func(heap, curr);
    }
}

DUK_LOCAL void duk__assert_walk_strtable(duk_heap *heap, duk__gc_hstring_assert func) {
    duk_uint32_t i;

    for (i = 0; i < heap->st_size; i++) {
        duk_hstring *h;

#if defined(DUK_USE_STRTAB_PTRCOMP)
        h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, heap->strtable16[i]);
#else
        h = heap->strtable[i];
#endif
        while (h != NULL) {
            func(heap, h);
            h = h->hdr.h_next;
        }
    }
}

DUK_LOCAL void duk__assert_heaphdr_flags_cb(duk_heap *heap, duk_heaphdr *h) {
    DUK_UNREF(heap);
    DUK_ASSERT(!DUK_HEAPHDR_HAS_REACHABLE(h));
    DUK_ASSERT(!DUK_HEAPHDR_HAS_TEMPROOT(h));
    DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(h));
    /* may have FINALIZED */
}
DUK_LOCAL void duk__assert_heaphdr_flags(duk_heap *heap) {
    duk__assert_walk_list(heap, heap->heap_allocated, duk__assert_heaphdr_flags_cb);
#if defined(DUK_USE_REFERENCE_COUNTING)
    DUK_ASSERT(heap->refzero_list == NULL);  /* Always handled to completion inline in DECREF. */
#endif
    /* XXX: Assertions for finalize_list? */
}

DUK_LOCAL void duk__assert_validity_cb1(duk_heap *heap, duk_heaphdr *h) {
    DUK_UNREF(heap);
    DUK_ASSERT(DUK_HEAPHDR_IS_OBJECT(h) || DUK_HEAPHDR_IS_BUFFER(h));
    duk_heaphdr_assert_valid_subclassed(h);
}
DUK_LOCAL void duk__assert_validity_cb2(duk_heap *heap, duk_hstring *h) {
    DUK_UNREF(heap);
    DUK_ASSERT(DUK_HEAPHDR_IS_STRING((duk_heaphdr *) h));
    duk_heaphdr_assert_valid_subclassed((duk_heaphdr *) h);
}
DUK_LOCAL void duk__assert_validity(duk_heap *heap) {
    duk__assert_walk_list(heap, heap->heap_allocated, duk__assert_validity_cb1);
#if defined(DUK_USE_FINALIZER_SUPPORT)
    duk__assert_walk_list(heap, heap->finalize_list, duk__assert_validity_cb1);
#endif
#if defined(DUK_USE_REFERENCE_COUNTING)
    duk__assert_walk_list(heap, heap->refzero_list, duk__assert_validity_cb1);
#endif
    duk__assert_walk_strtable(heap, duk__assert_validity_cb2);
}

#if defined(DUK_USE_REFERENCE_COUNTING)
DUK_LOCAL void duk__assert_valid_refcounts_cb(duk_heap *heap, duk_heaphdr *h) {
    /* Cannot really assert much w.r.t. refcounts now. */

    DUK_UNREF(heap);
    if (DUK_HEAPHDR_GET_REFCOUNT(h) == 0 &&
        DUK_HEAPHDR_HAS_FINALIZED(h)) {
        /* An object may be in heap_allocated list with a zero
         * refcount if it has just been finalized and is waiting
         * to be collected by the next cycle.
         * (This doesn't currently happen however.)
         */
    } else if (DUK_HEAPHDR_GET_REFCOUNT(h) == 0) {
        /* An object may be in heap_allocated list with a zero
         * refcount also if it is a temporary object created
         * during debugger paused state.  It will get collected
         * by mark-and-sweep based on its reachability status
         * (presumably not reachable because refcount is 0).
         */
    }
    DUK_ASSERT_DISABLE(DUK_HEAPHDR_GET_REFCOUNT(h) >= 0);  /* Unsigned. */
}
DUK_LOCAL void duk__assert_valid_refcounts(duk_heap *heap) {
    duk__assert_walk_list(heap, heap->heap_allocated, duk__assert_valid_refcounts_cb);
}

DUK_LOCAL void duk__clear_assert_refcounts_cb1(duk_heap *heap, duk_heaphdr *h) {
    DUK_UNREF(heap);
    h->h_assert_refcount = 0;
}
DUK_LOCAL void duk__clear_assert_refcounts_cb2(duk_heap *heap, duk_hstring *h) {
    DUK_UNREF(heap);
    ((duk_heaphdr *) h)->h_assert_refcount = 0;
}
DUK_LOCAL void duk__clear_assert_refcounts(duk_heap *heap) {
    duk__assert_walk_list(heap, heap->heap_allocated, duk__clear_assert_refcounts_cb1);
#if defined(DUK_USE_FINALIZER_SUPPORT)
    duk__assert_walk_list(heap, heap->finalize_list, duk__clear_assert_refcounts_cb1);
#endif
#if defined(DUK_USE_REFERENCE_COUNTING)
    duk__assert_walk_list(heap, heap->refzero_list, duk__clear_assert_refcounts_cb1);
#endif
    duk__assert_walk_strtable(heap, duk__clear_assert_refcounts_cb2);
}

DUK_LOCAL void duk__check_refcount_heaphdr(duk_heaphdr *hdr) {
    duk_bool_t count_ok;
    duk_size_t expect_refc;

    /* The refcount check only makes sense for reachable objects on
     * heap_allocated or string table, after the sweep phase.  Prior to
     * sweep phase refcounts will include references that are not visible
     * via reachability roots.
     *
     * Because we're called after the sweep phase, all heap objects on
     * heap_allocated are reachable.  REACHABLE flags have already been
     * cleared so we can't check them.
     */

    /* ROM objects have intentionally incorrect refcount (1), but we won't
     * check them.
     */
    DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(hdr));

    expect_refc = hdr->h_assert_refcount;
    if (DUK_HEAPHDR_IS_STRING(hdr) && DUK_HSTRING_HAS_PINNED_LITERAL((duk_hstring *) hdr)) {
        expect_refc++;
    }
    count_ok = ((duk_size_t) DUK_HEAPHDR_GET_REFCOUNT(hdr) == expect_refc);
    if (!count_ok) {
        DUK_D(DUK_DPRINT("refcount mismatch for: %p: header=%ld counted=%ld --> %!iO",
                         (void *) hdr, (long) DUK_HEAPHDR_GET_REFCOUNT(hdr),
                         (long) hdr->h_assert_refcount, hdr));
        DUK_ASSERT(0);
    }
}

DUK_LOCAL void duk__check_assert_refcounts_cb1(duk_heap *heap, duk_heaphdr *h) {
    DUK_UNREF(heap);
    duk__check_refcount_heaphdr(h);
}
DUK_LOCAL void duk__check_assert_refcounts_cb2(duk_heap *heap, duk_hstring *h) {
    DUK_UNREF(heap);
    duk__check_refcount_heaphdr((duk_heaphdr *) h);
}
DUK_LOCAL void duk__check_assert_refcounts(duk_heap *heap) {
    duk__assert_walk_list(heap, heap->heap_allocated, duk__check_assert_refcounts_cb1);
#if defined(DUK_USE_FINALIZER_SUPPORT)
    duk__assert_walk_list(heap, heap->finalize_list, duk__check_assert_refcounts_cb1);
#endif
    /* XXX: Assert anything for refzero_list? */
    duk__assert_walk_strtable(heap, duk__check_assert_refcounts_cb2);
}
#endif  /* DUK_USE_REFERENCE_COUNTING */

#if defined(DUK_USE_LITCACHE_SIZE)
DUK_LOCAL void duk__assert_litcache_nulls(duk_heap *heap) {
    duk_uint_t i;
    duk_litcache_entry *e;

    e = heap->litcache;
    for (i = 0; i < DUK_USE_LITCACHE_SIZE; i++) {
        /* Entry addresses were NULLed before mark-and-sweep, check
         * that they're still NULL afterwards to ensure no pointers
         * were recorded through any side effects.
         */
        DUK_ASSERT(e->addr == NULL);
    }
}
#endif  /* DUK_USE_LITCACHE_SIZE */
#endif  /* DUK_USE_ASSERTIONS */

/*
 *  Stats dump.
 */

#if defined(DUK_USE_DEBUG)
DUK_LOCAL void duk__dump_stats(duk_heap *heap) {
    DUK_D(DUK_DPRINT("stats executor: opcodes=%ld, interrupt=%ld, throw=%ld",
                     (long) heap->stats_exec_opcodes, (long) heap->stats_exec_interrupt,
                     (long) heap->stats_exec_throw));
    DUK_D(DUK_DPRINT("stats call: all=%ld, tailcall=%ld, ecmatoecma=%ld",
                     (long) heap->stats_call_all, (long) heap->stats_call_tailcall,
                     (long) heap->stats_call_ecmatoecma));
    DUK_D(DUK_DPRINT("stats safecall: all=%ld, nothrow=%ld, throw=%ld",
                     (long) heap->stats_safecall_all, (long) heap->stats_safecall_nothrow,
                     (long) heap->stats_safecall_throw));
    DUK_D(DUK_DPRINT("stats mark-and-sweep: try_count=%ld, skip_count=%ld, emergency_count=%ld",
                     (long) heap->stats_ms_try_count, (long) heap->stats_ms_skip_count,
                     (long) heap->stats_ms_emergency_count));
    DUK_D(DUK_DPRINT("stats stringtable: intern_hit=%ld, intern_miss=%ld, "
                     "resize_check=%ld, resize_grow=%ld, resize_shrink=%ld, "
                     "litcache_hit=%ld, litcache_miss=%ld, litcache_pin=%ld",
                     (long) heap->stats_strtab_intern_hit, (long) heap->stats_strtab_intern_miss,
                     (long) heap->stats_strtab_resize_check, (long) heap->stats_strtab_resize_grow,
                     (long) heap->stats_strtab_resize_shrink, (long) heap->stats_strtab_litcache_hit,
                     (long) heap->stats_strtab_litcache_miss, (long) heap->stats_strtab_litcache_pin));
    DUK_D(DUK_DPRINT("stats object: realloc_props=%ld, abandon_array=%ld",
                     (long) heap->stats_object_realloc_props, (long) heap->stats_object_abandon_array));
    DUK_D(DUK_DPRINT("stats getownpropdesc: count=%ld, hit=%ld, miss=%ld",
                     (long) heap->stats_getownpropdesc_count, (long) heap->stats_getownpropdesc_hit,
                     (long) heap->stats_getownpropdesc_miss));
    DUK_D(DUK_DPRINT("stats getpropdesc: count=%ld, hit=%ld, miss=%ld",
                     (long) heap->stats_getpropdesc_count, (long) heap->stats_getpropdesc_hit,
                     (long) heap->stats_getpropdesc_miss));
    DUK_D(DUK_DPRINT("stats getprop: all=%ld, arrayidx=%ld, bufobjidx=%ld, "
                     "bufferidx=%ld, bufferlen=%ld, stringidx=%ld, stringlen=%ld, "
                     "proxy=%ld, arguments=%ld",
                     (long) heap->stats_getprop_all, (long) heap->stats_getprop_arrayidx,
                     (long) heap->stats_getprop_bufobjidx, (long) heap->stats_getprop_bufferidx,
                     (long) heap->stats_getprop_bufferlen, (long) heap->stats_getprop_stringidx,
                     (long) heap->stats_getprop_stringlen, (long) heap->stats_getprop_proxy,
                     (long) heap->stats_getprop_arguments));
    DUK_D(DUK_DPRINT("stats putprop: all=%ld, arrayidx=%ld, bufobjidx=%ld, "
                     "bufferidx=%ld, proxy=%ld",
                     (long) heap->stats_putprop_all, (long) heap->stats_putprop_arrayidx,
                     (long) heap->stats_putprop_bufobjidx, (long) heap->stats_putprop_bufferidx,
                     (long) heap->stats_putprop_proxy));
    DUK_D(DUK_DPRINT("stats getvar: all=%ld",
                     (long) heap->stats_getvar_all));
    DUK_D(DUK_DPRINT("stats putvar: all=%ld",
                     (long) heap->stats_putvar_all));
    DUK_D(DUK_DPRINT("stats envrec: delayedcreate=%ld, create=%ld, newenv=%ld, oldenv=%ld, pushclosure=%ld",
                     (long) heap->stats_envrec_delayedcreate,
                     (long) heap->stats_envrec_create,
                     (long) heap->stats_envrec_newenv,
                     (long) heap->stats_envrec_oldenv,
                     (long) heap->stats_envrec_pushclosure));
}
#endif  /* DUK_USE_DEBUG */

/*
 *  Main mark-and-sweep function.
 *
 *  'flags' represents the features requested by the caller.  The current
 *  heap->ms_base_flags is ORed automatically into the flags; the base flags
 *  mask typically prevents certain mark-and-sweep operation to avoid trouble.
 */

DUK_INTERNAL void duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t flags) {
    duk_size_t count_keep_obj;
    duk_size_t count_keep_str;
#if defined(DUK_USE_VOLUNTARY_GC)
    duk_size_t tmp;
#endif
    duk_bool_t entry_creating_error;

    DUK_STATS_INC(heap, stats_ms_try_count);
#if defined(DUK_USE_DEBUG)
    if (flags & DUK_MS_FLAG_EMERGENCY) {
        DUK_STATS_INC(heap, stats_ms_emergency_count);
    }
#endif

    /* If debugger is paused, garbage collection is disabled by default.
     * This is achieved by bumping ms_prevent_count when becoming paused.
     */
    DUK_ASSERT(!DUK_HEAP_HAS_DEBUGGER_PAUSED(heap) || heap->ms_prevent_count > 0);

    /* Prevention/recursion check as soon as possible because we may
     * be called a number of times when voluntary mark-and-sweep is
     * pending.
     */
    if (heap->ms_prevent_count != 0) {
        DUK_DD(DUK_DDPRINT("reject recursive mark-and-sweep"));
        DUK_STATS_INC(heap, stats_ms_skip_count);
        return;
    }
    DUK_ASSERT(heap->ms_running == 0);  /* ms_prevent_count is bumped when ms_running is set */

    /* Heap_thread is used during mark-and-sweep for refcount finalization
     * (it's also used for finalizer execution once mark-and-sweep is
     * complete).  Heap allocation code ensures heap_thread is set and
     * properly initialized before setting ms_prevent_count to 0.
     */
    DUK_ASSERT(heap->heap_thread != NULL);
    DUK_ASSERT(heap->heap_thread->valstack != NULL);

    DUK_D(DUK_DPRINT("garbage collect (mark-and-sweep) starting, requested flags: 0x%08lx, effective flags: 0x%08lx",
                     (unsigned long) flags, (unsigned long) (flags | heap->ms_base_flags)));

    flags |= heap->ms_base_flags;
#if defined(DUK_USE_FINALIZER_SUPPORT)
    if (heap->finalize_list != NULL) {
        flags |= DUK_MS_FLAG_POSTPONE_RESCUE;
    }
#endif

    /*
     *  Assertions before
     */

#if defined(DUK_USE_ASSERTIONS)
    DUK_ASSERT(heap->ms_prevent_count == 0);
    DUK_ASSERT(heap->ms_running == 0);
    DUK_ASSERT(!DUK_HEAP_HAS_DEBUGGER_PAUSED(heap));
    DUK_ASSERT(!DUK_HEAP_HAS_MARKANDSWEEP_RECLIMIT_REACHED(heap));
    DUK_ASSERT(heap->ms_recursion_depth == 0);
    duk__assert_heaphdr_flags(heap);
    duk__assert_validity(heap);
#if defined(DUK_USE_REFERENCE_COUNTING)
    /* Note: heap->refzero_free_running may be true; a refcount
     * finalizer may trigger a mark-and-sweep.
     */
    duk__assert_valid_refcounts(heap);
#endif  /* DUK_USE_REFERENCE_COUNTING */
#endif  /* DUK_USE_ASSERTIONS */

    /*
     *  Begin
     */

    DUK_ASSERT(heap->ms_prevent_count == 0);
    DUK_ASSERT(heap->ms_running == 0);
    heap->ms_prevent_count = 1;
    heap->ms_running = 1;
    entry_creating_error = heap->creating_error;
    heap->creating_error = 0;

    /*
     *  Free activation/catcher freelists on every mark-and-sweep for now.
     *  This is an initial rough draft; ideally we'd keep count of the
     *  freelist size and free only excess entries.
     */

    DUK_D(DUK_DPRINT("freeing temporary freelists"));
    duk_heap_free_freelists(heap);

    /*
     *  Mark roots, hoping that recursion limit is not normally hit.
     *  If recursion limit is hit, run additional reachability rounds
     *  starting from "temproots" until marking is complete.
     *
     *  Marking happens in two phases: first we mark actual reachability
     *  roots (and run "temproots" to complete the process).  Then we
     *  check which objects are unreachable and are finalizable; such
     *  objects are marked as FINALIZABLE and marked as reachability
     *  (and "temproots" is run again to complete the process).
     *
     *  The heap finalize_list must also be marked as a reachability root.
     *  There may be objects on the list from a previous round if the
     *  previous run had finalizer skip flag.
     */

#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING)
    duk__clear_assert_refcounts(heap);
#endif
#if defined(DUK_USE_LITCACHE_SIZE)
    duk__wipe_litcache(heap);
#endif
    duk__mark_roots_heap(heap);               /* Mark main reachability roots. */
#if defined(DUK_USE_REFERENCE_COUNTING)
    DUK_ASSERT(heap->refzero_list == NULL);   /* Always handled to completion inline in DECREF. */
#endif
    duk__mark_temproots_by_heap_scan(heap);   /* Temproots. */

#if defined(DUK_USE_FINALIZER_SUPPORT)
    duk__mark_finalizable(heap);              /* Mark finalizable as reachability roots. */
    duk__mark_finalize_list(heap);            /* Mark finalizer work list as reachability roots. */
#endif
    duk__mark_temproots_by_heap_scan(heap);   /* Temproots. */

    /*
     *  Sweep garbage and remove marking flags, and move objects with
     *  finalizers to the finalizer work list.
     *
     *  Objects to be swept need to get their refcounts finalized before
     *  they are swept.  In other words, their target object refcounts
     *  need to be decreased.  This has to be done before freeing any
     *  objects to avoid decref'ing dangling pointers (which may happen
     *  even without bugs, e.g. with reference loops)
     *
     *  Because strings don't point to other heap objects, similar
     *  finalization is not necessary for strings.
     */

    /* XXX: more emergency behavior, e.g. find smaller hash sizes etc */

#if defined(DUK_USE_REFERENCE_COUNTING)
    duk__finalize_refcounts(heap);
#endif
    duk__sweep_heap(heap, flags, &count_keep_obj);
    duk__sweep_stringtable(heap, &count_keep_str);
#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING)
    duk__check_assert_refcounts(heap);
#endif
#if defined(DUK_USE_REFERENCE_COUNTING)
    DUK_ASSERT(heap->refzero_list == NULL);   /* Always handled to completion inline in DECREF. */
#endif
#if defined(DUK_USE_FINALIZER_SUPPORT)
    duk__clear_finalize_list_flags(heap);
#endif

    /*
     *  Object compaction (emergency only).
     *
     *  Object compaction is a separate step after sweeping, as there is
     *  more free memory for it to work with.  Also, currently compaction
     *  may insert new objects into the heap allocated list and the string
     *  table which we don't want to do during a sweep (the reachability
     *  flags of such objects would be incorrect).  The objects inserted
     *  are currently:
     *
     *    - a temporary duk_hbuffer for a new properties allocation
     *    - if array part is abandoned, string keys are interned
     *
     *  The object insertions go to the front of the list, so they do not
     *  cause an infinite loop (they are not compacted).
     *
     *  At present compaction is not allowed when mark-and-sweep runs
     *  during error handling because it involves a duk_safe_call()
     *  interfering with error state.
     */

    if ((flags & DUK_MS_FLAG_EMERGENCY) &&
        !(flags & DUK_MS_FLAG_NO_OBJECT_COMPACTION)) {
        if (heap->lj.type != DUK_LJ_TYPE_UNKNOWN) {
            DUK_D(DUK_DPRINT("lj.type (%ld) not DUK_LJ_TYPE_UNKNOWN, skip object compaction", (long) heap->lj.type));
        } else {
            DUK_D(DUK_DPRINT("object compaction"));
            duk__compact_objects(heap);
        }
    }

    /*
     *  String table resize check.
     *
     *  This is mainly useful in emergency GC: if the string table load
     *  factor is really low for some reason, we can shrink the string
     *  table to a smaller size and free some memory in the process.
     *  Only execute in emergency GC.  String table has internal flags
     *  to protect against recursive resizing if this mark-and-sweep pass
     *  was triggered by a string table resize.
     */

    if (flags & DUK_MS_FLAG_EMERGENCY) {
        DUK_D(DUK_DPRINT("stringtable resize check in emergency gc"));
        duk_heap_strtable_force_resize(heap);
    }

    /*
     *  Finish
     */

    DUK_ASSERT(heap->ms_prevent_count == 1);
    DUK_ASSERT(heap->ms_running == 1);
    heap->ms_prevent_count = 0;
    heap->ms_running = 0;
    heap->creating_error = entry_creating_error;  /* for nested error handling, see GH-2278 */

    /*
     *  Assertions after
     */

#if defined(DUK_USE_ASSERTIONS)
    DUK_ASSERT(heap->ms_prevent_count == 0);
    DUK_ASSERT(!DUK_HEAP_HAS_MARKANDSWEEP_RECLIMIT_REACHED(heap));
    DUK_ASSERT(heap->ms_recursion_depth == 0);
    duk__assert_heaphdr_flags(heap);
    duk__assert_validity(heap);
#if defined(DUK_USE_REFERENCE_COUNTING)
    /* Note: heap->refzero_free_running may be true; a refcount
     * finalizer may trigger a mark-and-sweep.
     */
    duk__assert_valid_refcounts(heap);
#endif  /* DUK_USE_REFERENCE_COUNTING */
#if defined(DUK_USE_LITCACHE_SIZE)
    duk__assert_litcache_nulls(heap);
#endif  /* DUK_USE_LITCACHE_SIZE */
#endif  /* DUK_USE_ASSERTIONS */

    /*
     *  Reset trigger counter
     */

#if defined(DUK_USE_VOLUNTARY_GC)
    tmp = (count_keep_obj + count_keep_str) / 256;
    heap->ms_trigger_counter = (duk_int_t) (
        (tmp * DUK_HEAP_MARK_AND_SWEEP_TRIGGER_MULT) +
        DUK_HEAP_MARK_AND_SWEEP_TRIGGER_ADD);
    DUK_D(DUK_DPRINT("garbage collect (mark-and-sweep) finished: %ld objects kept, %ld strings kept, trigger reset to %ld",
                     (long) count_keep_obj, (long) count_keep_str, (long) heap->ms_trigger_counter));
#else
    DUK_D(DUK_DPRINT("garbage collect (mark-and-sweep) finished: %ld objects kept, %ld strings kept, no voluntary trigger",
                     (long) count_keep_obj, (long) count_keep_str));
#endif

    /*
     *  Stats dump
     */

#if defined(DUK_USE_DEBUG)
    duk__dump_stats(heap);
#endif

    /*
     *  Finalize objects in the finalization work list.  Finalized
     *  objects are queued back to heap_allocated with FINALIZED set.
     *
     *  Since finalizers may cause arbitrary side effects, they are
     *  prevented e.g. during string table and object property allocation
     *  resizing using heap->pf_prevent_count.  In this case the objects
     *  remain in the finalization work list after mark-and-sweep exits
     *  and they may be finalized on the next pass or any DECREF checking
     *  for finalize_list.
     *
     *  As of Duktape 2.1 finalization happens outside mark-and-sweep
     *  protection.  Mark-and-sweep is allowed while the finalize_list
     *  is being processed, but no rescue decisions are done while the
     *  process is on-going.  This avoids incorrect rescue decisions
     *  if an object is considered reachable (and thus rescued) because
     *  of a reference via finalize_list (which is considered a reachability
     *  root).  When finalize_list is being processed, reachable objects
     *  with FINALIZED set will just keep their FINALIZED flag for later
     *  mark-and-sweep processing.
     *
     *  This could also be handled (a bit better) by having a more refined
     *  notion of reachability for rescue/free decisions.
     *
     *  XXX: avoid finalizer execution when doing emergency GC?
     */

#if defined(DUK_USE_FINALIZER_SUPPORT)
    /* Attempt to process finalize_list, pf_prevent_count check
     * is inside the target.
     */
    duk_heap_process_finalize_list(heap);
#endif  /* DUK_USE_FINALIZER_SUPPORT */
}
//#line 1 "duk_heap_memory.c"
/*
 *  Memory allocation handling.
 */

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

/*
 *  Allocate memory with garbage collection.
 */

/* Slow path: voluntary GC triggered, first alloc attempt failed, or zero size. */
DUK_LOCAL DUK_NOINLINE_PERF DUK_COLD void *duk__heap_mem_alloc_slowpath(duk_heap *heap, duk_size_t size) {
    void *res;
    duk_small_int_t i;

    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(heap->alloc_func != NULL);
    DUK_ASSERT_DISABLE(size >= 0);

    if (size == 0) {
        DUK_D(DUK_DPRINT("zero size alloc in slow path, return NULL"));
        return NULL;
    }

    DUK_D(DUK_DPRINT("first alloc attempt failed or voluntary GC limit reached, attempt to gc and retry"));

#if 0
    /*
     *  If GC is already running there is no point in attempting a GC
     *  because it will be skipped.  This could be checked for explicitly,
     *  but it isn't actually needed: the loop below will eventually
     *  fail resulting in a NULL.
     */

    if (heap->ms_prevent_count != 0) {
        DUK_D(DUK_DPRINT("duk_heap_mem_alloc() failed, gc in progress (gc skipped), alloc size %ld", (long) size));
        return NULL;
    }
#endif

    /*
     *  Retry with several GC attempts.  Initial attempts are made without
     *  emergency mode; later attempts use emergency mode which minimizes
     *  memory allocations forcibly.
     */

    for (i = 0; i < DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_LIMIT; i++) {
        duk_small_uint_t flags;

        flags = 0;
        if (i >= DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_EMERGENCY_LIMIT - 1) {
            flags |= DUK_MS_FLAG_EMERGENCY;
        }

        duk_heap_mark_and_sweep(heap, flags);

        DUK_ASSERT(size > 0);
        res = heap->alloc_func(heap->heap_udata, size);
        if (res != NULL) {
            DUK_D(DUK_DPRINT("duk_heap_mem_alloc() succeeded after gc (pass %ld), alloc size %ld",
                             (long) (i + 1), (long) size));
            return res;
        }
    }

    DUK_D(DUK_DPRINT("duk_heap_mem_alloc() failed even after gc, alloc size %ld", (long) size));
    return NULL;
}

DUK_INTERNAL DUK_INLINE_PERF DUK_HOT void *duk_heap_mem_alloc(duk_heap *heap, duk_size_t size) {
    void *res;

    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(heap->alloc_func != NULL);
    DUK_ASSERT_DISABLE(size >= 0);

#if defined(DUK_USE_VOLUNTARY_GC)
    /* Voluntary periodic GC (if enabled). */
    if (DUK_UNLIKELY(--(heap)->ms_trigger_counter < 0)) {
        goto slowpath;
    }
#endif

#if defined(DUK_USE_GC_TORTURE)
    /* Simulate alloc failure on every alloc, except when mark-and-sweep
     * is running.
     */
    if (heap->ms_prevent_count == 0) {
        DUK_DDD(DUK_DDDPRINT("gc torture enabled, pretend that first alloc attempt fails"));
        res = NULL;
        DUK_UNREF(res);
        goto slowpath;
    }
#endif

    /* Zero-size allocation should happen very rarely (if at all), so
     * don't check zero size on NULL; handle it in the slow path
     * instead.  This reduces size of inlined code.
     */
    res = heap->alloc_func(heap->heap_udata, size);
    if (DUK_LIKELY(res != NULL)) {
        return res;
    }

 slowpath:

    if (size == 0) {
        DUK_D(DUK_DPRINT("first alloc attempt returned NULL for zero size alloc, use slow path to deal with it"));
    } else {
        DUK_D(DUK_DPRINT("first alloc attempt failed, attempt to gc and retry"));
    }
    return duk__heap_mem_alloc_slowpath(heap, size);
}

DUK_INTERNAL DUK_INLINE_PERF DUK_HOT void *duk_heap_mem_alloc_zeroed(duk_heap *heap, duk_size_t size) {
    void *res;

    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(heap->alloc_func != NULL);
    DUK_ASSERT_DISABLE(size >= 0);

    res = DUK_ALLOC(heap, size);
    if (DUK_LIKELY(res != NULL)) {
        duk_memzero(res, size);
    }
    return res;
}

DUK_INTERNAL DUK_INLINE_PERF DUK_HOT void *duk_heap_mem_alloc_checked(duk_hthread *thr, duk_size_t size) {
    void *res;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT(thr->heap->alloc_func != NULL);

    res = duk_heap_mem_alloc(thr->heap, size);
    if (DUK_LIKELY(res != NULL)) {
        return res;
    } else if (size == 0) {
        DUK_ASSERT(res == NULL);
        return res;
    }
    DUK_ERROR_ALLOC_FAILED(thr);
    DUK_WO_NORETURN(return NULL;);
}

DUK_INTERNAL DUK_INLINE_PERF DUK_HOT void *duk_heap_mem_alloc_checked_zeroed(duk_hthread *thr, duk_size_t size) {
    void *res;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT(thr->heap->alloc_func != NULL);

    res = duk_heap_mem_alloc(thr->heap, size);
    if (DUK_LIKELY(res != NULL)) {
        duk_memzero(res, size);
        return res;
    } else if (size == 0) {
        DUK_ASSERT(res == NULL);
        return res;
    }
    DUK_ERROR_ALLOC_FAILED(thr);
    DUK_WO_NORETURN(return NULL;);
}

/*
 *  Reallocate memory with garbage collection.
 */

/* Slow path: voluntary GC triggered, first realloc attempt failed, or zero size. */
DUK_LOCAL DUK_NOINLINE_PERF DUK_COLD void *duk__heap_mem_realloc_slowpath(duk_heap *heap, void *ptr, duk_size_t newsize) {
    void *res;
    duk_small_int_t i;

    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(heap->realloc_func != NULL);
    /* ptr may be NULL */
    DUK_ASSERT_DISABLE(newsize >= 0);

    /* Newsize was 0 and realloc() returned NULL, this has the semantics
     * of free(oldptr), i.e. memory was successfully freed.
     */
    if (newsize == 0) {
        DUK_D(DUK_DPRINT("zero size realloc in slow path, return NULL"));
        return NULL;
    }

    DUK_D(DUK_DPRINT("first realloc attempt failed, attempt to gc and retry"));

#if 0
    /*
     *  Avoid a GC if GC is already running.  See duk_heap_mem_alloc().
     */

    if (heap->ms_prevent_count != 0) {
        DUK_D(DUK_DPRINT("duk_heap_mem_realloc() failed, gc in progress (gc skipped), alloc size %ld", (long) newsize));
        return NULL;
    }
#endif

    /*
     *  Retry with several GC attempts.  Initial attempts are made without
     *  emergency mode; later attempts use emergency mode which minimizes
     *  memory allocations forcibly.
     */

    for (i = 0; i < DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_LIMIT; i++) {
        duk_small_uint_t flags;

        flags = 0;
        if (i >= DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_EMERGENCY_LIMIT - 1) {
            flags |= DUK_MS_FLAG_EMERGENCY;
        }

        duk_heap_mark_and_sweep(heap, flags);

        DUK_ASSERT(newsize > 0);
        res = heap->realloc_func(heap->heap_udata, ptr, newsize);
        if (res || newsize == 0) {
            DUK_D(DUK_DPRINT("duk_heap_mem_realloc() succeeded after gc (pass %ld), alloc size %ld",
                             (long) (i + 1), (long) newsize));
            return res;
        }
    }

    DUK_D(DUK_DPRINT("duk_heap_mem_realloc() failed even after gc, alloc size %ld", (long) newsize));
    return NULL;
}

DUK_INTERNAL DUK_INLINE_PERF DUK_HOT void *duk_heap_mem_realloc(duk_heap *heap, void *ptr, duk_size_t newsize) {
    void *res;

    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(heap->realloc_func != NULL);
    /* ptr may be NULL */
    DUK_ASSERT_DISABLE(newsize >= 0);

#if defined(DUK_USE_VOLUNTARY_GC)
    /* Voluntary periodic GC (if enabled). */
    if (DUK_UNLIKELY(--(heap)->ms_trigger_counter < 0)) {
        goto slowpath;
    }
#endif

#if defined(DUK_USE_GC_TORTURE)
    /* Simulate alloc failure on every realloc, except when mark-and-sweep
     * is running.
     */
    if (heap->ms_prevent_count == 0) {
        DUK_DDD(DUK_DDDPRINT("gc torture enabled, pretend that first realloc attempt fails"));
        res = NULL;
        DUK_UNREF(res);
        goto slowpath;
    }
#endif

    res = heap->realloc_func(heap->heap_udata, ptr, newsize);
    if (DUK_LIKELY(res != NULL)) {
        return res;
    }

 slowpath:

    if (newsize == 0) {
        DUK_D(DUK_DPRINT("first realloc attempt returned NULL for zero size realloc, use slow path to deal with it"));
    } else {
        DUK_D(DUK_DPRINT("first realloc attempt failed, attempt to gc and retry"));
    }
    return duk__heap_mem_realloc_slowpath(heap, ptr, newsize);
}

/*
 *  Reallocate memory with garbage collection, using a callback to provide
 *  the current allocated pointer.  This variant is used when a mark-and-sweep
 *  (e.g. finalizers) might change the original pointer.
 */

/* Slow path: voluntary GC triggered, first realloc attempt failed, or zero size. */
DUK_LOCAL DUK_NOINLINE_PERF DUK_COLD void *duk__heap_mem_realloc_indirect_slowpath(duk_heap *heap, duk_mem_getptr cb, void *ud, duk_size_t newsize) {
    void *res;
    duk_small_int_t i;

    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(heap->realloc_func != NULL);
    DUK_ASSERT_DISABLE(newsize >= 0);

    if (newsize == 0) {
        DUK_D(DUK_DPRINT("zero size indirect realloc in slow path, return NULL"));
        return NULL;
    }

    DUK_D(DUK_DPRINT("first indirect realloc attempt failed, attempt to gc and retry"));

#if 0
    /*
     *  Avoid a GC if GC is already running.  See duk_heap_mem_alloc().
     */

    if (heap->ms_prevent_count != 0) {
        DUK_D(DUK_DPRINT("duk_heap_mem_realloc_indirect() failed, gc in progress (gc skipped), alloc size %ld", (long) newsize));
        return NULL;
    }
#endif

    /*
     *  Retry with several GC attempts.  Initial attempts are made without
     *  emergency mode; later attempts use emergency mode which minimizes
     *  memory allocations forcibly.
     */

    for (i = 0; i < DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_LIMIT; i++) {
        duk_small_uint_t flags;

#if defined(DUK_USE_DEBUG)
        void *ptr_pre;
        void *ptr_post;
#endif

#if defined(DUK_USE_DEBUG)
        ptr_pre = cb(heap, ud);
#endif
        flags = 0;
        if (i >= DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_EMERGENCY_LIMIT - 1) {
            flags |= DUK_MS_FLAG_EMERGENCY;
        }

        duk_heap_mark_and_sweep(heap, flags);
#if defined(DUK_USE_DEBUG)
        ptr_post = cb(heap, ud);
        if (ptr_pre != ptr_post) {
            DUK_DD(DUK_DDPRINT("realloc base pointer changed by mark-and-sweep: %p -> %p",
                               (void *) ptr_pre, (void *) ptr_post));
        }
#endif

        /* Note: key issue here is to re-lookup the base pointer on every attempt.
         * The pointer being reallocated may change after every mark-and-sweep.
         */

        DUK_ASSERT(newsize > 0);
        res = heap->realloc_func(heap->heap_udata, cb(heap, ud), newsize);
        if (res || newsize == 0) {
            DUK_D(DUK_DPRINT("duk_heap_mem_realloc_indirect() succeeded after gc (pass %ld), alloc size %ld",
                             (long) (i + 1), (long) newsize));
            return res;
        }
    }

    DUK_D(DUK_DPRINT("duk_heap_mem_realloc_indirect() failed even after gc, alloc size %ld", (long) newsize));
    return NULL;
}

DUK_INTERNAL DUK_INLINE_PERF DUK_HOT void *duk_heap_mem_realloc_indirect(duk_heap *heap, duk_mem_getptr cb, void *ud, duk_size_t newsize) {
    void *res;

    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(heap->realloc_func != NULL);
    DUK_ASSERT_DISABLE(newsize >= 0);

#if defined(DUK_USE_VOLUNTARY_GC)
    /* Voluntary periodic GC (if enabled). */
    if (DUK_UNLIKELY(--(heap)->ms_trigger_counter < 0)) {
        goto slowpath;
    }
#endif

#if defined(DUK_USE_GC_TORTURE)
    /* Simulate alloc failure on every realloc, except when mark-and-sweep
     * is running.
     */
    if (heap->ms_prevent_count == 0) {
        DUK_DDD(DUK_DDDPRINT("gc torture enabled, pretend that first indirect realloc attempt fails"));
        res = NULL;
        DUK_UNREF(res);
        goto slowpath;
    }
#endif

    res = heap->realloc_func(heap->heap_udata, cb(heap, ud), newsize);
    if (DUK_LIKELY(res != NULL)) {
        return res;
    }

 slowpath:

    if (newsize == 0) {
        DUK_D(DUK_DPRINT("first indirect realloc attempt returned NULL for zero size realloc, use slow path to deal with it"));
    } else {
        DUK_D(DUK_DPRINT("first indirect realloc attempt failed, attempt to gc and retry"));
    }
    return duk__heap_mem_realloc_indirect_slowpath(heap, cb, ud, newsize);
}

/*
 *  Free memory
 */

DUK_INTERNAL DUK_INLINE_PERF DUK_HOT void duk_heap_mem_free(duk_heap *heap, void *ptr) {
    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(heap->free_func != NULL);
    /* ptr may be NULL */

    /* Must behave like a no-op with NULL and any pointer returned from
     * malloc/realloc with zero size.
     */
    heap->free_func(heap->heap_udata, ptr);

    /* Never perform a GC (even voluntary) in a memory free, otherwise
     * all call sites doing frees would need to deal with the side effects.
     * No need to update voluntary GC counter either.
     */
}
//#line 1 "duk_heap_misc.c"
/*
 *  Support functions for duk_heap.
 */

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

DUK_INTERNAL void duk_heap_insert_into_heap_allocated(duk_heap *heap, duk_heaphdr *hdr) {
    duk_heaphdr *root;

    DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(hdr) != DUK_HTYPE_STRING);

    root = heap->heap_allocated;
#if defined(DUK_USE_DOUBLE_LINKED_HEAP)
    if (root != NULL) {
        DUK_ASSERT(DUK_HEAPHDR_GET_PREV(heap, root) == NULL);
        DUK_HEAPHDR_SET_PREV(heap, root, hdr);
    }
    DUK_HEAPHDR_SET_PREV(heap, hdr, NULL);
#endif
    DUK_HEAPHDR_SET_NEXT(heap, hdr, root);
    DUK_HEAPHDR_ASSERT_LINKS(heap, hdr);
    DUK_HEAPHDR_ASSERT_LINKS(heap, root);
    heap->heap_allocated = hdr;
}

#if defined(DUK_USE_REFERENCE_COUNTING)
DUK_INTERNAL void duk_heap_remove_from_heap_allocated(duk_heap *heap, duk_heaphdr *hdr) {
    duk_heaphdr *prev;
    duk_heaphdr *next;

    /* Strings are in string table. */
    DUK_ASSERT(hdr != NULL);
    DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(hdr) != DUK_HTYPE_STRING);

    /* Target 'hdr' must be in heap_allocated (not e.g. finalize_list).
     * If not, heap lists will become corrupted so assert early for it.
     */
#if defined(DUK_USE_ASSERTIONS)
    {
        duk_heaphdr *tmp;
        for (tmp = heap->heap_allocated; tmp != NULL; tmp = DUK_HEAPHDR_GET_NEXT(heap, tmp)) {
            if (tmp == hdr) {
                break;
            }
        }
        DUK_ASSERT(tmp == hdr);
    }
#endif

    /* Read/write only once to minimize pointer compression calls. */
    prev = DUK_HEAPHDR_GET_PREV(heap, hdr);
    next = DUK_HEAPHDR_GET_NEXT(heap, hdr);

    if (prev != NULL) {
        DUK_ASSERT(heap->heap_allocated != hdr);
        DUK_HEAPHDR_SET_NEXT(heap, prev, next);
    } else {
        DUK_ASSERT(heap->heap_allocated == hdr);
        heap->heap_allocated = next;
    }
    if (next != NULL) {
        DUK_HEAPHDR_SET_PREV(heap, next, prev);
    } else {
        ;
    }
}
#endif  /* DUK_USE_REFERENCE_COUNTING */

#if defined(DUK_USE_FINALIZER_SUPPORT)
DUK_INTERNAL void duk_heap_insert_into_finalize_list(duk_heap *heap, duk_heaphdr *hdr) {
    duk_heaphdr *root;

    root = heap->finalize_list;
#if defined(DUK_USE_DOUBLE_LINKED_HEAP)
    DUK_HEAPHDR_SET_PREV(heap, hdr, NULL);
    if (root != NULL) {
        DUK_ASSERT(DUK_HEAPHDR_GET_PREV(heap, root) == NULL);
        DUK_HEAPHDR_SET_PREV(heap, root, hdr);
    }
#endif
    DUK_HEAPHDR_SET_NEXT(heap, hdr, root);
    DUK_HEAPHDR_ASSERT_LINKS(heap, hdr);
    DUK_HEAPHDR_ASSERT_LINKS(heap, root);
    heap->finalize_list = hdr;
}
#endif  /* DUK_USE_FINALIZER_SUPPORT */

#if defined(DUK_USE_FINALIZER_SUPPORT)
DUK_INTERNAL void duk_heap_remove_from_finalize_list(duk_heap *heap, duk_heaphdr *hdr) {
#if defined(DUK_USE_DOUBLE_LINKED_HEAP)
    duk_heaphdr *next;
    duk_heaphdr *prev;

    next = DUK_HEAPHDR_GET_NEXT(heap, hdr);
    prev = DUK_HEAPHDR_GET_PREV(heap, hdr);
    if (next != NULL) {
        DUK_ASSERT(DUK_HEAPHDR_GET_PREV(heap, next) == hdr);
        DUK_HEAPHDR_SET_PREV(heap, next, prev);
    }
    if (prev == NULL) {
        DUK_ASSERT(hdr == heap->finalize_list);
        heap->finalize_list = next;
    } else {
        DUK_ASSERT(hdr != heap->finalize_list);
        DUK_HEAPHDR_SET_NEXT(heap, prev, next);
    }
#else
    duk_heaphdr *next;
    duk_heaphdr *curr;

    /* Random removal is expensive: we need to locate the previous element
     * because we don't have a 'prev' pointer.
     */
    curr = heap->finalize_list;
    if (curr == hdr) {
        heap->finalize_list = DUK_HEAPHDR_GET_NEXT(heap, curr);
    } else {
        DUK_ASSERT(hdr != heap->finalize_list);
        for (;;) {
            DUK_ASSERT(curr != NULL);  /* Caller responsibility. */

            next = DUK_HEAPHDR_GET_NEXT(heap, curr);
            if (next == hdr) {
                next = DUK_HEAPHDR_GET_NEXT(heap, hdr);
                DUK_HEAPHDR_SET_NEXT(heap, curr, next);
                break;
            }
        }
    }
#endif
}
#endif  /* DUK_USE_FINALIZER_SUPPORT */

#if defined(DUK_USE_ASSERTIONS)
DUK_INTERNAL duk_bool_t duk_heap_in_heap_allocated(duk_heap *heap, duk_heaphdr *ptr) {
    duk_heaphdr *curr;
    DUK_ASSERT(heap != NULL);

    for (curr = heap->heap_allocated; curr != NULL; curr = DUK_HEAPHDR_GET_NEXT(heap, curr)) {
        if (curr == ptr) {
            return 1;
        }
    }
    return 0;
}
#endif  /* DUK_USE_ASSERTIONS */

#if defined(DUK_USE_INTERRUPT_COUNTER)
DUK_INTERNAL void duk_heap_switch_thread(duk_heap *heap, duk_hthread *new_thr) {
    duk_hthread *curr_thr;

    DUK_ASSERT(heap != NULL);

    if (new_thr != NULL) {
        curr_thr = heap->curr_thread;
        if (curr_thr == NULL) {
            /* For initial entry use default value; zero forces an
             * interrupt before executing the first insturction.
             */
            DUK_DD(DUK_DDPRINT("switch thread, initial entry, init default interrupt counter"));
            new_thr->interrupt_counter = 0;
            new_thr->interrupt_init = 0;
        } else {
            /* Copy interrupt counter/init value state to new thread (if any).
             * It's OK for new_thr to be the same as curr_thr.
             */
#if defined(DUK_USE_DEBUG)
            if (new_thr != curr_thr) {
                DUK_DD(DUK_DDPRINT("switch thread, not initial entry, copy interrupt counter"));
            }
#endif
            new_thr->interrupt_counter = curr_thr->interrupt_counter;
            new_thr->interrupt_init = curr_thr->interrupt_init;
        }
    } else {
        DUK_DD(DUK_DDPRINT("switch thread, new thread is NULL, no interrupt counter changes"));
    }

    heap->curr_thread = new_thr;  /* may be NULL */
}
#endif  /* DUK_USE_INTERRUPT_COUNTER */

#if defined(DUK_USE_ASSERTIONS)
DUK_INTERNAL void duk_heap_assert_valid(duk_heap *heap) {
    DUK_ASSERT(heap != NULL);
}
#endif
//#line 1 "duk_heap_refcount.c"
/*
 *  Reference counting implementation.
 *
 *  INCREF/DECREF, finalization and freeing of objects whose refcount reaches
 *  zero (refzero).  These operations are very performance sensitive, so
 *  various small tricks are used in an attempt to maximize speed.
 */

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

#if defined(DUK_USE_REFERENCE_COUNTING)

#if !defined(DUK_USE_DOUBLE_LINKED_HEAP)
#error internal error, reference counting requires a double linked heap
#endif

/*
 *  Heap object refcount finalization.
 *
 *  When an object is about to be freed, all other objects it refers to must
 *  be decref'd.  Refcount finalization does NOT free the object or its inner
 *  allocations (mark-and-sweep shares these helpers), it just manipulates
 *  the refcounts.
 *
 *  Note that any of the DECREFs may cause a refcount to drop to zero.  If so,
 *  the object won't be refzero processed inline, but will just be queued to
 *  refzero_list and processed by an earlier caller working on refzero_list,
 *  eliminating C recursion from even long refzero cascades.  If refzero
 *  finalization is triggered by mark-and-sweep, refzero conditions are ignored
 *  (objects are not even queued to refzero_list) because mark-and-sweep deals
 *  with them; refcounts are still updated so that they remain in sync with
 *  actual references.
 */

DUK_LOCAL void duk__decref_tvals_norz(duk_hthread *thr, duk_tval *tv, duk_idx_t count) {
    DUK_ASSERT(count == 0 || tv != NULL);

    while (count-- > 0) {
        DUK_TVAL_DECREF_NORZ(thr, tv);
        tv++;
    }
}

DUK_INTERNAL void duk_hobject_refcount_finalize_norz(duk_heap *heap, duk_hobject *h) {
    duk_hthread *thr;
    duk_uint_fast32_t i;
    duk_uint_fast32_t n;
    duk_propvalue *p_val;
    duk_tval *p_tv;
    duk_hstring **p_key;
    duk_uint8_t *p_flag;
    duk_hobject *h_proto;

    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(heap->heap_thread != NULL);
    DUK_ASSERT(h);
    DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) h) == DUK_HTYPE_OBJECT);

    thr = heap->heap_thread;
    DUK_ASSERT(thr != NULL);

    p_key = DUK_HOBJECT_E_GET_KEY_BASE(heap, h);
    p_val = DUK_HOBJECT_E_GET_VALUE_BASE(heap, h);
    p_flag = DUK_HOBJECT_E_GET_FLAGS_BASE(heap, h);
    n = DUK_HOBJECT_GET_ENEXT(h);
    while (n-- > 0) {
        duk_hstring *key;

        key = p_key[n];
        if (DUK_UNLIKELY(key == NULL)) {
            continue;
        }
        DUK_HSTRING_DECREF_NORZ(thr, key);
        if (DUK_UNLIKELY(p_flag[n] & DUK_PROPDESC_FLAG_ACCESSOR)) {
            duk_hobject *h_getset;
            h_getset = p_val[n].a.get;
            DUK_ASSERT(h_getset == NULL || DUK_HEAPHDR_IS_OBJECT((duk_heaphdr *) h_getset));
            DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, h_getset);
            h_getset = p_val[n].a.set;
            DUK_ASSERT(h_getset == NULL || DUK_HEAPHDR_IS_OBJECT((duk_heaphdr *) h_getset));
            DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, h_getset);
        } else {
            duk_tval *tv_val;
            tv_val = &p_val[n].v;
            DUK_TVAL_DECREF_NORZ(thr, tv_val);
        }
    }

    p_tv = DUK_HOBJECT_A_GET_BASE(heap, h);
    n = DUK_HOBJECT_GET_ASIZE(h);
    while (n-- > 0) {
        duk_tval *tv_val;
        tv_val = p_tv + n;
        DUK_TVAL_DECREF_NORZ(thr, tv_val);
    }

    /* Hash part is a 'weak reference' and doesn't contribute to refcounts. */

    h_proto = (duk_hobject *) DUK_HOBJECT_GET_PROTOTYPE(heap, h);
    DUK_ASSERT(h_proto == NULL || DUK_HEAPHDR_IS_OBJECT((duk_heaphdr *) h_proto));
    DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, h_proto);

    /* XXX: Object subclass tests are quite awkward at present, ideally
     * we should be able to switch-case here with a dense index (subtype
     * number or something).  For now, fast path plain objects and arrays
     * and bit test the rest individually.
     */

    if (DUK_HOBJECT_HAS_FASTREFS(h)) {
        /* Plain object or array, nothing more to do.  While a
         * duk_harray has additional fields, none of them need
         * DECREF updates.
         */
        DUK_ASSERT(DUK_HOBJECT_ALLOWS_FASTREFS(h));
        return;
    }
    DUK_ASSERT(DUK_HOBJECT_PROHIBITS_FASTREFS(h));

    /* Slow path: special object, start bit checks from most likely. */

    /* XXX: reorg, more common first */
    if (DUK_HOBJECT_IS_COMPFUNC(h)) {
        duk_hcompfunc *f = (duk_hcompfunc *) h;
        duk_tval *tv, *tv_end;
        duk_hobject **funcs, **funcs_end;

        DUK_HCOMPFUNC_ASSERT_VALID(f);

        if (DUK_LIKELY(DUK_HCOMPFUNC_GET_DATA(heap, f) != NULL)) {
            tv = DUK_HCOMPFUNC_GET_CONSTS_BASE(heap, f);
            tv_end = DUK_HCOMPFUNC_GET_CONSTS_END(heap, f);
            while (tv < tv_end) {
                DUK_TVAL_DECREF_NORZ(thr, tv);
                tv++;
            }

            funcs = DUK_HCOMPFUNC_GET_FUNCS_BASE(heap, f);
            funcs_end = DUK_HCOMPFUNC_GET_FUNCS_END(heap, f);
            while (funcs < funcs_end) {
                duk_hobject *h_func;
                h_func = *funcs;
                DUK_ASSERT(h_func != NULL);
                DUK_ASSERT(DUK_HEAPHDR_IS_OBJECT((duk_heaphdr *) h_func));
                DUK_HCOMPFUNC_DECREF_NORZ(thr, (duk_hcompfunc *) h_func);
                funcs++;
            }
        } else {
            /* May happen in some out-of-memory corner cases. */
            DUK_D(DUK_DPRINT("duk_hcompfunc 'data' is NULL, skipping decref"));
        }

        DUK_HEAPHDR_DECREF_ALLOWNULL(thr, (duk_heaphdr *) DUK_HCOMPFUNC_GET_LEXENV(heap, f));
        DUK_HEAPHDR_DECREF_ALLOWNULL(thr, (duk_heaphdr *) DUK_HCOMPFUNC_GET_VARENV(heap, f));
        DUK_HEAPHDR_DECREF_ALLOWNULL(thr, (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(heap, f));
    } else if (DUK_HOBJECT_IS_DECENV(h)) {
        duk_hdecenv *e = (duk_hdecenv *) h;
        DUK_HDECENV_ASSERT_VALID(e);
        DUK_HTHREAD_DECREF_NORZ_ALLOWNULL(thr, e->thread);
        DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, e->varmap);
    } else if (DUK_HOBJECT_IS_OBJENV(h)) {
        duk_hobjenv *e = (duk_hobjenv *) h;
        DUK_HOBJENV_ASSERT_VALID(e);
        DUK_ASSERT(e->target != NULL);  /* Required for object environments. */
        DUK_HOBJECT_DECREF_NORZ(thr, e->target);
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
    } else if (DUK_HOBJECT_IS_BUFOBJ(h)) {
        duk_hbufobj *b = (duk_hbufobj *) h;
        DUK_HBUFOBJ_ASSERT_VALID(b);
        DUK_HBUFFER_DECREF_NORZ_ALLOWNULL(thr, (duk_hbuffer *) b->buf);
        DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, (duk_hobject *) b->buf_prop);
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
    } else if (DUK_HOBJECT_IS_BOUNDFUNC(h)) {
        duk_hboundfunc *f = (duk_hboundfunc *) (void *) h;
        DUK_HBOUNDFUNC_ASSERT_VALID(f);
        DUK_TVAL_DECREF_NORZ(thr, &f->target);
        DUK_TVAL_DECREF_NORZ(thr, &f->this_binding);
        duk__decref_tvals_norz(thr, f->args, f->nargs);
#if defined(DUK_USE_ES6_PROXY)
    } else if (DUK_HOBJECT_IS_PROXY(h)) {
        duk_hproxy *p = (duk_hproxy *) h;
        DUK_HPROXY_ASSERT_VALID(p);
        DUK_HOBJECT_DECREF_NORZ(thr, p->target);
        DUK_HOBJECT_DECREF_NORZ(thr, p->handler);
#endif  /* DUK_USE_ES6_PROXY */
    } else if (DUK_HOBJECT_IS_THREAD(h)) {
        duk_hthread *t = (duk_hthread *) h;
        duk_activation *act;
        duk_tval *tv;

        DUK_HTHREAD_ASSERT_VALID(t);

        tv = t->valstack;
        while (tv < t->valstack_top) {
            DUK_TVAL_DECREF_NORZ(thr, tv);
            tv++;
        }

        for (act = t->callstack_curr; act != NULL; act = act->parent) {
            DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, (duk_hobject *) DUK_ACT_GET_FUNC(act));
            DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, (duk_hobject *) act->var_env);
            DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, (duk_hobject *) act->lex_env);
#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
            DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, (duk_hobject *) act->prev_caller);
#endif
#if 0  /* nothing now */
            for (cat = act->cat; cat != NULL; cat = cat->parent) {
            }
#endif
        }


        for (i = 0; i < DUK_NUM_BUILTINS; i++) {
            DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, (duk_hobject *) t->builtins[i]);
        }

        DUK_HTHREAD_DECREF_NORZ_ALLOWNULL(thr, (duk_hthread *) t->resumer);
    } else {
        /* We may come here if the object should have a FASTREFS flag
         * but it's missing for some reason.  Assert for never getting
         * here; however, other than performance, this is harmless.
         */
        DUK_D(DUK_DPRINT("missing FASTREFS flag for: %!iO", h));
        DUK_ASSERT(0);
    }
}

DUK_INTERNAL void duk_heaphdr_refcount_finalize_norz(duk_heap *heap, duk_heaphdr *hdr) {
    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(heap->heap_thread != NULL);
    DUK_ASSERT(hdr != NULL);

    if (DUK_HEAPHDR_IS_OBJECT(hdr)) {
        duk_hobject_refcount_finalize_norz(heap, (duk_hobject *) hdr);
    }
    /* DUK_HTYPE_BUFFER: nothing to finalize */
    /* DUK_HTYPE_STRING: nothing to finalize */
}

/*
 *  Refzero processing for duk_hobject: queue a refzero'ed object to either
 *  finalize_list or refzero_list and process the relevent list(s) if
 *  necessary.
 *
 *  Refzero_list is single linked, with only 'prev' pointers set and valid.
 *  All 'next' pointers are intentionally left as garbage.  This doesn't
 *  matter because refzero_list is processed to completion before any other
 *  code (like mark-and-sweep) might walk the list.
 *
 *  In more detail:
 *
 *  - On first insert refzero_list is NULL and the new object becomes the
 *    first and only element on the list; duk__refcount_free_pending() is
 *    called and it starts processing the list from the initial element,
 *    i.e. the list tail.
 *
 *  - As each object is refcount finalized, new objects may be queued to
 *    refzero_list head.  Their 'next' pointers are left as garbage, but
 *    'prev' points are set correctly, with the element at refzero_list
 *    having a NULL 'prev' pointer.  The fact that refzero_list is non-NULL
 *    is used to reject (1) recursive duk__refcount_free_pending() and
 *    (2) finalize_list processing calls.
 *
 *  - When we're done with the current object, read its 'prev' pointer and
 *    free the object.  If 'prev' is NULL, we've reached head of list and are
 *    done: set refzero_list to NULL and process pending finalizers.  Otherwise
 *    continue processing the list.
 *
 *  A refzero cascade is free of side effects because it only involves
 *  queueing more objects and freeing memory; finalizer execution is blocked
 *  in the code path queueing objects to finalize_list.  As a result the
 *  initial refzero call (which triggers duk__refcount_free_pending()) must
 *  check finalize_list so that finalizers are executed snappily.
 *
 *  If finalize_list processing starts first, refzero may occur while we're
 *  processing finalizers.  That's fine: that particular refzero cascade is
 *  handled to completion without side effects.  Once the cascade is complete,
 *  we'll run pending finalizers but notice that we're already doing that and
 *  return.
 *
 *  This could be expanded to allow incremental freeing: just bail out
 *  early and resume at a future alloc/decref/refzero.  However, if that
 *  were done, the list structure would need to be kept consistent at all
 *  times, mark-and-sweep would need to handle refzero_list, etc.
 */

DUK_LOCAL void duk__refcount_free_pending(duk_heap *heap) {
    duk_heaphdr *curr;
#if defined(DUK_USE_DEBUG)
    duk_int_t count = 0;
#endif

    DUK_ASSERT(heap != NULL);

    curr = heap->refzero_list;
    DUK_ASSERT(curr != NULL);
    DUK_ASSERT(DUK_HEAPHDR_GET_PREV(heap, curr) == NULL);  /* We're called on initial insert only. */
    /* curr->next is GARBAGE. */

    do {
        duk_heaphdr *prev;

        DUK_DDD(DUK_DDDPRINT("refzero processing %p: %!O", (void *) curr, (duk_heaphdr *) curr));

#if defined(DUK_USE_DEBUG)
        count++;
#endif

        DUK_ASSERT(curr != NULL);
        DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT);  /* currently, always the case */
        /* FINALIZED may be set; don't care about flags here. */

        /* Refcount finalize 'curr'.  Refzero_list must be non-NULL
         * here to prevent recursive entry to duk__refcount_free_pending().
         */
        DUK_ASSERT(heap->refzero_list != NULL);
        duk_hobject_refcount_finalize_norz(heap, (duk_hobject *) curr);

        prev = DUK_HEAPHDR_GET_PREV(heap, curr);
        DUK_ASSERT((prev == NULL && heap->refzero_list == curr) || \
                   (prev != NULL && heap->refzero_list != curr));
        /* prev->next is intentionally not updated and is garbage. */

        duk_free_hobject(heap, (duk_hobject *) curr);  /* Invalidates 'curr'. */

        curr = prev;
    } while (curr != NULL);

    heap->refzero_list = NULL;

    DUK_DD(DUK_DDPRINT("refzero processed %ld objects", (long) count));
}

DUK_LOCAL DUK_INLINE void duk__refcount_refzero_hobject(duk_heap *heap, duk_hobject *obj, duk_bool_t skip_free_pending) {
    duk_heaphdr *hdr;
    duk_heaphdr *root;

    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(heap->heap_thread != NULL);
    DUK_ASSERT(obj != NULL);
    DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) obj) == DUK_HTYPE_OBJECT);

    hdr = (duk_heaphdr *) obj;

    /* Refzero'd objects must be in heap_allocated.  They can't be in
     * finalize_list because all objects on finalize_list have an
     * artificial +1 refcount bump.
     */
#if defined(DUK_USE_ASSERTIONS)
    DUK_ASSERT(duk_heap_in_heap_allocated(heap, (duk_heaphdr *) obj));
#endif

    DUK_HEAP_REMOVE_FROM_HEAP_ALLOCATED(heap, hdr);

#if defined(DUK_USE_FINALIZER_SUPPORT)
    /* This finalizer check MUST BE side effect free.  It should also be
     * as fast as possible because it's applied to every object freed.
     */
    if (DUK_UNLIKELY(DUK_HOBJECT_HAS_FINALIZER_FAST(heap, (duk_hobject *) hdr) != 0U)) {
        /* Special case: FINALIZED may be set if mark-and-sweep queued
         * object for finalization, the finalizer was executed (and
         * FINALIZED set), mark-and-sweep hasn't yet processed the
         * object again, but its refcount drops to zero.  Free without
         * running the finalizer again.
         */
        if (DUK_HEAPHDR_HAS_FINALIZED(hdr)) {
            DUK_D(DUK_DPRINT("refzero'd object has finalizer and FINALIZED is set -> free"));
        } else {
            /* Set FINALIZABLE flag so that all objects on finalize_list
             * will have it set and are thus detectable based on the
             * flag alone.
             */
            DUK_HEAPHDR_SET_FINALIZABLE(hdr);
            DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(hdr));

#if defined(DUK_USE_REFERENCE_COUNTING)
            /* Bump refcount on finalize_list insert so that a
             * refzero can never occur when an object is waiting
             * for its finalizer call.  Refzero might otherwise
             * now happen because we allow duk_push_heapptr() for
             * objects pending finalization.
             */
            DUK_HEAPHDR_PREINC_REFCOUNT(hdr);
#endif
            DUK_HEAP_INSERT_INTO_FINALIZE_LIST(heap, hdr);

            /* Process finalizers unless skipping is explicitly
             * requested (NORZ) or refzero_list is being processed
             * (avoids side effects during a refzero cascade).
             * If refzero_list is processed, the initial refzero
             * call will run pending finalizers when refzero_list
             * is done.
             */
            if (!skip_free_pending && heap->refzero_list == NULL) {
                duk_heap_process_finalize_list(heap);
            }
            return;
        }
    }
#endif  /* DUK_USE_FINALIZER_SUPPORT */

    /* No need to finalize, free object via refzero_list. */

    root = heap->refzero_list;

    DUK_HEAPHDR_SET_PREV(heap, hdr, NULL);
    /* 'next' is left as GARBAGE. */
    heap->refzero_list = hdr;

    if (root == NULL) {
        /* Object is now queued.  Refzero_list was NULL so
         * no-one is currently processing it; do it here.
         * With refzero processing just doing a cascade of
         * free calls, we can process it directly even when
         * NORZ macros are used: there are no side effects.
         */
        duk__refcount_free_pending(heap);
        DUK_ASSERT(heap->refzero_list == NULL);

        /* Process finalizers only after the entire cascade
         * is finished.  In most cases there's nothing to
         * finalize, so fast path check to avoid a call.
         */
#if defined(DUK_USE_FINALIZER_SUPPORT)
        if (!skip_free_pending && DUK_UNLIKELY(heap->finalize_list != NULL)) {
            duk_heap_process_finalize_list(heap);
        }
#endif
    } else {
        DUK_ASSERT(DUK_HEAPHDR_GET_PREV(heap, root) == NULL);
        DUK_HEAPHDR_SET_PREV(heap, root, hdr);

        /* Object is now queued.  Because refzero_list was
         * non-NULL, it's already being processed by someone
         * in the C call stack, so we're done.
         */
    }
}

#if defined(DUK_USE_FINALIZER_SUPPORT)
DUK_INTERNAL DUK_ALWAYS_INLINE void duk_refzero_check_fast(duk_hthread *thr) {
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT(thr->heap->refzero_list == NULL);  /* Processed to completion inline. */

    if (DUK_UNLIKELY(thr->heap->finalize_list != NULL)) {
        duk_heap_process_finalize_list(thr->heap);
    }
}

DUK_INTERNAL void duk_refzero_check_slow(duk_hthread *thr) {
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT(thr->heap->refzero_list == NULL);  /* Processed to completion inline. */

    if (DUK_UNLIKELY(thr->heap->finalize_list != NULL)) {
        duk_heap_process_finalize_list(thr->heap);
    }
}
#endif  /* DUK_USE_FINALIZER_SUPPORT */

/*
 *  Refzero processing for duk_hstring.
 */

DUK_LOCAL DUK_INLINE void duk__refcount_refzero_hstring(duk_heap *heap, duk_hstring *str) {
    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(heap->heap_thread != NULL);
    DUK_ASSERT(str != NULL);
    DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) str) == DUK_HTYPE_STRING);

    duk_heap_strcache_string_remove(heap, str);
    duk_heap_strtable_unlink(heap, str);
    duk_free_hstring(heap, str);
}

/*
 *  Refzero processing for duk_hbuffer.
 */

DUK_LOCAL DUK_INLINE void duk__refcount_refzero_hbuffer(duk_heap *heap, duk_hbuffer *buf) {
    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(heap->heap_thread != NULL);
    DUK_ASSERT(buf != NULL);
    DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) buf) == DUK_HTYPE_BUFFER);

    DUK_HEAP_REMOVE_FROM_HEAP_ALLOCATED(heap, (duk_heaphdr *) buf);
    duk_free_hbuffer(heap, buf);
}

/*
 *  Incref and decref functions.
 *
 *  Decref may trigger immediate refzero handling, which may free and finalize
 *  an arbitrary number of objects (a "DECREF cascade").
 *
 *  Refzero handling is skipped entirely if (1) mark-and-sweep is running or
 *  (2) execution is paused in the debugger.  The objects are left in the heap,
 *  and will be freed by mark-and-sweep or eventual heap destruction.
 *
 *  This is necessary during mark-and-sweep because refcounts are also updated
 *  during the sweep phase (otherwise objects referenced by a swept object
 *  would have incorrect refcounts) which then calls here.  This could be
 *  avoided by using separate decref macros in mark-and-sweep; however,
 *  mark-and-sweep also calls finalizers which would use the ordinary decref
 *  macros anyway.
 *
 *  We can't process refzeros (= free objects) when the debugger is running
 *  as the debugger might make an object unreachable but still continue
 *  inspecting it (or even cause it to be pushed back).  So we must rely on
 *  mark-and-sweep to collect them.
 *
 *  The DUK__RZ_SUPPRESS_CHECK() condition is also used in heap destruction
 *  when running finalizers for remaining objects: the flag prevents objects
 *  from being moved around in heap linked lists while that's being done.
 *
 *  The suppress condition is important to performance.
 */

#define DUK__RZ_SUPPRESS_ASSERT1() do { \
        DUK_ASSERT(thr != NULL); \
        DUK_ASSERT(thr->heap != NULL); \
        /* When mark-and-sweep runs, heap_thread must exist. */ \
        DUK_ASSERT(thr->heap->ms_running == 0 || thr->heap->heap_thread != NULL); \
        /* In normal operation finalizers are executed with ms_running == 0 \
         * so we should never see ms_running == 1 and thr != heap_thread. \
         * In heap destruction finalizers are executed with ms_running != 0 \
         * to e.g. prevent refzero; a special value ms_running == 2 is used \
         * in that case so it can be distinguished from the normal runtime \
         * case, and allows a stronger assertion here (GH-2030). \
         */ \
        DUK_ASSERT(!(thr->heap->ms_running == 1 && thr != thr->heap->heap_thread)); \
        /* We may be called when the heap is initializing and we process \
         * refzeros normally, but mark-and-sweep and finalizers are prevented \
         * if that's the case. \
         */ \
        DUK_ASSERT(thr->heap->heap_initializing == 0 || thr->heap->ms_prevent_count > 0); \
        DUK_ASSERT(thr->heap->heap_initializing == 0 || thr->heap->pf_prevent_count > 0); \
    } while (0)

#if defined(DUK_USE_DEBUGGER_SUPPORT)
#define DUK__RZ_SUPPRESS_ASSERT2() do { \
        /* When debugger is paused, ms_running is set. */ \
        DUK_ASSERT(!DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap) || thr->heap->ms_running != 0); \
    } while (0)
#define DUK__RZ_SUPPRESS_COND()  (heap->ms_running != 0)
#else
#define DUK__RZ_SUPPRESS_ASSERT2() do { } while (0)
#define DUK__RZ_SUPPRESS_COND()  (heap->ms_running != 0)
#endif  /* DUK_USE_DEBUGGER_SUPPORT */

#define DUK__RZ_SUPPRESS_CHECK() do { \
        DUK__RZ_SUPPRESS_ASSERT1(); \
        DUK__RZ_SUPPRESS_ASSERT2(); \
        if (DUK_UNLIKELY(DUK__RZ_SUPPRESS_COND())) { \
            DUK_DDD(DUK_DDDPRINT("refzero handling suppressed (not even queued) when mark-and-sweep running, object: %p", (void *) h)); \
            return; \
        } \
    } while (0)

#define DUK__RZ_STRING() do { \
        duk__refcount_refzero_hstring(heap, (duk_hstring *) h); \
    } while (0)
#define DUK__RZ_BUFFER() do { \
        duk__refcount_refzero_hbuffer(heap, (duk_hbuffer *) h); \
    } while (0)
#define DUK__RZ_OBJECT() do { \
        duk__refcount_refzero_hobject(heap, (duk_hobject *) h, skip_free_pending); \
    } while (0)

/* XXX: test the effect of inlining here vs. NOINLINE in refzero helpers */
#if defined(DUK_USE_FAST_REFCOUNT_DEFAULT)
#define DUK__RZ_INLINE DUK_ALWAYS_INLINE
#else
#define DUK__RZ_INLINE /*nop*/
#endif

DUK_LOCAL DUK__RZ_INLINE void duk__hstring_refzero_helper(duk_hthread *thr, duk_hstring *h) {
    duk_heap *heap;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(h != NULL);
    heap = thr->heap;

    DUK__RZ_SUPPRESS_CHECK();
    DUK__RZ_STRING();
}

DUK_LOCAL DUK__RZ_INLINE void duk__hbuffer_refzero_helper(duk_hthread *thr, duk_hbuffer *h) {
    duk_heap *heap;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(h != NULL);
    heap = thr->heap;

    DUK__RZ_SUPPRESS_CHECK();
    DUK__RZ_BUFFER();
}

DUK_LOCAL DUK__RZ_INLINE void duk__hobject_refzero_helper(duk_hthread *thr, duk_hobject *h, duk_bool_t skip_free_pending) {
    duk_heap *heap;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(h != NULL);
    heap = thr->heap;

    DUK__RZ_SUPPRESS_CHECK();
    DUK__RZ_OBJECT();
}

DUK_LOCAL DUK__RZ_INLINE void duk__heaphdr_refzero_helper(duk_hthread *thr, duk_heaphdr *h, duk_bool_t skip_free_pending) {
    duk_heap *heap;
    duk_small_uint_t htype;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(h != NULL);
    heap = thr->heap;

    htype = (duk_small_uint_t) DUK_HEAPHDR_GET_TYPE(h);
    DUK_DDD(DUK_DDDPRINT("ms_running=%ld, heap_thread=%p", (long) thr->heap->ms_running, thr->heap->heap_thread));
    DUK__RZ_SUPPRESS_CHECK();

    switch (htype) {
    case DUK_HTYPE_STRING:
        /* Strings have no internal references but do have "weak"
         * references in the string cache.  Also note that strings
         * are not on the heap_allocated list like other heap
         * elements.
         */

        DUK__RZ_STRING();
        break;

    case DUK_HTYPE_OBJECT:
        /* Objects have internal references.  Must finalize through
         * the "refzero" work list.
         */

        DUK__RZ_OBJECT();
        break;

    default:
        /* Buffers have no internal references.  However, a dynamic
         * buffer has a separate allocation for the buffer.  This is
         * freed by duk_heap_free_heaphdr_raw().
         */

        DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(h) == DUK_HTYPE_BUFFER);
        DUK__RZ_BUFFER();
        break;
    }
}

DUK_INTERNAL DUK_NOINLINE void duk_heaphdr_refzero(duk_hthread *thr, duk_heaphdr *h) {
    duk__heaphdr_refzero_helper(thr, h, 0 /*skip_free_pending*/);
}

DUK_INTERNAL DUK_NOINLINE void duk_heaphdr_refzero_norz(duk_hthread *thr, duk_heaphdr *h) {
    duk__heaphdr_refzero_helper(thr, h, 1 /*skip_free_pending*/);
}

DUK_INTERNAL DUK_NOINLINE void duk_hstring_refzero(duk_hthread *thr, duk_hstring *h) {
    duk__hstring_refzero_helper(thr, h);
}

DUK_INTERNAL DUK_NOINLINE void duk_hbuffer_refzero(duk_hthread *thr, duk_hbuffer *h) {
    duk__hbuffer_refzero_helper(thr, h);
}

DUK_INTERNAL DUK_NOINLINE void duk_hobject_refzero(duk_hthread *thr, duk_hobject *h) {
    duk__hobject_refzero_helper(thr, h, 0 /*skip_free_pending*/);
}

DUK_INTERNAL DUK_NOINLINE void duk_hobject_refzero_norz(duk_hthread *thr, duk_hobject *h) {
    duk__hobject_refzero_helper(thr, h, 1 /*skip_free_pending*/);
}

#if !defined(DUK_USE_FAST_REFCOUNT_DEFAULT)
DUK_INTERNAL void duk_tval_incref(duk_tval *tv) {
    DUK_ASSERT(tv != NULL);

    if (DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv)) {
        duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv);
        DUK_ASSERT(h != NULL);
        DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h));
        DUK_ASSERT_DISABLE(h->h_refcount >= 0);
        DUK_HEAPHDR_PREINC_REFCOUNT(h);
        DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(h) != 0);  /* No wrapping. */
    }
}

DUK_INTERNAL void duk_tval_decref(duk_hthread *thr, duk_tval *tv) {
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(tv != NULL);

    if (DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv)) {
        duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv);
        DUK_ASSERT(h != NULL);
        DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h));
        DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(h) >= 1);
#if 0
        if (DUK_HEAPHDR_PREDEC_REFCOUNT(h) != 0) {
            return;
        }
        duk_heaphdr_refzero(thr, h);
#else
        duk_heaphdr_decref(thr, h);
#endif
    }
}

DUK_INTERNAL void duk_tval_decref_norz(duk_hthread *thr, duk_tval *tv) {
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(tv != NULL);

    if (DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv)) {
        duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv);
        DUK_ASSERT(h != NULL);
        DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h));
        DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(h) >= 1);
#if 0
        if (DUK_HEAPHDR_PREDEC_REFCOUNT(h) != 0) {
            return;
        }
        duk_heaphdr_refzero_norz(thr, h);
#else
        duk_heaphdr_decref_norz(thr, h);
#endif
    }
}
#endif  /* !DUK_USE_FAST_REFCOUNT_DEFAULT */

#define DUK__DECREF_ASSERTS() do { \
        DUK_ASSERT(thr != NULL); \
        DUK_ASSERT(thr->heap != NULL); \
        DUK_ASSERT(h != NULL); \
        DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID((duk_heaphdr *) h)); \
        DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) >= 1); \
    } while (0)
#if defined(DUK_USE_ROM_OBJECTS)
#define DUK__INCREF_SHARED() do { \
        if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) h)) { \
            return; \
        } \
        DUK_HEAPHDR_PREINC_REFCOUNT((duk_heaphdr *) h); \
        DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) != 0);  /* No wrapping. */ \
    } while (0)
#define DUK__DECREF_SHARED() do { \
        if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) h)) { \
            return; \
        } \
        if (DUK_HEAPHDR_PREDEC_REFCOUNT((duk_heaphdr *) h) != 0) { \
            return; \
        } \
    } while (0)
#else
#define DUK__INCREF_SHARED() do { \
        DUK_HEAPHDR_PREINC_REFCOUNT((duk_heaphdr *) h); \
        DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) != 0);  /* No wrapping. */ \
    } while (0)
#define DUK__DECREF_SHARED() do { \
        if (DUK_HEAPHDR_PREDEC_REFCOUNT((duk_heaphdr *) h) != 0) { \
            return; \
        } \
    } while (0)
#endif

#if !defined(DUK_USE_FAST_REFCOUNT_DEFAULT)
/* This will in practice be inlined because it's just an INC instructions
 * and a bit test + INC when ROM objects are enabled.
 */
DUK_INTERNAL void duk_heaphdr_incref(duk_heaphdr *h) {
    DUK_ASSERT(h != NULL);
    DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h));
    DUK_ASSERT_DISABLE(DUK_HEAPHDR_GET_REFCOUNT(h) >= 0);

    DUK__INCREF_SHARED();
}

DUK_INTERNAL void duk_heaphdr_decref(duk_hthread *thr, duk_heaphdr *h) {
    DUK__DECREF_ASSERTS();
    DUK__DECREF_SHARED();
    duk_heaphdr_refzero(thr, h);

    /* Forced mark-and-sweep when GC torture enabled; this could happen
     * on any DECREF (but not DECREF_NORZ).
     */
    DUK_GC_TORTURE(thr->heap);
}
DUK_INTERNAL void duk_heaphdr_decref_norz(duk_hthread *thr, duk_heaphdr *h) {
    DUK__DECREF_ASSERTS();
    DUK__DECREF_SHARED();
    duk_heaphdr_refzero_norz(thr, h);
}
#endif  /* !DUK_USE_FAST_REFCOUNT_DEFAULT */

#if 0  /* Not needed. */
DUK_INTERNAL void duk_hstring_decref(duk_hthread *thr, duk_hstring *h) {
    DUK__DECREF_ASSERTS();
    DUK__DECREF_SHARED();
    duk_hstring_refzero(thr, h);
}
DUK_INTERNAL void duk_hstring_decref_norz(duk_hthread *thr, duk_hstring *h) {
    DUK__DECREF_ASSERTS();
    DUK__DECREF_SHARED();
    duk_hstring_refzero_norz(thr, h);
}
DUK_INTERNAL void duk_hbuffer_decref(duk_hthread *thr, duk_hbuffer *h) {
    DUK__DECREF_ASSERTS();
    DUK__DECREF_SHARED();
    duk_hbuffer_refzero(thr, h);
}
DUK_INTERNAL void duk_hbuffer_decref_norz(duk_hthread *thr, duk_hbuffer *h) {
    DUK__DECREF_ASSERTS();
    DUK__DECREF_SHARED();
    duk_hbuffer_refzero_norz(thr, h);
}
DUK_INTERNAL void duk_hobject_decref(duk_hthread *thr, duk_hobject *h) {
    DUK__DECREF_ASSERTS();
    DUK__DECREF_SHARED();
    duk_hobject_refzero(thr, h);
}
DUK_INTERNAL void duk_hobject_decref_norz(duk_hthread *thr, duk_hobject *h) {
    DUK__DECREF_ASSERTS();
    DUK__DECREF_SHARED();
    duk_hobject_refzero_norz(thr, h);
}
#endif

#else  /* DUK_USE_REFERENCE_COUNTING */

/* no refcounting */

#endif  /* DUK_USE_REFERENCE_COUNTING */

/* automatic undefs */
#undef DUK__DECREF_ASSERTS
#undef DUK__DECREF_SHARED
#undef DUK__INCREF_SHARED
#undef DUK__RZ_BUFFER
#undef DUK__RZ_INLINE
#undef DUK__RZ_OBJECT
#undef DUK__RZ_STRING
#undef DUK__RZ_SUPPRESS_ASSERT1
#undef DUK__RZ_SUPPRESS_ASSERT2
#undef DUK__RZ_SUPPRESS_CHECK
#undef DUK__RZ_SUPPRESS_COND
//#line 1 "duk_heap_stringcache.c"
/*
 *  String cache.
 *
 *  Provides a cache to optimize indexed string lookups.  The cache keeps
 *  track of (byte offset, char offset) states for a fixed number of strings.
 *  Otherwise we'd need to scan from either end of the string, as we store
 *  strings in (extended) UTF-8.
 */

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

/*
 *  Delete references to given hstring from the heap string cache.
 *
 *  String cache references are 'weak': they are not counted towards
 *  reference counts, nor serve as roots for mark-and-sweep.  When an
 *  object is about to be freed, such references need to be removed.
 */

DUK_INTERNAL void duk_heap_strcache_string_remove(duk_heap *heap, duk_hstring *h) {
    duk_uint_t i;
    for (i = 0; i < DUK_HEAP_STRCACHE_SIZE; i++) {
        duk_strcache_entry *c = heap->strcache + i;
        if (c->h == h) {
            DUK_DD(DUK_DDPRINT("deleting weak strcache reference to hstring %p from heap %p",
                               (void *) h, (void *) heap));
            c->h = NULL;

            /* XXX: the string shouldn't appear twice, but we now loop to the
             * end anyway; if fixed, add a looping assertion to ensure there
             * is no duplicate.
             */
        }
    }
}

/*
 *  String scanning helpers
 *
 *  All bytes other than UTF-8 continuation bytes ([0x80,0xbf]) are
 *  considered to contribute a character.  This must match how string
 *  character length is computed.
 */

DUK_LOCAL const duk_uint8_t *duk__scan_forwards(const duk_uint8_t *p, const duk_uint8_t *q, duk_uint_fast32_t n) {
    while (n > 0) {
        for (;;) {
            p++;
            if (p >= q) {
                return NULL;
            }
            if ((*p & 0xc0) != 0x80) {
                break;
            }
        }
        n--;
    }
    return p;
}

DUK_LOCAL const duk_uint8_t *duk__scan_backwards(const duk_uint8_t *p, const duk_uint8_t *q, duk_uint_fast32_t n) {
    while (n > 0) {
        for (;;) {
            p--;
            if (p < q) {
                return NULL;
            }
            if ((*p & 0xc0) != 0x80) {
                break;
            }
        }
        n--;
    }
    return p;
}

/*
 *  Convert char offset to byte offset
 *
 *  Avoid using the string cache if possible: for ASCII strings byte and
 *  char offsets are equal and for short strings direct scanning may be
 *  better than using the string cache (which may evict a more important
 *  entry).
 *
 *  Typing now assumes 32-bit string byte/char offsets (duk_uint_fast32_t).
 *  Better typing might be to use duk_size_t.
 *
 *  Caller should ensure 'char_offset' is within the string bounds [0,charlen]
 *  (endpoint is inclusive).  If this is not the case, no memory unsafe
 *  behavior will happen but an error will be thrown.
 */

DUK_INTERNAL duk_uint_fast32_t duk_heap_strcache_offset_char2byte(duk_hthread *thr, duk_hstring *h, duk_uint_fast32_t char_offset) {
    duk_heap *heap;
    duk_strcache_entry *sce;
    duk_uint_fast32_t byte_offset;
    duk_uint_t i;
    duk_bool_t use_cache;
    duk_uint_fast32_t dist_start, dist_end, dist_sce;
    duk_uint_fast32_t char_length;
    const duk_uint8_t *p_start;
    const duk_uint8_t *p_end;
    const duk_uint8_t *p_found;

    /*
     *  For ASCII strings, the answer is simple.
     */

    if (DUK_LIKELY(DUK_HSTRING_IS_ASCII(h))) {
        return char_offset;
    }

    char_length = (duk_uint_fast32_t) DUK_HSTRING_GET_CHARLEN(h);
    DUK_ASSERT(char_offset <= char_length);

    if (DUK_LIKELY(DUK_HSTRING_IS_ASCII(h))) {
        /* Must recheck because the 'is ascii' flag may be set
         * lazily.  Alternatively, we could just compare charlen
         * to bytelen.
         */
        return char_offset;
    }

    /*
     *  For non-ASCII strings, we need to scan forwards or backwards
     *  from some starting point.  The starting point may be the start
     *  or end of the string, or some cached midpoint in the string
     *  cache.
     *
     *  For "short" strings we simply scan without checking or updating
     *  the cache.  For longer strings we check and update the cache as
     *  necessary, inserting a new cache entry if none exists.
     */

    DUK_DDD(DUK_DDDPRINT("non-ascii string %p, char_offset=%ld, clen=%ld, blen=%ld",
                         (void *) h, (long) char_offset,
                         (long) DUK_HSTRING_GET_CHARLEN(h),
                         (long) DUK_HSTRING_GET_BYTELEN(h)));

    heap = thr->heap;
    sce = NULL;
    use_cache = (char_length > DUK_HEAP_STRINGCACHE_NOCACHE_LIMIT);

    if (use_cache) {
#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)
        DUK_DDD(DUK_DDDPRINT("stringcache before char2byte (using cache):"));
        for (i = 0; i < DUK_HEAP_STRCACHE_SIZE; i++) {
            duk_strcache_entry *c = heap->strcache + i;
            DUK_DDD(DUK_DDDPRINT("  [%ld] -> h=%p, cidx=%ld, bidx=%ld",
                                 (long) i, (void *) c->h, (long) c->cidx, (long) c->bidx));
        }
#endif

        for (i = 0; i < DUK_HEAP_STRCACHE_SIZE; i++) {
            duk_strcache_entry *c = heap->strcache + i;

            if (c->h == h) {
                sce = c;
                break;
            }
        }
    }

    /*
     *  Scan from shortest distance:
     *    - start of string
     *    - end of string
     *    - cache entry (if exists)
     */

    DUK_ASSERT(DUK_HSTRING_GET_CHARLEN(h) >= char_offset);
    dist_start = char_offset;
    dist_end = char_length - char_offset;
    dist_sce = 0; DUK_UNREF(dist_sce);  /* initialize for debug prints, needed if sce==NULL */

    p_start = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h);
    p_end = (const duk_uint8_t *) (p_start + DUK_HSTRING_GET_BYTELEN(h));
    p_found = NULL;

    if (sce) {
        if (char_offset >= sce->cidx) {
            dist_sce = char_offset - sce->cidx;
            if ((dist_sce <= dist_start) && (dist_sce <= dist_end)) {
                DUK_DDD(DUK_DDDPRINT("non-ascii string, use_cache=%ld, sce=%p:%ld:%ld, "
                                     "dist_start=%ld, dist_end=%ld, dist_sce=%ld => "
                                     "scan forwards from sce",
                                     (long) use_cache, (void *) (sce ? sce->h : NULL),
                                     (sce ? (long) sce->cidx : (long) -1),
                                     (sce ? (long) sce->bidx : (long) -1),
                                     (long) dist_start, (long) dist_end, (long) dist_sce));

                p_found = duk__scan_forwards(p_start + sce->bidx,
                                             p_end,
                                             dist_sce);
                goto scan_done;
            }
        } else {
            dist_sce = sce->cidx - char_offset;
            if ((dist_sce <= dist_start) && (dist_sce <= dist_end)) {
                DUK_DDD(DUK_DDDPRINT("non-ascii string, use_cache=%ld, sce=%p:%ld:%ld, "
                                     "dist_start=%ld, dist_end=%ld, dist_sce=%ld => "
                                     "scan backwards from sce",
                                     (long) use_cache, (void *) (sce ? sce->h : NULL),
                                     (sce ? (long) sce->cidx : (long) -1),
                                     (sce ? (long) sce->bidx : (long) -1),
                                     (long) dist_start, (long) dist_end, (long) dist_sce));

                p_found = duk__scan_backwards(p_start + sce->bidx,
                                              p_start,
                                              dist_sce);
                goto scan_done;
            }
        }
    }

    /* no sce, or sce scan not best */

    if (dist_start <= dist_end) {
        DUK_DDD(DUK_DDDPRINT("non-ascii string, use_cache=%ld, sce=%p:%ld:%ld, "
                             "dist_start=%ld, dist_end=%ld, dist_sce=%ld => "
                             "scan forwards from string start",
                             (long) use_cache, (void *) (sce ? sce->h : NULL),
                             (sce ? (long) sce->cidx : (long) -1),
                             (sce ? (long) sce->bidx : (long) -1),
                             (long) dist_start, (long) dist_end, (long) dist_sce));

        p_found = duk__scan_forwards(p_start,
                                     p_end,
                                     dist_start);
    } else {
        DUK_DDD(DUK_DDDPRINT("non-ascii string, use_cache=%ld, sce=%p:%ld:%ld, "
                             "dist_start=%ld, dist_end=%ld, dist_sce=%ld => "
                             "scan backwards from string end",
                             (long) use_cache, (void *) (sce ? sce->h : NULL),
                             (sce ? (long) sce->cidx : (long) -1),
                             (sce ? (long) sce->bidx : (long) -1),
                             (long) dist_start, (long) dist_end, (long) dist_sce));

        p_found = duk__scan_backwards(p_end,
                                      p_start,
                                      dist_end);
    }

 scan_done:

    if (DUK_UNLIKELY(p_found == NULL)) {
        /* Scan error: this shouldn't normally happen; it could happen if
         * string is not valid UTF-8 data, and clen/blen are not consistent
         * with the scanning algorithm.
         */
        goto scan_error;
    }

    DUK_ASSERT(p_found >= p_start);
    DUK_ASSERT(p_found <= p_end);  /* may be equal */
    byte_offset = (duk_uint32_t) (p_found - p_start);

    DUK_DDD(DUK_DDDPRINT("-> string %p, cidx %ld -> bidx %ld",
                         (void *) h, (long) char_offset, (long) byte_offset));

    /*
     *  Update cache entry (allocating if necessary), and move the
     *  cache entry to the first place (in an "LRU" policy).
     */

    if (use_cache) {
        /* update entry, allocating if necessary */
        if (!sce) {
            sce = heap->strcache + DUK_HEAP_STRCACHE_SIZE - 1;  /* take last entry */
            sce->h = h;
        }
        DUK_ASSERT(sce != NULL);
        sce->bidx = (duk_uint32_t) (p_found - p_start);
        sce->cidx = (duk_uint32_t) char_offset;

        /* LRU: move our entry to first */
        if (sce > &heap->strcache[0]) {
            /*
             *   A                  C
             *   B                  A
             *   C <- sce    ==>    B
             *   D                  D
             */
            duk_strcache_entry tmp;

            tmp = *sce;
            duk_memmove((void *) (&heap->strcache[1]),
                        (const void *) (&heap->strcache[0]),
                        (size_t) (((char *) sce) - ((char *) &heap->strcache[0])));
            heap->strcache[0] = tmp;

            /* 'sce' points to the wrong entry here, but is no longer used */
        }
#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)
        DUK_DDD(DUK_DDDPRINT("stringcache after char2byte (using cache):"));
        for (i = 0; i < DUK_HEAP_STRCACHE_SIZE; i++) {
            duk_strcache_entry *c = heap->strcache + i;
            DUK_DDD(DUK_DDDPRINT("  [%ld] -> h=%p, cidx=%ld, bidx=%ld",
                                 (long) i, (void *) c->h, (long) c->cidx, (long) c->bidx));
        }
#endif
    }

    return byte_offset;

 scan_error:
    DUK_ERROR_INTERNAL(thr);
    DUK_WO_NORETURN(return 0;);
}
//#line 1 "duk_heap_stringtable.c"
/*
 *  Heap string table handling, string interning.
 */

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

/* Resize checks not needed if minsize == maxsize, typical for low memory
 * targets.
 */
#define DUK__STRTAB_RESIZE_CHECK
#if (DUK_USE_STRTAB_MINSIZE == DUK_USE_STRTAB_MAXSIZE)
#undef DUK__STRTAB_RESIZE_CHECK
#endif

#if defined(DUK_USE_STRTAB_PTRCOMP)
#define DUK__HEAPPTR_ENC16(heap,ptr)    DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (ptr))
#define DUK__HEAPPTR_DEC16(heap,val)    DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (val))
#define DUK__GET_STRTABLE(heap)         ((heap)->strtable16)
#else
#define DUK__HEAPPTR_ENC16(heap,ptr)    (ptr)
#define DUK__HEAPPTR_DEC16(heap,val)    (val)
#define DUK__GET_STRTABLE(heap)         ((heap)->strtable)
#endif

#define DUK__STRTAB_U32_MAX_STRLEN      10               /* 4'294'967'295 */

/*
 *  Debug dump stringtable.
 */

#if defined(DUK_USE_DEBUG)
DUK_INTERNAL void duk_heap_strtable_dump(duk_heap *heap) {
#if defined(DUK_USE_STRTAB_PTRCOMP)
    duk_uint16_t *strtable;
#else
    duk_hstring **strtable;
#endif
    duk_uint32_t i;
    duk_hstring *h;
    duk_size_t count_total = 0;
    duk_size_t count_chain;
    duk_size_t count_chain_min = DUK_SIZE_MAX;
    duk_size_t count_chain_max = 0;
    duk_size_t count_len[8];  /* chain lengths from 0 to 7 */

    if (heap == NULL) {
        DUK_D(DUK_DPRINT("string table, heap=NULL"));
        return;
    }

    strtable = DUK__GET_STRTABLE(heap);
    if (strtable == NULL) {
        DUK_D(DUK_DPRINT("string table, strtab=NULL"));
        return;
    }

    duk_memzero((void *) count_len, sizeof(count_len));
    for (i = 0; i < heap->st_size; i++) {
        h = DUK__HEAPPTR_DEC16(heap, strtable[i]);
        count_chain = 0;
        while (h != NULL) {
            count_chain++;
            h = h->hdr.h_next;
        }
        if (count_chain < sizeof(count_len) / sizeof(duk_size_t)) {
            count_len[count_chain]++;
        }
        count_chain_max = (count_chain > count_chain_max ? count_chain : count_chain_max);
        count_chain_min = (count_chain < count_chain_min ? count_chain : count_chain_min);
        count_total += count_chain;
    }

    DUK_D(DUK_DPRINT("string table, strtab=%p, count=%lu, chain min=%lu max=%lu avg=%lf: "
                     "counts: %lu %lu %lu %lu %lu %lu %lu %lu ...",
                     (void *) heap->strtable, (unsigned long) count_total,
                     (unsigned long) count_chain_min, (unsigned long) count_chain_max,
                     (double) count_total / (double) heap->st_size,
                     (unsigned long) count_len[0], (unsigned long) count_len[1],
                     (unsigned long) count_len[2], (unsigned long) count_len[3],
                     (unsigned long) count_len[4], (unsigned long) count_len[5],
                     (unsigned long) count_len[6], (unsigned long) count_len[7]));
}
#endif  /* DUK_USE_DEBUG */

/*
 *  Assertion helper to ensure strtable is populated correctly.
 */

#if defined(DUK_USE_ASSERTIONS)
DUK_LOCAL void duk__strtable_assert_checks(duk_heap *heap) {
#if defined(DUK_USE_STRTAB_PTRCOMP)
    duk_uint16_t *strtable;
#else
    duk_hstring **strtable;
#endif
    duk_uint32_t i;
    duk_hstring *h;
    duk_size_t count = 0;

    DUK_ASSERT(heap != NULL);

    strtable = DUK__GET_STRTABLE(heap);
    if (strtable != NULL) {
        DUK_ASSERT(heap->st_size != 0);
        DUK_ASSERT(heap->st_mask == heap->st_size - 1);

        for (i = 0; i < heap->st_size; i++) {
            h = DUK__HEAPPTR_DEC16(heap, strtable[i]);
            while (h != NULL) {
                DUK_ASSERT((DUK_HSTRING_GET_HASH(h) & heap->st_mask) == i);
                count++;
                h = h->hdr.h_next;
            }
        }
    } else {
        DUK_ASSERT(heap->st_size == 0);
        DUK_ASSERT(heap->st_mask == 0);
    }

#if defined(DUK__STRTAB_RESIZE_CHECK)
    DUK_ASSERT(count == (duk_size_t) heap->st_count);
#endif
}
#endif  /* DUK_USE_ASSERTIONS */

/*
 *  Allocate and initialize a duk_hstring.
 *
 *  Returns a NULL if allocation or initialization fails for some reason.
 *
 *  The string won't be inserted into the string table and isn't tracked in
 *  any way (link pointers will be NULL).  The caller must place the string
 *  into the string table without any risk of a longjmp, otherwise the string
 *  is leaked.
 */

DUK_LOCAL duk_hstring *duk__strtable_alloc_hstring(duk_heap *heap,
                                                   const duk_uint8_t *str,
                                                   duk_uint32_t blen,
                                                   duk_uint32_t strhash,
                                                   const duk_uint8_t *extdata) {
    duk_hstring *res;
    const duk_uint8_t *data;
#if !defined(DUK_USE_HSTRING_ARRIDX)
    duk_uarridx_t dummy;
#endif

    DUK_ASSERT(heap != NULL);
    DUK_UNREF(extdata);

#if defined(DUK_USE_STRLEN16)
    /* If blen <= 0xffffUL, clen is also guaranteed to be <= 0xffffUL. */
    if (blen > 0xffffUL) {
        DUK_D(DUK_DPRINT("16-bit string blen/clen active and blen over 16 bits, reject intern"));
        goto alloc_error;
    }
#endif

    /* XXX: Memzeroing the allocated structure is not really necessary
     * because we could just initialize all fields explicitly (almost
     * all fields are initialized explicitly anyway).
     */
#if defined(DUK_USE_HSTRING_EXTDATA) && defined(DUK_USE_EXTSTR_INTERN_CHECK)
    if (extdata) {
        res = (duk_hstring *) DUK_ALLOC(heap, sizeof(duk_hstring_external));
        if (DUK_UNLIKELY(res == NULL)) {
            goto alloc_error;
        }
        duk_memzero(res, sizeof(duk_hstring_external));
#if defined(DUK_USE_EXPLICIT_NULL_INIT)
        DUK_HEAPHDR_STRING_INIT_NULLS(&res->hdr);
#endif
        DUK_HEAPHDR_SET_TYPE_AND_FLAGS(&res->hdr, DUK_HTYPE_STRING, DUK_HSTRING_FLAG_EXTDATA);

        DUK_ASSERT(extdata[blen] == 0);  /* Application responsibility. */
        data = extdata;
        ((duk_hstring_external *) res)->extdata = extdata;
    } else
#endif  /* DUK_USE_HSTRING_EXTDATA && DUK_USE_EXTSTR_INTERN_CHECK */
    {
        duk_uint8_t *data_tmp;

        /* NUL terminate for convenient C access */
        DUK_ASSERT(sizeof(duk_hstring) + blen + 1 > blen);  /* No wrap, limits ensure. */
        res = (duk_hstring *) DUK_ALLOC(heap, sizeof(duk_hstring) + blen + 1);
        if (DUK_UNLIKELY(res == NULL)) {
            goto alloc_error;
        }
        duk_memzero(res, sizeof(duk_hstring));
#if defined(DUK_USE_EXPLICIT_NULL_INIT)
        DUK_HEAPHDR_STRING_INIT_NULLS(&res->hdr);
#endif
        DUK_HEAPHDR_SET_TYPE_AND_FLAGS(&res->hdr, DUK_HTYPE_STRING, 0);

        data_tmp = (duk_uint8_t *) (res + 1);
        duk_memcpy(data_tmp, str, blen);
        data_tmp[blen] = (duk_uint8_t) 0;
        data = (const duk_uint8_t *) data_tmp;
    }

    DUK_HSTRING_SET_BYTELEN(res, blen);
    DUK_HSTRING_SET_HASH(res, strhash);

    DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(res));
#if defined(DUK_USE_HSTRING_ARRIDX)
    res->arridx = duk_js_to_arrayindex_string(data, blen);
    if (res->arridx != DUK_HSTRING_NO_ARRAY_INDEX) {
#else
    dummy = duk_js_to_arrayindex_string(data, blen);
    if (dummy != DUK_HSTRING_NO_ARRAY_INDEX) {
#endif
        /* Array index strings cannot be symbol strings,
         * and they're always pure ASCII so blen == clen.
         */
        DUK_HSTRING_SET_ARRIDX(res);
        DUK_HSTRING_SET_ASCII(res);
        DUK_ASSERT(duk_unicode_unvalidated_utf8_length(data, (duk_size_t) blen) == blen);
    } else {
        /* Because 'data' is NUL-terminated, we don't need a
         * blen > 0 check here.  For NUL (0x00) the symbol
         * checks will be false.
         */
        if (DUK_UNLIKELY(data[0] >= 0x80U)) {
            if (data[0] <= 0x81) {
                DUK_HSTRING_SET_SYMBOL(res);
            } else if (data[0] == 0x82U || data[0] == 0xffU) {
                DUK_HSTRING_SET_HIDDEN(res);
                DUK_HSTRING_SET_SYMBOL(res);
            }
        }

        /* Using an explicit 'ASCII' flag has larger footprint (one call site
         * only) but is quite useful for the case when there's no explicit
         * 'clen' in duk_hstring.
         *
         * The flag is set lazily for RAM strings.
         */
        DUK_ASSERT(!DUK_HSTRING_HAS_ASCII(res));

#if defined(DUK_USE_HSTRING_LAZY_CLEN)
        /* Charlen initialized to 0, updated on-the-fly. */
#else
        duk_hstring_init_charlen(res);  /* Also sets ASCII flag. */
#endif
    }

    DUK_DDD(DUK_DDDPRINT("interned string, hash=0x%08lx, blen=%ld, has_arridx=%ld, has_extdata=%ld",
                         (unsigned long) DUK_HSTRING_GET_HASH(res),
                         (long) DUK_HSTRING_GET_BYTELEN(res),
                         (long) (DUK_HSTRING_HAS_ARRIDX(res) ? 1 : 0),
                         (long) (DUK_HSTRING_HAS_EXTDATA(res) ? 1 : 0)));

    DUK_ASSERT(res != NULL);
    return res;

 alloc_error:
    return NULL;
}

/*
 *  Grow strtable allocation in-place.
 */

#if defined(DUK__STRTAB_RESIZE_CHECK)
DUK_LOCAL void duk__strtable_grow_inplace(duk_heap *heap) {
    duk_uint32_t new_st_size;
    duk_uint32_t old_st_size;
    duk_uint32_t i;
    duk_hstring *h;
    duk_hstring *next;
    duk_hstring *prev;
#if defined(DUK_USE_STRTAB_PTRCOMP)
    duk_uint16_t *new_ptr;
    duk_uint16_t *new_ptr_high;
#else
    duk_hstring **new_ptr;
    duk_hstring **new_ptr_high;
#endif

    DUK_DD(DUK_DDPRINT("grow in-place: %lu -> %lu", (unsigned long) heap->st_size, (unsigned long) heap->st_size * 2));

    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(heap->st_resizing == 1);
    DUK_ASSERT(heap->st_size >= 2);
    DUK_ASSERT((heap->st_size & (heap->st_size - 1)) == 0);  /* 2^N */
    DUK_ASSERT(DUK__GET_STRTABLE(heap) != NULL);

    DUK_STATS_INC(heap, stats_strtab_resize_grow);

    new_st_size = heap->st_size << 1U;
    DUK_ASSERT(new_st_size > heap->st_size);  /* No overflow. */

    /* Reallocate the strtable first and then work in-place to rehash
     * strings.  We don't need an indirect allocation here: even if GC
     * is triggered to satisfy the allocation, recursive strtable resize
     * is prevented by flags.  This is also why we don't need to use
     * DUK_REALLOC_INDIRECT().
     */

#if defined(DUK_USE_STRTAB_PTRCOMP)
    new_ptr = (duk_uint16_t *) DUK_REALLOC(heap, heap->strtable16, sizeof(duk_uint16_t) * new_st_size);
#else
    new_ptr = (duk_hstring **) DUK_REALLOC(heap, heap->strtable, sizeof(duk_hstring *) * new_st_size);
#endif
    if (DUK_UNLIKELY(new_ptr == NULL)) {
        /* If realloc fails we can continue normally: the string table
         * won't "fill up" although chains will gradually get longer.
         * When string insertions continue, we'll quite soon try again
         * with no special handling.
         */
        DUK_D(DUK_DPRINT("string table grow failed, ignoring"));
        return;
    }
#if defined(DUK_USE_STRTAB_PTRCOMP)
    heap->strtable16 = new_ptr;
#else
    heap->strtable = new_ptr;
#endif

    /* Rehash a single bucket into two separate ones.  When we grow
     * by x2 the highest 'new' bit determines whether a string remains
     * in its old position (bit is 0) or goes to a new one (bit is 1).
     */

    old_st_size = heap->st_size;
    new_ptr_high = new_ptr + old_st_size;
    for (i = 0; i < old_st_size; i++) {
        duk_hstring *new_root;
        duk_hstring *new_root_high;

        h = DUK__HEAPPTR_DEC16(heap, new_ptr[i]);
        new_root = h;
        new_root_high = NULL;

        prev = NULL;
        while (h != NULL) {
            duk_uint32_t mask;

            DUK_ASSERT((DUK_HSTRING_GET_HASH(h) & heap->st_mask) == i);
            next = h->hdr.h_next;

            /* Example: if previous size was 256, previous mask is 0xFF
             * and size is 0x100 which corresponds to the new bit that
             * comes into play.
             */
            DUK_ASSERT(heap->st_mask == old_st_size - 1);
            mask = old_st_size;
            if (DUK_HSTRING_GET_HASH(h) & mask) {
                if (prev != NULL) {
                    prev->hdr.h_next = h->hdr.h_next;
                } else {
                    DUK_ASSERT(h == new_root);
                    new_root = h->hdr.h_next;
                }

                h->hdr.h_next = new_root_high;
                new_root_high = h;
            } else {
                prev = h;
            }
            h = next;
        }

        new_ptr[i] = DUK__HEAPPTR_ENC16(heap, new_root);
        new_ptr_high[i] = DUK__HEAPPTR_ENC16(heap, new_root_high);
    }

    heap->st_size = new_st_size;
    heap->st_mask = new_st_size - 1;

#if defined(DUK_USE_ASSERTIONS)
    duk__strtable_assert_checks(heap);
#endif
}
#endif  /* DUK__STRTAB_RESIZE_CHECK */

/*
 *  Shrink strtable allocation in-place.
 */

#if defined(DUK__STRTAB_RESIZE_CHECK)
DUK_LOCAL void duk__strtable_shrink_inplace(duk_heap *heap) {
    duk_uint32_t new_st_size;
    duk_uint32_t i;
    duk_hstring *h;
    duk_hstring *other;
    duk_hstring *root;
#if defined(DUK_USE_STRTAB_PTRCOMP)
    duk_uint16_t *old_ptr;
    duk_uint16_t *old_ptr_high;
    duk_uint16_t *new_ptr;
#else
    duk_hstring **old_ptr;
    duk_hstring **old_ptr_high;
    duk_hstring **new_ptr;
#endif

    DUK_DD(DUK_DDPRINT("shrink in-place: %lu -> %lu", (unsigned long) heap->st_size, (unsigned long) heap->st_size / 2));

    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(heap->st_resizing == 1);
    DUK_ASSERT(heap->st_size >= 2);
    DUK_ASSERT((heap->st_size & (heap->st_size - 1)) == 0);  /* 2^N */
    DUK_ASSERT(DUK__GET_STRTABLE(heap) != NULL);

    DUK_STATS_INC(heap, stats_strtab_resize_shrink);

    new_st_size = heap->st_size >> 1U;

    /* Combine two buckets into a single one.  When we shrink, one hash
     * bit (highest) disappears.
     */
    old_ptr = DUK__GET_STRTABLE(heap);
    old_ptr_high = old_ptr + new_st_size;
    for (i = 0; i < new_st_size; i++) {
        h = DUK__HEAPPTR_DEC16(heap, old_ptr[i]);
        other = DUK__HEAPPTR_DEC16(heap, old_ptr_high[i]);

        if (h == NULL) {
            /* First chain is empty, so use second one as is. */
            root = other;
        } else {
            /* Find end of first chain, and link in the second. */
            root = h;
            while (h->hdr.h_next != NULL) {
                h = h->hdr.h_next;
            }
            h->hdr.h_next = other;
        }

        old_ptr[i] = DUK__HEAPPTR_ENC16(heap, root);
    }

    heap->st_size = new_st_size;
    heap->st_mask = new_st_size - 1;

    /* The strtable is now consistent and we can realloc safely.  Even
     * if side effects cause string interning or removal the strtable
     * updates are safe.  Recursive resize has been prevented by caller.
     * This is also why we don't need to use DUK_REALLOC_INDIRECT().
     *
     * We assume a realloc() to a smaller size is guaranteed to succeed.
     * It would be relatively straightforward to handle the error by
     * essentially performing a "grow" step to recover.
     */

#if defined(DUK_USE_STRTAB_PTRCOMP)
    new_ptr = (duk_uint16_t *) DUK_REALLOC(heap, heap->strtable16, sizeof(duk_uint16_t) * new_st_size);
    DUK_ASSERT(new_ptr != NULL);
    heap->strtable16 = new_ptr;
#else
    new_ptr = (duk_hstring **) DUK_REALLOC(heap, heap->strtable, sizeof(duk_hstring *) * new_st_size);
    DUK_ASSERT(new_ptr != NULL);
    heap->strtable = new_ptr;
#endif

#if defined(DUK_USE_ASSERTIONS)
    duk__strtable_assert_checks(heap);
#endif
}
#endif  /* DUK__STRTAB_RESIZE_CHECK */

/*
 *  Grow/shrink check.
 */

#if defined(DUK__STRTAB_RESIZE_CHECK)
DUK_LOCAL DUK_COLD DUK_NOINLINE void duk__strtable_resize_check(duk_heap *heap) {
    duk_uint32_t load_factor;  /* fixed point */

    DUK_ASSERT(heap != NULL);
#if defined(DUK_USE_STRTAB_PTRCOMP)
    DUK_ASSERT(heap->strtable16 != NULL);
#else
    DUK_ASSERT(heap->strtable != NULL);
#endif

    DUK_STATS_INC(heap, stats_strtab_resize_check);

    /* Prevent recursive resizing. */
    if (DUK_UNLIKELY(heap->st_resizing != 0U)) {
        DUK_D(DUK_DPRINT("prevent recursive strtable resize"));
        return;
    }

    heap->st_resizing = 1;

    DUK_ASSERT(heap->st_size >= 16U);
    DUK_ASSERT((heap->st_size >> 4U) >= 1);
    load_factor = heap->st_count / (heap->st_size >> 4U);

    DUK_DD(DUK_DDPRINT("resize check string table: size=%lu, count=%lu, load_factor=%lu (fixed point .4; float %lf)",
                       (unsigned long) heap->st_size, (unsigned long) heap->st_count,
                       (unsigned long) load_factor,
                       (double) heap->st_count / (double) heap->st_size));

    if (load_factor >= DUK_USE_STRTAB_GROW_LIMIT) {
        if (heap->st_size >= DUK_USE_STRTAB_MAXSIZE) {
            DUK_DD(DUK_DDPRINT("want to grow strtable (based on load factor) but already maximum size"));
        } else {
            DUK_D(DUK_DPRINT("grow string table: %lu -> %lu", (unsigned long) heap->st_size, (unsigned long) heap->st_size * 2));
#if defined(DUK_USE_DEBUG)
            duk_heap_strtable_dump(heap);
#endif
            duk__strtable_grow_inplace(heap);
        }
    } else if (load_factor <= DUK_USE_STRTAB_SHRINK_LIMIT) {
        if (heap->st_size <= DUK_USE_STRTAB_MINSIZE) {
            DUK_DD(DUK_DDPRINT("want to shrink strtable (based on load factor) but already minimum size"));
        } else {
            DUK_D(DUK_DPRINT("shrink string table: %lu -> %lu", (unsigned long) heap->st_size, (unsigned long) heap->st_size / 2));
#if defined(DUK_USE_DEBUG)
            duk_heap_strtable_dump(heap);
#endif
            duk__strtable_shrink_inplace(heap);
        }
    } else {
        DUK_DD(DUK_DDPRINT("no need for strtable resize"));
    }

    heap->st_resizing = 0;
}
#endif  /* DUK__STRTAB_RESIZE_CHECK */

/*
 *  Torture grow/shrink: unconditionally grow and shrink back.
 */

#if defined(DUK_USE_STRTAB_TORTURE) && defined(DUK__STRTAB_RESIZE_CHECK)
DUK_LOCAL void duk__strtable_resize_torture(duk_heap *heap) {
    duk_uint32_t old_st_size;

    DUK_ASSERT(heap != NULL);

    old_st_size = heap->st_size;
    if (old_st_size >= DUK_USE_STRTAB_MAXSIZE) {
        return;
    }

    heap->st_resizing = 1;
    duk__strtable_grow_inplace(heap);
    if (heap->st_size > old_st_size) {
        duk__strtable_shrink_inplace(heap);
    }
    heap->st_resizing = 0;
}
#endif  /* DUK_USE_STRTAB_TORTURE && DUK__STRTAB_RESIZE_CHECK */

/*
 *  Raw intern; string already checked not to be present.
 */

DUK_LOCAL duk_hstring *duk__strtable_do_intern(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) {
    duk_hstring *res;
    const duk_uint8_t *extdata;
#if defined(DUK_USE_STRTAB_PTRCOMP)
    duk_uint16_t *slot;
#else
    duk_hstring **slot;
#endif

    DUK_DDD(DUK_DDDPRINT("do_intern: heap=%p, str=%p, blen=%lu, strhash=%lx, st_size=%lu, st_count=%lu, load=%lf",
                         (void *) heap, (const void *) str, (unsigned long) blen, (unsigned long) strhash,
                         (unsigned long) heap->st_size, (unsigned long) heap->st_count,
                         (double) heap->st_count / (double) heap->st_size));

    DUK_ASSERT(heap != NULL);

    /* Prevent any side effects on the string table and the caller provided
     * str/blen arguments while interning is in progress.  For example, if
     * the caller provided str/blen from a dynamic buffer, a finalizer
     * might resize or modify that dynamic buffer, invalidating the call
     * arguments.
     *
     * While finalizers must be prevented, mark-and-sweep itself is fine.
     * Recursive string table resize is prevented explicitly here.
     */

    heap->pf_prevent_count++;
    DUK_ASSERT(heap->pf_prevent_count != 0);  /* Wrap. */

#if defined(DUK_USE_STRTAB_TORTURE) && defined(DUK__STRTAB_RESIZE_CHECK)
    duk__strtable_resize_torture(heap);
#endif

    /* String table grow/shrink check.  Because of chaining (and no
     * accumulation issues as with hash probe chains and DELETED
     * markers) there's never a mandatory need to resize right now.
     * Check for the resize only periodically, based on st_count
     * bit pattern.  Because string table removal doesn't do a shrink
     * check, we do that also here.
     *
     * Do the resize and possible grow/shrink before the new duk_hstring
     * has been allocated.  Otherwise we may trigger a GC when the result
     * duk_hstring is not yet strongly referenced.
     */

#if defined(DUK__STRTAB_RESIZE_CHECK)
    if (DUK_UNLIKELY((heap->st_count & DUK_USE_STRTAB_RESIZE_CHECK_MASK) == 0)) {
        duk__strtable_resize_check(heap);
    }
#endif

    /* External string check (low memory optimization). */

#if defined(DUK_USE_HSTRING_EXTDATA) && defined(DUK_USE_EXTSTR_INTERN_CHECK)
    extdata = (const duk_uint8_t *) DUK_USE_EXTSTR_INTERN_CHECK(heap->heap_udata, (void *) DUK_LOSE_CONST(str), (duk_size_t) blen);
#else
    extdata = (const duk_uint8_t *) NULL;
#endif

    /* Allocate and initialize string, not yet linked.  This may cause a
     * GC which may cause other strings to be interned and inserted into
     * the string table before we insert our string.  Finalizer execution
     * is disabled intentionally to avoid a finalizer from e.g. resizing
     * a buffer used as a data area for 'str'.
     */

    res = duk__strtable_alloc_hstring(heap, str, blen, strhash, extdata);

    /* Allow side effects again: GC must be avoided until duk_hstring
     * result (if successful) has been INCREF'd.
     */
    DUK_ASSERT(heap->pf_prevent_count > 0);
    heap->pf_prevent_count--;

    /* Alloc error handling. */

    if (DUK_UNLIKELY(res == NULL)) {
#if defined(DUK_USE_HSTRING_EXTDATA) && defined(DUK_USE_EXTSTR_INTERN_CHECK)
        if (extdata != NULL) {
            DUK_USE_EXTSTR_FREE(heap->heap_udata, (const void *) extdata);
        }
#endif
        return NULL;
    }

    /* Insert into string table. */

#if defined(DUK_USE_STRTAB_PTRCOMP)
    slot = heap->strtable16 + (strhash & heap->st_mask);
#else
    slot = heap->strtable + (strhash & heap->st_mask);
#endif
    DUK_ASSERT(res->hdr.h_next == NULL);  /* This is the case now, but unnecessary zeroing/NULLing. */
    res->hdr.h_next = DUK__HEAPPTR_DEC16(heap, *slot);
    *slot = DUK__HEAPPTR_ENC16(heap, res);

    /* Update string count only for successful inserts. */

#if defined(DUK__STRTAB_RESIZE_CHECK)
    heap->st_count++;
#endif

    /* The duk_hstring is in the string table but is not yet strongly
     * reachable.  Calling code MUST NOT make any allocations or other
     * side effects before the duk_hstring has been INCREF'd and made
     * reachable.
     */

    return res;
}

/*
 *  Intern a string from str/blen, returning either an existing duk_hstring
 *  or adding a new one into the string table.  The input string does -not-
 *  need to be NUL terminated.
 *
 *  The input 'str' argument may point to a Duktape managed data area such as
 *  the data area of a dynamic buffer.  It's crucial to avoid any side effects
 *  that might affect the data area (e.g. resize the dynamic buffer, or write
 *  to the buffer) before the string is fully interned.
 */

#if defined(DUK_USE_ROM_STRINGS)
DUK_LOCAL duk_hstring *duk__strtab_romstring_lookup(duk_heap *heap, const duk_uint8_t *str, duk_size_t blen, duk_uint32_t strhash) {
    duk_size_t lookup_hash;
    duk_hstring *curr;

    DUK_ASSERT(heap != NULL);
    DUK_UNREF(heap);

    lookup_hash = (blen << 4);
    if (blen > 0) {
        lookup_hash += str[0];
    }
    lookup_hash &= 0xff;

    curr = (duk_hstring *) DUK_LOSE_CONST(duk_rom_strings_lookup[lookup_hash]);
    while (curr != NULL) {
        /* Unsafe memcmp() because for zero blen, str may be NULL. */
        if (strhash == DUK_HSTRING_GET_HASH(curr) &&
            blen == DUK_HSTRING_GET_BYTELEN(curr) &&
            duk_memcmp_unsafe((const void *) str, (const void *) DUK_HSTRING_GET_DATA(curr), blen) == 0) {
            DUK_DDD(DUK_DDDPRINT("intern check: rom string: %!O, computed hash 0x%08lx, rom hash 0x%08lx",
                                 curr, (unsigned long) strhash, (unsigned long) DUK_HSTRING_GET_HASH(curr)));
            return curr;
        }
        curr = curr->hdr.h_next;
    }

    return NULL;
}
#endif  /* DUK_USE_ROM_STRINGS */

DUK_INTERNAL duk_hstring *duk_heap_strtable_intern(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen) {
    duk_uint32_t strhash;
    duk_hstring *h;

    DUK_DDD(DUK_DDDPRINT("intern check: heap=%p, str=%p, blen=%lu", (void *) heap, (const void *) str, (unsigned long) blen));

    /* Preliminaries. */

    /* XXX: maybe just require 'str != NULL' even for zero size? */
    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(blen == 0 || str != NULL);
    DUK_ASSERT(blen <= DUK_HSTRING_MAX_BYTELEN);  /* Caller is responsible for ensuring this. */
    strhash = duk_heap_hashstring(heap, str, (duk_size_t) blen);

    /* String table lookup. */

    DUK_ASSERT(DUK__GET_STRTABLE(heap) != NULL);
    DUK_ASSERT(heap->st_size > 0);
    DUK_ASSERT(heap->st_size == heap->st_mask + 1);
#if defined(DUK_USE_STRTAB_PTRCOMP)
    h = DUK__HEAPPTR_DEC16(heap, heap->strtable16[strhash & heap->st_mask]);
#else
    h = heap->strtable[strhash & heap->st_mask];
#endif
    while (h != NULL) {
        if (DUK_HSTRING_GET_HASH(h) == strhash &&
            DUK_HSTRING_GET_BYTELEN(h) == blen &&
            duk_memcmp_unsafe((const void *) str, (const void *) DUK_HSTRING_GET_DATA(h), (size_t) blen) == 0) {
            /* Found existing entry. */
            DUK_STATS_INC(heap, stats_strtab_intern_hit);
            return h;
        }
        h = h->hdr.h_next;
    }

    /* ROM table lookup.  Because this lookup is slower, do it only after
     * RAM lookup.  This works because no ROM string is ever interned into
     * the RAM string table.
     */

#if defined(DUK_USE_ROM_STRINGS)
    h = duk__strtab_romstring_lookup(heap, str, blen, strhash);
    if (h != NULL) {
        DUK_STATS_INC(heap, stats_strtab_intern_hit);
        return h;
    }
#endif

    /* Not found in string table; insert. */

    DUK_STATS_INC(heap, stats_strtab_intern_miss);
    h = duk__strtable_do_intern(heap, str, blen, strhash);
    return h;  /* may be NULL */
}

/*
 *  Intern a string from u32.
 */

/* XXX: Could arrange some special handling because we know that the result
 * will have an arridx flag and an ASCII flag, won't need a clen check, etc.
 */

DUK_INTERNAL duk_hstring *duk_heap_strtable_intern_u32(duk_heap *heap, duk_uint32_t val) {
    duk_uint8_t buf[DUK__STRTAB_U32_MAX_STRLEN];
    duk_uint8_t *p;

    DUK_ASSERT(heap != NULL);

    /* This is smaller and faster than a %lu sprintf. */
    p = buf + sizeof(buf);
    do {
        p--;
        *p = duk_lc_digits[val % 10];
        val = val / 10;
    } while (val != 0);  /* For val == 0, emit exactly one '0'. */
    DUK_ASSERT(p >= buf);

    return duk_heap_strtable_intern(heap, (const duk_uint8_t *) p, (duk_uint32_t) ((buf + sizeof(buf)) - p));
}

/*
 *  Checked convenience variants.
 *
 *  XXX: Because the main use case is for the checked variants, make them the
 *  main functionality and provide a safe variant separately (it is only needed
 *  during heap init).  The problem with that is that longjmp state and error
 *  creation must already be possible to throw.
 */

DUK_INTERNAL duk_hstring *duk_heap_strtable_intern_checked(duk_hthread *thr, const duk_uint8_t *str, duk_uint32_t blen) {
    duk_hstring *res;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT(blen == 0 || str != NULL);

    res = duk_heap_strtable_intern(thr->heap, str, blen);
    if (DUK_UNLIKELY(res == NULL)) {
        DUK_ERROR_ALLOC_FAILED(thr);
        DUK_WO_NORETURN(return NULL;);
    }
    return res;
}

#if defined(DUK_USE_LITCACHE_SIZE)
DUK_LOCAL duk_uint_t duk__strtable_litcache_key(const duk_uint8_t *str, duk_uint32_t blen) {
    duk_uintptr_t key;

    DUK_ASSERT(DUK_USE_LITCACHE_SIZE > 0);
    DUK_ASSERT(DUK_IS_POWER_OF_TWO((duk_uint_t) DUK_USE_LITCACHE_SIZE));

    key = (duk_uintptr_t) blen ^ (duk_uintptr_t) str;
    key &= (duk_uintptr_t) (DUK_USE_LITCACHE_SIZE - 1);  /* Assumes size is power of 2. */
    /* Due to masking, cast is in 32-bit range. */
    DUK_ASSERT(key <= DUK_UINT_MAX);
    return (duk_uint_t) key;
}

DUK_INTERNAL duk_hstring *duk_heap_strtable_intern_literal_checked(duk_hthread *thr, const duk_uint8_t *str, duk_uint32_t blen) {
    duk_uint_t key;
    duk_litcache_entry *ent;
    duk_hstring *h;

    /* Fast path check: literal exists in literal cache. */
    key = duk__strtable_litcache_key(str, blen);
    ent = thr->heap->litcache + key;
    if (ent->addr == str) {
        DUK_DD(DUK_DDPRINT("intern check for cached, pinned literal: str=%p, blen=%ld -> duk_hstring %!O",
                           (const void *) str, (long) blen, (duk_heaphdr *) ent->h));
        DUK_ASSERT(ent->h != NULL);
        DUK_ASSERT(DUK_HSTRING_HAS_PINNED_LITERAL(ent->h));
        DUK_STATS_INC(thr->heap, stats_strtab_litcache_hit);
        return ent->h;
    }

    /* Intern and update (overwrite) cache entry. */
    h = duk_heap_strtable_intern_checked(thr, str, blen);
    ent->addr = str;
    ent->h = h;
    DUK_STATS_INC(thr->heap, stats_strtab_litcache_miss);

    /* Pin the duk_hstring until the next mark-and-sweep.  This means
     * litcache entries don't need to be invalidated until the next
     * mark-and-sweep as their target duk_hstring is not freed before
     * the mark-and-sweep happens.  The pin remains even if the literal
     * cache entry is overwritten, and is still useful to avoid string
     * table traffic.
     */
    if (!DUK_HSTRING_HAS_PINNED_LITERAL(h)) {
        DUK_DD(DUK_DDPRINT("pin duk_hstring because it is a literal: %!O", (duk_heaphdr *) h));
        DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) h));
        DUK_HSTRING_INCREF(thr, h);
        DUK_HSTRING_SET_PINNED_LITERAL(h);
        DUK_STATS_INC(thr->heap, stats_strtab_litcache_pin);
    }

    return h;
}
#endif  /* DUK_USE_LITCACHE_SIZE */

DUK_INTERNAL duk_hstring *duk_heap_strtable_intern_u32_checked(duk_hthread *thr, duk_uint32_t val) {
    duk_hstring *res;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);

    res = duk_heap_strtable_intern_u32(thr->heap, val);
    if (DUK_UNLIKELY(res == NULL)) {
        DUK_ERROR_ALLOC_FAILED(thr);
        DUK_WO_NORETURN(return NULL;);
    }
    return res;
}

/*
 *  Remove (unlink) a string from the string table.
 *
 *  Just unlinks the duk_hstring, leaving link pointers as garbage.
 *  Caller must free the string itself.
 */

#if defined(DUK_USE_REFERENCE_COUNTING)
/* Unlink without a 'prev' pointer. */
DUK_INTERNAL void duk_heap_strtable_unlink(duk_heap *heap, duk_hstring *h) {
#if defined(DUK_USE_STRTAB_PTRCOMP)
    duk_uint16_t *slot;
#else
    duk_hstring **slot;
#endif
    duk_hstring *other;
    duk_hstring *prev;

    DUK_DDD(DUK_DDDPRINT("remove: heap=%p, h=%p, blen=%lu, strhash=%lx",
                         (void *) heap, (void *) h,
                         (unsigned long) (h != NULL ? DUK_HSTRING_GET_BYTELEN(h) : 0),
                         (unsigned long) (h != NULL ? DUK_HSTRING_GET_HASH(h) : 0)));

    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(h != NULL);

#if defined(DUK__STRTAB_RESIZE_CHECK)
    DUK_ASSERT(heap->st_count > 0);
    heap->st_count--;
#endif

#if defined(DUK_USE_STRTAB_PTRCOMP)
    slot = heap->strtable16 + (DUK_HSTRING_GET_HASH(h) & heap->st_mask);
#else
    slot = heap->strtable + (DUK_HSTRING_GET_HASH(h) & heap->st_mask);
#endif
    other = DUK__HEAPPTR_DEC16(heap, *slot);
    DUK_ASSERT(other != NULL);  /* At least argument string is in the chain. */

    prev = NULL;
    while (other != h) {
        prev = other;
        other = other->hdr.h_next;
        DUK_ASSERT(other != NULL);  /* We'll eventually find 'h'. */
    }
    if (prev != NULL) {
        /* Middle of list. */
        prev->hdr.h_next = h->hdr.h_next;
    } else {
        /* Head of list. */
        *slot = DUK__HEAPPTR_ENC16(heap, h->hdr.h_next);
    }

    /* There's no resize check on a string free.  The next string
     * intern will do one.
     */
}
#endif  /* DUK_USE_REFERENCE_COUNTING */

/* Unlink with a 'prev' pointer. */
DUK_INTERNAL void duk_heap_strtable_unlink_prev(duk_heap *heap, duk_hstring *h, duk_hstring *prev) {
#if defined(DUK_USE_STRTAB_PTRCOMP)
    duk_uint16_t *slot;
#else
    duk_hstring **slot;
#endif

    DUK_DDD(DUK_DDDPRINT("remove: heap=%p, prev=%p, h=%p, blen=%lu, strhash=%lx",
                         (void *) heap, (void *) prev, (void *) h,
                         (unsigned long) (h != NULL ? DUK_HSTRING_GET_BYTELEN(h) : 0),
                         (unsigned long) (h != NULL ? DUK_HSTRING_GET_HASH(h) : 0)));

    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(h != NULL);
    DUK_ASSERT(prev == NULL || prev->hdr.h_next == h);

#if defined(DUK__STRTAB_RESIZE_CHECK)
    DUK_ASSERT(heap->st_count > 0);
    heap->st_count--;
#endif

    if (prev != NULL) {
        /* Middle of list. */
        prev->hdr.h_next = h->hdr.h_next;
    } else {
        /* Head of list. */
#if defined(DUK_USE_STRTAB_PTRCOMP)
        slot = heap->strtable16 + (DUK_HSTRING_GET_HASH(h) & heap->st_mask);
#else
        slot = heap->strtable + (DUK_HSTRING_GET_HASH(h) & heap->st_mask);
#endif
        DUK_ASSERT(DUK__HEAPPTR_DEC16(heap, *slot) == h);
        *slot = DUK__HEAPPTR_ENC16(heap, h->hdr.h_next);
    }
}

/*
 *  Force string table resize check in mark-and-sweep.
 */

DUK_INTERNAL void duk_heap_strtable_force_resize(duk_heap *heap) {
    /* Does only one grow/shrink step if needed.  The heap->st_resizing
     * flag protects against recursive resizing.
     */

    DUK_ASSERT(heap != NULL);
    DUK_UNREF(heap);

#if defined(DUK__STRTAB_RESIZE_CHECK)
#if defined(DUK_USE_STRTAB_PTRCOMP)
    if (heap->strtable16 != NULL) {
#else
    if (heap->strtable != NULL) {
#endif
        duk__strtable_resize_check(heap);
    }
#endif
}

/*
 *  Free strings in the string table and the string table itself.
 */

DUK_INTERNAL void duk_heap_strtable_free(duk_heap *heap) {
#if defined(DUK_USE_STRTAB_PTRCOMP)
    duk_uint16_t *strtable;
    duk_uint16_t *st;
#else
    duk_hstring **strtable;
    duk_hstring **st;
#endif
    duk_hstring *h;

    DUK_ASSERT(heap != NULL);

#if defined(DUK_USE_ASSERTIONS)
    duk__strtable_assert_checks(heap);
#endif

    /* Strtable can be NULL if heap init fails.  However, in that case
     * heap->st_size is 0, so strtable == strtable_end and we skip the
     * loop without a special check.
     */
    strtable = DUK__GET_STRTABLE(heap);
    st = strtable + heap->st_size;
    DUK_ASSERT(strtable != NULL || heap->st_size == 0);

    while (strtable != st) {
        --st;
        h = DUK__HEAPPTR_DEC16(heap, *st);
        while (h) {
            duk_hstring *h_next;
            h_next = h->hdr.h_next;

            /* Strings may have inner refs (extdata) in some cases. */
            duk_free_hstring(heap, h);

            h = h_next;
        }
    }

    DUK_FREE(heap, strtable);
}

/* automatic undefs */
#undef DUK__GET_STRTABLE
#undef DUK__HEAPPTR_DEC16
#undef DUK__HEAPPTR_ENC16
#undef DUK__STRTAB_U32_MAX_STRLEN
//#line 1 "duk_heaphdr_assert.c"
/*
 *  duk_heaphdr assertion helpers
 */

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

#if defined(DUK_USE_ASSERTIONS)

#if defined(DUK_USE_DOUBLE_LINKED_HEAP)
DUK_INTERNAL void duk_heaphdr_assert_links(duk_heap *heap, duk_heaphdr *h) {
    DUK_UNREF(heap);
    if (h != NULL) {
        duk_heaphdr *h_prev, *h_next;
        h_prev = DUK_HEAPHDR_GET_PREV(heap, h);
        h_next = DUK_HEAPHDR_GET_NEXT(heap, h);
        DUK_ASSERT(h_prev == NULL || (DUK_HEAPHDR_GET_NEXT(heap, h_prev) == h));
        DUK_ASSERT(h_next == NULL || (DUK_HEAPHDR_GET_PREV(heap, h_next) == h));
    }
}
#else
DUK_INTERNAL void duk_heaphdr_assert_links(duk_heap *heap, duk_heaphdr *h) {
    DUK_UNREF(heap);
    DUK_UNREF(h);
}
#endif

DUK_INTERNAL void duk_heaphdr_assert_valid(duk_heaphdr *h) {
    DUK_ASSERT(h != NULL);
    DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h));
}

/* Assert validity of a heaphdr, including all subclasses. */
DUK_INTERNAL void duk_heaphdr_assert_valid_subclassed(duk_heaphdr *h) {
    switch (DUK_HEAPHDR_GET_TYPE(h)) {
    case DUK_HTYPE_OBJECT: {
        duk_hobject *h_obj = (duk_hobject *) h;
        DUK_HOBJECT_ASSERT_VALID(h_obj);
        if (DUK_HOBJECT_IS_COMPFUNC(h_obj)) {
            DUK_HCOMPFUNC_ASSERT_VALID((duk_hcompfunc *) h_obj);
        } else if (DUK_HOBJECT_IS_NATFUNC(h_obj)) {
            DUK_HNATFUNC_ASSERT_VALID((duk_hnatfunc *) h_obj);
        } else if (DUK_HOBJECT_IS_DECENV(h_obj)) {
            DUK_HDECENV_ASSERT_VALID((duk_hdecenv *) h_obj);
        } else if (DUK_HOBJECT_IS_OBJENV(h_obj)) {
            DUK_HOBJENV_ASSERT_VALID((duk_hobjenv *) h_obj);
        } else if (DUK_HOBJECT_IS_BUFOBJ(h_obj)) {
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
            DUK_HBUFOBJ_ASSERT_VALID((duk_hbufobj *) h_obj);
#endif
        } else if (DUK_HOBJECT_IS_BOUNDFUNC(h_obj)) {
            DUK_HBOUNDFUNC_ASSERT_VALID((duk_hboundfunc *) h_obj);
        } else if (DUK_HOBJECT_IS_PROXY(h_obj)) {
            DUK_HPROXY_ASSERT_VALID((duk_hproxy *) h_obj);
        } else if (DUK_HOBJECT_IS_THREAD(h_obj)) {
            DUK_HTHREAD_ASSERT_VALID((duk_hthread *) h_obj);
        } else {
            /* Just a plain object. */
            ;
        }
        break;
    }
    case DUK_HTYPE_STRING: {
        duk_hstring *h_str = (duk_hstring *) h;
        DUK_HSTRING_ASSERT_VALID(h_str);
        break;
    }
    case DUK_HTYPE_BUFFER: {
        duk_hbuffer *h_buf = (duk_hbuffer *) h;
        DUK_HBUFFER_ASSERT_VALID(h_buf);
        break;
    }
    default: {
        DUK_ASSERT(0);
    }
    }
}

#endif  /* DUK_USE_ASSERTIONS */
//#line 1 "duk_hobject_alloc.c"
/*
 *  Hobject allocation.
 *
 *  Provides primitive allocation functions for all object types (plain object,
 *  compiled function, native function, thread).  The object return is not yet
 *  in "heap allocated" list and has a refcount of zero, so caller must careful.
 */

/* XXX: In most cases there's no need for plain allocation without pushing
 * to the value stack.  Maybe rework contract?
 */

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

/*
 *  Helpers.
 */

DUK_LOCAL void duk__init_object_parts(duk_heap *heap, duk_uint_t hobject_flags, duk_hobject *obj) {
    DUK_ASSERT(obj != NULL);
    /* Zeroed by caller. */

    obj->hdr.h_flags = hobject_flags | DUK_HTYPE_OBJECT;
    DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(&obj->hdr) == DUK_HTYPE_OBJECT);  /* Assume zero shift. */

#if defined(DUK_USE_EXPLICIT_NULL_INIT)
    DUK_HOBJECT_SET_PROTOTYPE(heap, obj, NULL);
    DUK_HOBJECT_SET_PROPS(heap, obj, NULL);
#endif
#if defined(DUK_USE_HEAPPTR16)
    /* Zero encoded pointer is required to match NULL. */
    DUK_HEAPHDR_SET_NEXT(heap, &obj->hdr, NULL);
#if defined(DUK_USE_DOUBLE_LINKED_HEAP)
    DUK_HEAPHDR_SET_PREV(heap, &obj->hdr, NULL);
#endif
#endif
    DUK_HEAPHDR_ASSERT_LINKS(heap, &obj->hdr);
    DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap, &obj->hdr);

    /* obj->props is intentionally left as NULL, and duk_hobject_props.c must deal
     * with this properly.  This is intentional: empty objects consume a minimum
     * amount of memory.  Further, an initial allocation might fail and cause
     * 'obj' to "leak" (require a mark-and-sweep) since it is not reachable yet.
     */
}

DUK_LOCAL void *duk__hobject_alloc_init(duk_hthread *thr, duk_uint_t hobject_flags, duk_size_t size) {
    void *res;

    res = (void *) DUK_ALLOC_CHECKED_ZEROED(thr, size);
    DUK_ASSERT(res != NULL);
    duk__init_object_parts(thr->heap, hobject_flags, (duk_hobject *) res);
    return res;
}

/*
 *  Allocate an duk_hobject.
 *
 *  The allocated object has no allocation for properties; the caller may
 *  want to force a resize if a desired size is known.
 *
 *  The allocated object has zero reference count and is not reachable.
 *  The caller MUST make the object reachable and increase its reference
 *  count before invoking any operation that might require memory allocation.
 */

DUK_INTERNAL duk_hobject *duk_hobject_alloc_unchecked(duk_heap *heap, duk_uint_t hobject_flags) {
    duk_hobject *res;

    DUK_ASSERT(heap != NULL);

    /* different memory layout, alloc size, and init */
    DUK_ASSERT((hobject_flags & DUK_HOBJECT_FLAG_COMPFUNC) == 0);
    DUK_ASSERT((hobject_flags & DUK_HOBJECT_FLAG_NATFUNC) == 0);
    DUK_ASSERT((hobject_flags & DUK_HOBJECT_FLAG_BOUNDFUNC) == 0);

    res = (duk_hobject *) DUK_ALLOC_ZEROED(heap, sizeof(duk_hobject));
    if (DUK_UNLIKELY(res == NULL)) {
        return NULL;
    }
    DUK_ASSERT(!DUK_HOBJECT_IS_THREAD(res));

    duk__init_object_parts(heap, hobject_flags, res);

    DUK_ASSERT(!DUK_HOBJECT_IS_THREAD(res));
    return res;
}

DUK_INTERNAL duk_hobject *duk_hobject_alloc(duk_hthread *thr, duk_uint_t hobject_flags) {
    duk_hobject *res;

    res = (duk_hobject *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_hobject));
    return res;
}

DUK_INTERNAL duk_hcompfunc *duk_hcompfunc_alloc(duk_hthread *thr, duk_uint_t hobject_flags) {
    duk_hcompfunc *res;

    res = (duk_hcompfunc *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_hcompfunc));
#if defined(DUK_USE_EXPLICIT_NULL_INIT)
#if defined(DUK_USE_HEAPPTR16)
    /* NULL pointer is required to encode to zero, so memset is enough. */
#else
    res->data = NULL;
    res->funcs = NULL;
    res->bytecode = NULL;
#endif
    res->lex_env = NULL;
    res->var_env = NULL;
#endif

    return res;
}

DUK_INTERNAL duk_hnatfunc *duk_hnatfunc_alloc(duk_hthread *thr, duk_uint_t hobject_flags) {
    duk_hnatfunc *res;

    res = (duk_hnatfunc *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_hnatfunc));
#if defined(DUK_USE_EXPLICIT_NULL_INIT)
    res->func = NULL;
#endif

    return res;
}

DUK_INTERNAL duk_hboundfunc *duk_hboundfunc_alloc(duk_heap *heap, duk_uint_t hobject_flags) {
    duk_hboundfunc *res;

    res = (duk_hboundfunc *) DUK_ALLOC(heap, sizeof(duk_hboundfunc));
    if (!res) {
        return NULL;
    }
    duk_memzero(res, sizeof(duk_hboundfunc));

    duk__init_object_parts(heap, hobject_flags, &res->obj);

    DUK_TVAL_SET_UNDEFINED(&res->target);
    DUK_TVAL_SET_UNDEFINED(&res->this_binding);

#if defined(DUK_USE_EXPLICIT_NULL_INIT)
    res->args = NULL;
#endif

    return res;
}

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL duk_hbufobj *duk_hbufobj_alloc(duk_hthread *thr, duk_uint_t hobject_flags) {
    duk_hbufobj *res;

    res = (duk_hbufobj *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_hbufobj));
#if defined(DUK_USE_EXPLICIT_NULL_INIT)
    res->buf = NULL;
    res->buf_prop = NULL;
#endif

    DUK_HBUFOBJ_ASSERT_VALID(res);
    return res;
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/* Allocate a new thread.
 *
 * Leaves the built-ins array uninitialized.  The caller must either
 * initialize a new global context or share existing built-ins from
 * another thread.
 */
DUK_INTERNAL duk_hthread *duk_hthread_alloc_unchecked(duk_heap *heap, duk_uint_t hobject_flags) {
    duk_hthread *res;

    res = (duk_hthread *) DUK_ALLOC(heap, sizeof(duk_hthread));
    if (DUK_UNLIKELY(res == NULL)) {
        return NULL;
    }
    duk_memzero(res, sizeof(duk_hthread));

    duk__init_object_parts(heap, hobject_flags, &res->obj);

#if defined(DUK_USE_EXPLICIT_NULL_INIT)
    res->ptr_curr_pc = NULL;
    res->heap = NULL;
    res->valstack = NULL;
    res->valstack_end = NULL;
    res->valstack_alloc_end = NULL;
    res->valstack_bottom = NULL;
    res->valstack_top = NULL;
    res->callstack_curr = NULL;
    res->resumer = NULL;
    res->compile_ctx = NULL,
#if defined(DUK_USE_HEAPPTR16)
    res->strs16 = NULL;
#else
    res->strs = NULL;
#endif
    {
        duk_small_uint_t i;
        for (i = 0; i < DUK_NUM_BUILTINS; i++) {
            res->builtins[i] = NULL;
        }
    }
#endif
    /* When nothing is running, API calls are in non-strict mode. */
    DUK_ASSERT(res->strict == 0);

    res->heap = heap;

    /* XXX: Any reason not to merge duk_hthread_alloc.c here? */
    return res;
}

DUK_INTERNAL duk_hthread *duk_hthread_alloc(duk_hthread *thr, duk_uint_t hobject_flags) {
    duk_hthread *res;

    res = duk_hthread_alloc_unchecked(thr->heap, hobject_flags);
    if (res == NULL) {
        DUK_ERROR_ALLOC_FAILED(thr);
        DUK_WO_NORETURN(return NULL;);
    }
    return res;
}

DUK_INTERNAL duk_harray *duk_harray_alloc(duk_hthread *thr, duk_uint_t hobject_flags) {
    duk_harray *res;

    res = (duk_harray *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_harray));

    DUK_ASSERT(res->length == 0);

    return res;
}

DUK_INTERNAL duk_hdecenv *duk_hdecenv_alloc(duk_hthread *thr, duk_uint_t hobject_flags) {
    duk_hdecenv *res;

    res = (duk_hdecenv *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_hdecenv));
#if defined(DUK_USE_EXPLICIT_NULL_INIT)
    res->thread = NULL;
    res->varmap = NULL;
#endif

    DUK_ASSERT(res->thread == NULL);
    DUK_ASSERT(res->varmap == NULL);
    DUK_ASSERT(res->regbase_byteoff == 0);

    return res;
}

DUK_INTERNAL duk_hobjenv *duk_hobjenv_alloc(duk_hthread *thr, duk_uint_t hobject_flags) {
    duk_hobjenv *res;

    res = (duk_hobjenv *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_hobjenv));
#if defined(DUK_USE_EXPLICIT_NULL_INIT)
    res->target = NULL;
#endif

    DUK_ASSERT(res->target == NULL);

    return res;
}

DUK_INTERNAL duk_hproxy *duk_hproxy_alloc(duk_hthread *thr, duk_uint_t hobject_flags) {
    duk_hproxy *res;

    res = (duk_hproxy *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_hproxy));

    /* Leave ->target and ->handler uninitialized, as caller will always
     * explicitly initialize them before any side effects are possible.
     */

    return res;
}
//#line 1 "duk_hobject_assert.c"
/*
 *  duk_hobject and subclass assertion helpers
 */

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

#if defined(DUK_USE_ASSERTIONS)

DUK_INTERNAL void duk_hobject_assert_valid(duk_hobject *h) {
    DUK_ASSERT(h != NULL);
    DUK_ASSERT(!DUK_HOBJECT_IS_CALLABLE(h) ||
               DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_FUNCTION);
    DUK_ASSERT(!DUK_HOBJECT_IS_BUFOBJ(h) ||
               (DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_ARRAYBUFFER ||
                DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_DATAVIEW ||
                DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_INT8ARRAY ||
                DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_UINT8ARRAY ||
                DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_UINT8CLAMPEDARRAY ||
                DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_INT16ARRAY ||
                DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_UINT16ARRAY ||
                DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_INT32ARRAY ||
                DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_UINT32ARRAY ||
                DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_FLOAT32ARRAY ||
                DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_FLOAT64ARRAY));
    /* Object is an Array <=> object has exotic array behavior */
    DUK_ASSERT((DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_ARRAY && DUK_HOBJECT_HAS_EXOTIC_ARRAY(h)) ||
               (DUK_HOBJECT_GET_CLASS_NUMBER(h) != DUK_HOBJECT_CLASS_ARRAY && !DUK_HOBJECT_HAS_EXOTIC_ARRAY(h)));
}

DUK_INTERNAL void duk_harray_assert_valid(duk_harray *h) {
    DUK_ASSERT(h != NULL);
    DUK_ASSERT(DUK_HOBJECT_IS_ARRAY((duk_hobject *) h));
    DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_ARRAY((duk_hobject *) h));
}

DUK_INTERNAL void duk_hboundfunc_assert_valid(duk_hboundfunc *h) {
    DUK_ASSERT(h != NULL);
    DUK_ASSERT(DUK_HOBJECT_IS_BOUNDFUNC((duk_hobject *) h));
    DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(&h->target) ||
               (DUK_TVAL_IS_OBJECT(&h->target) &&
                DUK_HOBJECT_IS_CALLABLE(DUK_TVAL_GET_OBJECT(&h->target))));
    DUK_ASSERT(!DUK_TVAL_IS_UNUSED(&h->this_binding));
    DUK_ASSERT(h->nargs == 0 || h->args != NULL);
}

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL void duk_hbufobj_assert_valid(duk_hbufobj *h) {
    DUK_ASSERT(h != NULL);
    DUK_ASSERT(h->shift <= 3);
    DUK_ASSERT(h->elem_type <= DUK_HBUFOBJ_ELEM_MAX);
    DUK_ASSERT((h->shift == 0 && h->elem_type == DUK_HBUFOBJ_ELEM_UINT8) ||
               (h->shift == 0 && h->elem_type == DUK_HBUFOBJ_ELEM_UINT8CLAMPED) ||
               (h->shift == 0 && h->elem_type == DUK_HBUFOBJ_ELEM_INT8) ||
               (h->shift == 1 && h->elem_type == DUK_HBUFOBJ_ELEM_UINT16) ||
               (h->shift == 1 && h->elem_type == DUK_HBUFOBJ_ELEM_INT16) ||
               (h->shift == 2 && h->elem_type == DUK_HBUFOBJ_ELEM_UINT32) ||
               (h->shift == 2 && h->elem_type == DUK_HBUFOBJ_ELEM_INT32) ||
               (h->shift == 2 && h->elem_type == DUK_HBUFOBJ_ELEM_FLOAT32) ||
               (h->shift == 3 && h->elem_type == DUK_HBUFOBJ_ELEM_FLOAT64));
    DUK_ASSERT(h->is_typedarray == 0 || h->is_typedarray == 1);
    DUK_ASSERT(DUK_HOBJECT_IS_BUFOBJ((duk_hobject *) h));
    if (h->buf == NULL) {
        DUK_ASSERT(h->offset == 0);
        DUK_ASSERT(h->length == 0);
    } else {
        /* No assertions for offset or length; in particular,
         * it's OK for length to be longer than underlying
         * buffer.  Just ensure they don't wrap when added.
         */
        DUK_ASSERT(h->offset + h->length >= h->offset);
    }
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

DUK_INTERNAL void duk_hcompfunc_assert_valid(duk_hcompfunc *h) {
    DUK_ASSERT(h != NULL);
}

DUK_INTERNAL void duk_hnatfunc_assert_valid(duk_hnatfunc *h) {
    DUK_ASSERT(h != NULL);
}

DUK_INTERNAL void duk_hdecenv_assert_valid(duk_hdecenv *h) {
    DUK_ASSERT(h != NULL);
    DUK_ASSERT(DUK_HOBJECT_IS_DECENV((duk_hobject *) h));
    DUK_ASSERT(h->thread == NULL || h->varmap != NULL);
}

DUK_INTERNAL void duk_hobjenv_assert_valid(duk_hobjenv *h) {
    DUK_ASSERT(h != NULL);
    DUK_ASSERT(DUK_HOBJECT_IS_OBJENV((duk_hobject *) h));
    DUK_ASSERT(h->target != NULL);
    DUK_ASSERT(h->has_this == 0 || h->has_this == 1);
}

DUK_INTERNAL void duk_hproxy_assert_valid(duk_hproxy *h) {
    DUK_ASSERT(h != NULL);
    DUK_ASSERT(h->target != NULL);
    DUK_ASSERT(h->handler != NULL);
    DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ((duk_hobject *) h));
}

DUK_INTERNAL void duk_hthread_assert_valid(duk_hthread *thr) {
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) thr) == DUK_HTYPE_OBJECT);
    DUK_ASSERT(DUK_HOBJECT_IS_THREAD((duk_hobject *) thr));
    DUK_ASSERT(thr->unused1 == 0);
    DUK_ASSERT(thr->unused2 == 0);
}

DUK_INTERNAL void duk_ctx_assert_valid(duk_hthread *thr) {
    DUK_ASSERT(thr != NULL);
    DUK_HTHREAD_ASSERT_VALID(thr);
    DUK_ASSERT(thr->valstack != NULL);
    DUK_ASSERT(thr->valstack_bottom != NULL);
    DUK_ASSERT(thr->valstack_top != NULL);
    DUK_ASSERT(thr->valstack_end != NULL);
    DUK_ASSERT(thr->valstack_alloc_end != NULL);
    DUK_ASSERT(thr->valstack_alloc_end >= thr->valstack);
    DUK_ASSERT(thr->valstack_end >= thr->valstack);
    DUK_ASSERT(thr->valstack_top >= thr->valstack);
    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
    DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
    DUK_ASSERT(thr->valstack_alloc_end >= thr->valstack_end);
}

#endif  /* DUK_USE_ASSERTIONS */
//#line 1 "duk_hobject_enum.c"
/*
 *  Object enumeration support.
 *
 *  Creates an internal enumeration state object to be used e.g. with for-in
 *  enumeration.  The state object contains a snapshot of target object keys
 *  and internal control state for enumeration.  Enumerator flags allow caller
 *  to e.g. request internal/non-enumerable properties, and to enumerate only
 *  "own" properties.
 *
 *  Also creates the result value for e.g. Object.keys() based on the same
 *  internal structure.
 *
 *  This snapshot-based enumeration approach is used to simplify enumeration:
 *  non-snapshot-based approaches are difficult to reconcile with mutating
 *  the enumeration target, running multiple long-lived enumerators at the
 *  same time, garbage collection details, etc.  The downside is that the
 *  enumerator object is memory inefficient especially for iterating arrays.
 */

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

/* XXX: identify enumeration target with an object index (not top of stack) */

/* First enumerated key index in enumerator object, must match exactly the
 * number of control properties inserted to the enumerator.
 */
#define DUK__ENUM_START_INDEX  2

/* Current implementation suffices for ES2015 for now because there's no symbol
 * sorting, so commented out for now.
 */

/*
 *  Helper to sort enumeration keys using a callback for pairwise duk_hstring
 *  comparisons.  The keys are in the enumeration object entry part, starting
 *  from DUK__ENUM_START_INDEX, and the entry part is dense.  Entry part values
 *  are all "true", e.g. "1" -> true, "3" -> true, "foo" -> true, "2" -> true,
 *  so it suffices to just switch keys without switching values.
 *
 *  ES2015 [[OwnPropertyKeys]] enumeration order for ordinary objects:
 *  (1) array indices in ascending order,
 *  (2) non-array-index keys in insertion order, and
 *  (3) symbols in insertion order.
 *  http://www.ecma-international.org/ecma-262/6.0/#sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys.
 *
 *  This rule is applied to "own properties" at each inheritance level;
 *  non-duplicate parent keys always follow child keys.  For example,
 *  an inherited array index will enumerate -after- a symbol in the
 *  child.
 *
 *  Insertion sort is used because (1) it's simple and compact, (2) works
 *  in-place, (3) minimizes operations if data is already nearly sorted,
 *  (4) doesn't reorder elements considered equal.
 *  http://en.wikipedia.org/wiki/Insertion_sort
 */

/* Sort key, must hold array indices, "not array index" marker, and one more
 * higher value for symbols.
 */
#if !defined(DUK_USE_SYMBOL_BUILTIN)
typedef duk_uint32_t duk__sort_key_t;
#elif defined(DUK_USE_64BIT_OPS)
typedef duk_uint64_t duk__sort_key_t;
#else
typedef duk_double_t duk__sort_key_t;
#endif

/* Get sort key for a duk_hstring. */
DUK_LOCAL duk__sort_key_t duk__hstring_sort_key(duk_hstring *x) {
    duk__sort_key_t val;

    /* For array indices [0,0xfffffffe] use the array index as is.
     * For strings, use 0xffffffff, the marker 'arridx' already in
     * duk_hstring.  For symbols, any value above 0xffffffff works,
     * as long as it is the same for all symbols; currently just add
     * the masked flag field into the arridx temporary.
     */
    DUK_ASSERT(x != NULL);
    DUK_ASSERT(!DUK_HSTRING_HAS_SYMBOL(x) || DUK_HSTRING_GET_ARRIDX_FAST(x) == DUK_HSTRING_NO_ARRAY_INDEX);

    val = (duk__sort_key_t) DUK_HSTRING_GET_ARRIDX_FAST(x);

#if defined(DUK_USE_SYMBOL_BUILTIN)
    val = val + (duk__sort_key_t) (DUK_HEAPHDR_GET_FLAGS_RAW((duk_heaphdr *) x) & DUK_HSTRING_FLAG_SYMBOL);
#endif

    return (duk__sort_key_t) val;
}

/* Insert element 'b' after element 'a'? */
DUK_LOCAL duk_bool_t duk__sort_compare_es6(duk_hstring *a, duk_hstring *b, duk__sort_key_t val_b) {
    duk__sort_key_t val_a;

    DUK_ASSERT(a != NULL);
    DUK_ASSERT(b != NULL);
    DUK_UNREF(b);  /* Not actually needed now, val_b suffices. */

    val_a = duk__hstring_sort_key(a);

    if (val_a > val_b) {
        return 0;
    } else {
        return 1;
    }
}

DUK_LOCAL void duk__sort_enum_keys_es6(duk_hthread *thr, duk_hobject *h_obj, duk_int_fast32_t idx_start, duk_int_fast32_t idx_end) {
    duk_hstring **keys;
    duk_int_fast32_t idx;

    DUK_ASSERT(h_obj != NULL);
    DUK_ASSERT(idx_start >= DUK__ENUM_START_INDEX);
    DUK_ASSERT(idx_end >= idx_start);
    DUK_UNREF(thr);

    if (idx_end <= idx_start + 1) {
        return;  /* Zero or one element(s). */
    }

    keys = DUK_HOBJECT_E_GET_KEY_BASE(thr->heap, h_obj);

    for (idx = idx_start + 1; idx < idx_end; idx++) {
        duk_hstring *h_curr;
        duk_int_fast32_t idx_insert;
        duk__sort_key_t val_curr;

        h_curr = keys[idx];
        DUK_ASSERT(h_curr != NULL);

        /* Scan backwards for insertion place.  This works very well
         * when the elements are nearly in order which is the common
         * (and optimized for) case.
         */

        val_curr = duk__hstring_sort_key(h_curr);  /* Remains same during scanning. */
        for (idx_insert = idx - 1; idx_insert >= idx_start; idx_insert--) {
            duk_hstring *h_insert;
            h_insert = keys[idx_insert];
            DUK_ASSERT(h_insert != NULL);

            if (duk__sort_compare_es6(h_insert, h_curr, val_curr)) {
                break;
            }
        }
        /* If we're out of indices, idx_insert == idx_start - 1 and idx_insert++
         * brings us back to idx_start.
         */
        idx_insert++;
        DUK_ASSERT(idx_insert >= 0 && idx_insert <= idx);

        /*        .-- p_insert   .-- p_curr
         *        v              v
         *  | ... | insert | ... | curr
         */

        /* This could also done when the keys are in order, i.e.
         * idx_insert == idx.  The result would be an unnecessary
         * memmove() but we use an explicit check because the keys
         * are very often in order already.
         */
        if (idx != idx_insert) {
            duk_memmove((void *) (keys + idx_insert + 1),
                        (const void *) (keys + idx_insert),
                        ((size_t) (idx - idx_insert) * sizeof(duk_hstring *)));
            keys[idx_insert] = h_curr;
        }
    }

    /* Entry part has been reordered now with no side effects.
     * If the object has a hash part, it will now be incorrect
     * and we need to rehash.  Do that by forcing a resize to
     * the current size.
     */
    duk_hobject_resize_entrypart(thr, h_obj, DUK_HOBJECT_GET_ESIZE(h_obj));
}

/*
 *  Create an internal enumerator object E, which has its keys ordered
 *  to match desired enumeration ordering.  Also initialize internal control
 *  properties for enumeration.
 *
 *  Note: if an array was used to hold enumeration keys instead, an array
 *  scan would be needed to eliminate duplicates found in the prototype chain.
 */

DUK_LOCAL void duk__add_enum_key(duk_hthread *thr, duk_hstring *k) {
    /* 'k' may be unreachable on entry so must push without any
     * potential for GC.
     */
    duk_push_hstring(thr, k);
    duk_push_true(thr);
    duk_put_prop(thr, -3);
}

DUK_LOCAL void duk__add_enum_key_stridx(duk_hthread *thr, duk_small_uint_t stridx) {
    duk__add_enum_key(thr, DUK_HTHREAD_GET_STRING(thr, stridx));
}

DUK_INTERNAL void duk_hobject_enumerator_create(duk_hthread *thr, duk_small_uint_t enum_flags) {
    duk_hobject *enum_target;
    duk_hobject *curr;
    duk_hobject *res;
#if defined(DUK_USE_ES6_PROXY)
    duk_hobject *h_proxy_target;
    duk_hobject *h_proxy_handler;
    duk_hobject *h_trap_result;
#endif
    duk_uint_fast32_t i, len;  /* used for array, stack, and entry indices */
    duk_uint_fast32_t sort_start_index;

    DUK_ASSERT(thr != NULL);

    enum_target = duk_require_hobject(thr, -1);
    DUK_ASSERT(enum_target != NULL);

    duk_push_bare_object(thr);
    res = duk_known_hobject(thr, -1);

    /* [enum_target res] */

    /* Target must be stored so that we can recheck whether or not
     * keys still exist when we enumerate.  This is not done if the
     * enumeration result comes from a proxy trap as there is no
     * real object to check against.
     */
    duk_push_hobject(thr, enum_target);
    duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_INT_TARGET);  /* Target is bare, plain put OK. */

    /* Initialize index so that we skip internal control keys. */
    duk_push_int(thr, DUK__ENUM_START_INDEX);
    duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_INT_NEXT);  /* Target is bare, plain put OK. */

    /*
     *  Proxy object handling
     */

#if defined(DUK_USE_ES6_PROXY)
    if (DUK_LIKELY((enum_flags & DUK_ENUM_NO_PROXY_BEHAVIOR) != 0)) {
        goto skip_proxy;
    }
    if (DUK_LIKELY(!duk_hobject_proxy_check(enum_target,
                                            &h_proxy_target,
                                            &h_proxy_handler))) {
        goto skip_proxy;
    }

    /* XXX: share code with Object.keys() Proxy handling */

    /* In ES2015 for-in invoked the "enumerate" trap; in ES2016 "enumerate"
     * has been obsoleted and "ownKeys" is used instead.
     */
    DUK_DDD(DUK_DDDPRINT("proxy enumeration"));
    duk_push_hobject(thr, h_proxy_handler);
    if (!duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_OWN_KEYS)) {
        /* No need to replace the 'enum_target' value in stack, only the
         * enum_target reference.  This also ensures that the original
         * enum target is reachable, which keeps the proxy and the proxy
         * target reachable.  We do need to replace the internal _Target.
         */
        DUK_DDD(DUK_DDDPRINT("no ownKeys trap, enumerate proxy target instead"));
        DUK_DDD(DUK_DDDPRINT("h_proxy_target=%!O", (duk_heaphdr *) h_proxy_target));
        enum_target = h_proxy_target;

        duk_push_hobject(thr, enum_target);  /* -> [ ... enum_target res handler undefined target ] */
        duk_put_prop_stridx_short(thr, -4, DUK_STRIDX_INT_TARGET);  /* Target is bare, plain put OK. */

        duk_pop_2(thr);  /* -> [ ... enum_target res ] */
        goto skip_proxy;
    }

    /* [ ... enum_target res handler trap ] */
    duk_insert(thr, -2);
    duk_push_hobject(thr, h_proxy_target);    /* -> [ ... enum_target res trap handler target ] */
    duk_call_method(thr, 1 /*nargs*/);        /* -> [ ... enum_target res trap_result ] */
    h_trap_result = duk_require_hobject(thr, -1);
    DUK_UNREF(h_trap_result);

    duk_proxy_ownkeys_postprocess(thr, h_proxy_target, enum_flags);
    /* -> [ ... enum_target res trap_result keys_array ] */

    /* Copy cleaned up trap result keys into the enumerator object. */
    /* XXX: result is a dense array; could make use of that. */
    DUK_ASSERT(duk_is_array(thr, -1));
    len = (duk_uint_fast32_t) duk_get_length(thr, -1);
    for (i = 0; i < len; i++) {
        (void) duk_get_prop_index(thr, -1, (duk_uarridx_t) i);
        DUK_ASSERT(duk_is_string(thr, -1));  /* postprocess cleaned up */
        /* [ ... enum_target res trap_result keys_array val ] */
        duk_push_true(thr);
        /* [ ... enum_target res trap_result keys_array val true ] */
        duk_put_prop(thr, -5);
    }
    /* [ ... enum_target res trap_result keys_array ] */
    duk_pop_2(thr);
    duk_remove_m2(thr);

    /* [ ... res ] */

    /* The internal _Target property is kept pointing to the original
     * enumeration target (the proxy object), so that the enumerator
     * 'next' operation can read property values if so requested.  The
     * fact that the _Target is a proxy disables key existence check
     * during enumeration.
     */
    DUK_DDD(DUK_DDDPRINT("proxy enumeration, final res: %!O", (duk_heaphdr *) res));
    goto compact_and_return;

 skip_proxy:
#endif  /* DUK_USE_ES6_PROXY */

    curr = enum_target;
    sort_start_index = DUK__ENUM_START_INDEX;
    DUK_ASSERT(DUK_HOBJECT_GET_ENEXT(res) == DUK__ENUM_START_INDEX);
    while (curr) {
        duk_uint_fast32_t sort_end_index;
#if !defined(DUK_USE_PREFER_SIZE)
        duk_bool_t need_sort = 0;
#endif
        duk_bool_t cond;

        /* Enumeration proceeds by inheritance level.  Virtual
         * properties need to be handled specially, followed by
         * array part, and finally entry part.
         *
         * If there are array index keys in the entry part or any
         * other risk of the ES2015 [[OwnPropertyKeys]] order being
         * violated, need_sort is set and an explicit ES2015 sort is
         * done for the inheritance level.
         */

        /* XXX: inheriting from proxy */

        /*
         *  Virtual properties.
         *
         *  String and buffer indices are virtual and always enumerable,
         *  'length' is virtual and non-enumerable.  Array and arguments
         *  object props have special behavior but are concrete.
         *
         *  String and buffer objects don't have an array part so as long
         *  as virtual array index keys are enumerated first, we don't
         *  need to set need_sort.
         */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
        cond = DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(curr) || DUK_HOBJECT_IS_BUFOBJ(curr);
#else
        cond = DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(curr);
#endif
        cond = cond && !(enum_flags & DUK_ENUM_EXCLUDE_STRINGS);
        if (cond) {
            duk_bool_t have_length = 1;

            /* String and buffer enumeration behavior is identical now,
             * so use shared handler.
             */
            if (DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(curr)) {
                duk_hstring *h_val;
                h_val = duk_hobject_get_internal_value_string(thr->heap, curr);
                DUK_ASSERT(h_val != NULL);  /* string objects must not created without internal value */
                len = (duk_uint_fast32_t) DUK_HSTRING_GET_CHARLEN(h_val);
            }
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
            else {
                duk_hbufobj *h_bufobj;
                DUK_ASSERT(DUK_HOBJECT_IS_BUFOBJ(curr));
                h_bufobj = (duk_hbufobj *) curr;

                if (h_bufobj == NULL || !h_bufobj->is_typedarray) {
                    /* Zero length seems like a good behavior for neutered buffers.
                     * ArrayBuffer (non-view) and DataView don't have index properties
                     * or .length property.
                     */
                    len = 0;
                    have_length = 0;
                } else {
                    /* There's intentionally no check for
                     * current underlying buffer length.
                     */
                    len = (duk_uint_fast32_t) (h_bufobj->length >> h_bufobj->shift);
                }
            }
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

            for (i = 0; i < len; i++) {
                duk_hstring *k;

                /* This is a bit fragile: the string is not
                 * reachable until it is pushed by the helper.
                 */
                k = duk_heap_strtable_intern_u32_checked(thr, (duk_uint32_t) i);
                DUK_ASSERT(k);

                duk__add_enum_key(thr, k);

                /* [enum_target res] */
            }

            /* 'length' and other virtual properties are not
             * enumerable, but are included if non-enumerable
             * properties are requested.
             */

            if (have_length && (enum_flags & DUK_ENUM_INCLUDE_NONENUMERABLE)) {
                duk__add_enum_key_stridx(thr, DUK_STRIDX_LENGTH);
            }
        }

        /*
         *  Array part
         */

        cond = !(enum_flags & DUK_ENUM_EXCLUDE_STRINGS);
        if (cond) {
            for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(curr); i++) {
                duk_hstring *k;
                duk_tval *tv;

                tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, curr, i);
                if (DUK_TVAL_IS_UNUSED(tv)) {
                    continue;
                }
                k = duk_heap_strtable_intern_u32_checked(thr, (duk_uint32_t) i);  /* Fragile reachability. */
                DUK_ASSERT(k);

                duk__add_enum_key(thr, k);

                /* [enum_target res] */
            }

            if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(curr)) {
                /* Array .length comes after numeric indices. */
                if (enum_flags & DUK_ENUM_INCLUDE_NONENUMERABLE) {
                    duk__add_enum_key_stridx(thr, DUK_STRIDX_LENGTH);
                }
            }
        }

        /*
         *  Entries part
         */

        for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(curr); i++) {
            duk_hstring *k;

            k = DUK_HOBJECT_E_GET_KEY(thr->heap, curr, i);
            if (!k) {
                continue;
            }
            if (!(enum_flags & DUK_ENUM_INCLUDE_NONENUMERABLE) &&
                !DUK_HOBJECT_E_SLOT_IS_ENUMERABLE(thr->heap, curr, i)) {
                continue;
            }
            if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(k))) {
                if (!(enum_flags & DUK_ENUM_INCLUDE_HIDDEN) &&
                    DUK_HSTRING_HAS_HIDDEN(k)) {
                    continue;
                }
                if (!(enum_flags & DUK_ENUM_INCLUDE_SYMBOLS)) {
                    continue;
                }
#if !defined(DUK_USE_PREFER_SIZE)
                need_sort = 1;
#endif
            } else {
                DUK_ASSERT(!DUK_HSTRING_HAS_HIDDEN(k));  /* would also have symbol flag */
                if (enum_flags & DUK_ENUM_EXCLUDE_STRINGS) {
                    continue;
                }
            }
            if (DUK_HSTRING_HAS_ARRIDX(k)) {
                /* This in currently only possible if the
                 * object has no array part: the array part
                 * is exhaustive when it is present.
                 */
#if !defined(DUK_USE_PREFER_SIZE)
                need_sort = 1;
#endif
            } else {
                if (enum_flags & DUK_ENUM_ARRAY_INDICES_ONLY) {
                    continue;
                }
            }

            DUK_ASSERT(DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, curr, i) ||
                       !DUK_TVAL_IS_UNUSED(&DUK_HOBJECT_E_GET_VALUE_PTR(thr->heap, curr, i)->v));

            duk__add_enum_key(thr, k);

            /* [enum_target res] */
        }

        /* Sort enumerated keys according to ES2015 requirements for
         * the "inheritance level" just processed.  This is far from
         * optimal, ES2015 semantics could be achieved more efficiently
         * by handling array index string keys (and symbol keys)
         * specially above in effect doing the sort inline.
         *
         * Skip the sort if array index sorting is requested because
         * we must consider all keys, also inherited, so an explicit
         * sort is done for the whole result after we're done with the
         * prototype chain.
         *
         * Also skip the sort if need_sort == 0, i.e. we know for
         * certain that the enumerated order is already correct.
         */
        sort_end_index = DUK_HOBJECT_GET_ENEXT(res);

        if (!(enum_flags & DUK_ENUM_SORT_ARRAY_INDICES)) {
#if defined(DUK_USE_PREFER_SIZE)
            duk__sort_enum_keys_es6(thr, res, (duk_int_fast32_t) sort_start_index, (duk_int_fast32_t) sort_end_index);
#else
            if (need_sort) {
                DUK_DDD(DUK_DDDPRINT("need to sort"));
                duk__sort_enum_keys_es6(thr, res, (duk_int_fast32_t) sort_start_index, (duk_int_fast32_t) sort_end_index);
            } else {
                DUK_DDD(DUK_DDDPRINT("no need to sort"));
            }
#endif
        }

        sort_start_index = sort_end_index;

        if (enum_flags & DUK_ENUM_OWN_PROPERTIES_ONLY) {
            break;
        }

        curr = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, curr);
    }

    /* [enum_target res] */

    duk_remove_m2(thr);

    /* [res] */

    if (enum_flags & DUK_ENUM_SORT_ARRAY_INDICES) {
        /* Some E5/E5.1 algorithms require that array indices are iterated
         * in a strictly ascending order.  This is the case for e.g.
         * Array.prototype.forEach() and JSON.stringify() PropertyList
         * handling.  The caller can request an explicit sort in these
         * cases.
         */

        /* Sort to ES2015 order which works for pure array incides but
         * also for mixed keys.
         */
        duk__sort_enum_keys_es6(thr, res, (duk_int_fast32_t) DUK__ENUM_START_INDEX, (duk_int_fast32_t) DUK_HOBJECT_GET_ENEXT(res));
    }

#if defined(DUK_USE_ES6_PROXY)
 compact_and_return:
#endif
    /* compact; no need to seal because object is internal */
    duk_hobject_compact_props(thr, res);

    DUK_DDD(DUK_DDDPRINT("created enumerator object: %!iT", (duk_tval *) duk_get_tval(thr, -1)));
}

/*
 *  Returns non-zero if a key and/or value was enumerated, and:
 *
 *   [enum] -> [key]        (get_value == 0)
 *   [enum] -> [key value]  (get_value == 1)
 *
 *  Returns zero without pushing anything on the stack otherwise.
 */
DUK_INTERNAL duk_bool_t duk_hobject_enumerator_next(duk_hthread *thr, duk_bool_t get_value) {
    duk_hobject *e;
    duk_hobject *enum_target;
    duk_hstring *res = NULL;
    duk_uint_fast32_t idx;
    duk_bool_t check_existence;

    DUK_ASSERT(thr != NULL);

    /* [... enum] */

    e = duk_require_hobject(thr, -1);

    /* XXX use get tval ptr, more efficient */
    duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INT_NEXT);
    idx = (duk_uint_fast32_t) duk_require_uint(thr, -1);
    duk_pop(thr);
    DUK_DDD(DUK_DDDPRINT("enumeration: index is: %ld", (long) idx));

    /* Enumeration keys are checked against the enumeration target (to see
     * that they still exist).  In the proxy enumeration case _Target will
     * be the proxy, and checking key existence against the proxy is not
     * required (or sensible, as the keys may be fully virtual).
     */
    duk_xget_owndataprop_stridx_short(thr, -1, DUK_STRIDX_INT_TARGET);
    enum_target = duk_require_hobject(thr, -1);
    DUK_ASSERT(enum_target != NULL);
#if defined(DUK_USE_ES6_PROXY)
    check_existence = (!DUK_HOBJECT_IS_PROXY(enum_target));
#else
    check_existence = 1;
#endif
    duk_pop(thr);  /* still reachable */

    DUK_DDD(DUK_DDDPRINT("getting next enum value, enum_target=%!iO, enumerator=%!iT",
                         (duk_heaphdr *) enum_target, (duk_tval *) duk_get_tval(thr, -1)));

    /* no array part */
    for (;;) {
        duk_hstring *k;

        if (idx >= DUK_HOBJECT_GET_ENEXT(e)) {
            DUK_DDD(DUK_DDDPRINT("enumeration: ran out of elements"));
            break;
        }

        /* we know these because enum objects are internally created */
        k = DUK_HOBJECT_E_GET_KEY(thr->heap, e, idx);
        DUK_ASSERT(k != NULL);
        DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, e, idx));
        DUK_ASSERT(!DUK_TVAL_IS_UNUSED(&DUK_HOBJECT_E_GET_VALUE(thr->heap, e, idx).v));

        idx++;

        /* recheck that the property still exists */
        if (check_existence && !duk_hobject_hasprop_raw(thr, enum_target, k)) {
            DUK_DDD(DUK_DDDPRINT("property deleted during enumeration, skip"));
            continue;
        }

        DUK_DDD(DUK_DDDPRINT("enumeration: found element, key: %!O", (duk_heaphdr *) k));
        res = k;
        break;
    }

    DUK_DDD(DUK_DDDPRINT("enumeration: updating next index to %ld", (long) idx));

    duk_push_u32(thr, (duk_uint32_t) idx);
    duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_INT_NEXT);

    /* [... enum] */

    if (res) {
        duk_push_hstring(thr, res);
        if (get_value) {
            duk_push_hobject(thr, enum_target);
            duk_dup_m2(thr);       /* -> [... enum key enum_target key] */
            duk_get_prop(thr, -2); /* -> [... enum key enum_target val] */
            duk_remove_m2(thr);    /* -> [... enum key val] */
            duk_remove(thr, -3);   /* -> [... key val] */
        } else {
            duk_remove_m2(thr);    /* -> [... key] */
        }
        return 1;
    } else {
        duk_pop(thr);  /* -> [...] */
        return 0;
    }
}

/*
 *  Get enumerated keys in an ECMAScript array.  Matches Object.keys() behavior
 *  described in E5 Section 15.2.3.14.
 */

DUK_INTERNAL duk_ret_t duk_hobject_get_enumerated_keys(duk_hthread *thr, duk_small_uint_t enum_flags) {
    duk_hobject *e;
    duk_hstring **keys;
    duk_tval *tv;
    duk_uint_fast32_t count;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(duk_get_hobject(thr, -1) != NULL);

    /* Create a temporary enumerator to get the (non-duplicated) key list;
     * the enumerator state is initialized without being needed, but that
     * has little impact.
     */

    duk_hobject_enumerator_create(thr, enum_flags);
    e = duk_known_hobject(thr, -1);

    /* [enum_target enum res] */

    /* Create dense result array to exact size. */
    DUK_ASSERT(DUK_HOBJECT_GET_ENEXT(e) >= DUK__ENUM_START_INDEX);
    count = (duk_uint32_t) (DUK_HOBJECT_GET_ENEXT(e) - DUK__ENUM_START_INDEX);

    /* XXX: uninit would be OK */
    tv = duk_push_harray_with_size_outptr(thr, (duk_uint32_t) count);
    DUK_ASSERT(count == 0 || tv != NULL);
    DUK_ASSERT(!duk_is_bare_object(thr, -1));

    /* Fill result array, no side effects. */

    keys = DUK_HOBJECT_E_GET_KEY_BASE(thr->heap, e);
    keys += DUK__ENUM_START_INDEX;

    while (count-- > 0) {
        duk_hstring *k;

        k = *keys++;
        DUK_ASSERT(k != NULL);  /* enumerator must have no keys deleted */

        DUK_TVAL_SET_STRING(tv, k);
        tv++;
        DUK_HSTRING_INCREF(thr, k);
    }

    /* [enum_target enum res] */
    duk_remove_m2(thr);

    /* [enum_target res] */

    return 1;  /* return 1 to allow callers to tail call */
}

/* automatic undefs */
#undef DUK__ENUM_START_INDEX
//#line 1 "duk_hobject_misc.c"
/*
 *  Misc support functions
 */

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

DUK_INTERNAL duk_bool_t duk_hobject_prototype_chain_contains(duk_hthread *thr, duk_hobject *h, duk_hobject *p, duk_bool_t ignore_loop) {
    duk_uint_t sanity;

    DUK_ASSERT(thr != NULL);

    /* False if the object is NULL or the prototype 'p' is NULL.
     * In particular, false if both are NULL (don't compare equal).
     */
    if (h == NULL || p == NULL) {
        return 0;
    }

    sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
    do {
        if (h == p) {
            return 1;
        }

        if (sanity-- == 0) {
            if (ignore_loop) {
                break;
            } else {
                DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT);
                DUK_WO_NORETURN(return 0;);
            }
        }
        h = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h);
    } while (h);

    return 0;
}

DUK_INTERNAL void duk_hobject_set_prototype_updref(duk_hthread *thr, duk_hobject *h, duk_hobject *p) {
#if defined(DUK_USE_REFERENCE_COUNTING)
    duk_hobject *tmp;

    DUK_ASSERT(h);
    tmp = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h);
    DUK_HOBJECT_SET_PROTOTYPE(thr->heap, h, p);
    DUK_HOBJECT_INCREF_ALLOWNULL(thr, p);  /* avoid problems if p == h->prototype */
    DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp);
#else
    DUK_ASSERT(h);
    DUK_UNREF(thr);
    DUK_HOBJECT_SET_PROTOTYPE(thr->heap, h, p);
#endif
}
//#line 1 "duk_hobject_pc2line.c"
/*
 *  Helpers for creating and querying pc2line debug data, which
 *  converts a bytecode program counter to a source line number.
 *
 *  The run-time pc2line data is bit-packed, and documented in:
 *
 *    doc/function-objects.rst
 */

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

#if defined(DUK_USE_PC2LINE)

/* Generate pc2line data for an instruction sequence, leaving a buffer on stack top. */
DUK_INTERNAL void duk_hobject_pc2line_pack(duk_hthread *thr, duk_compiler_instr *instrs, duk_uint_fast32_t length) {
    duk_hbuffer_dynamic *h_buf;
    duk_bitencoder_ctx be_ctx_alloc;
    duk_bitencoder_ctx *be_ctx = &be_ctx_alloc;
    duk_uint32_t *hdr;
    duk_size_t new_size;
    duk_uint_fast32_t num_header_entries;
    duk_uint_fast32_t curr_offset;
    duk_int_fast32_t curr_line, next_line, diff_line;
    duk_uint_fast32_t curr_pc;
    duk_uint_fast32_t hdr_index;

    DUK_ASSERT(length <= DUK_COMPILER_MAX_BYTECODE_LENGTH);

    num_header_entries = (length + DUK_PC2LINE_SKIP - 1) / DUK_PC2LINE_SKIP;
    curr_offset = (duk_uint_fast32_t) (sizeof(duk_uint32_t) + num_header_entries * sizeof(duk_uint32_t) * 2);

    duk_push_dynamic_buffer(thr, (duk_size_t) curr_offset);
    h_buf = (duk_hbuffer_dynamic *) duk_known_hbuffer(thr, -1);
    DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(h_buf) && !DUK_HBUFFER_HAS_EXTERNAL(h_buf));

    hdr = (duk_uint32_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, h_buf);
    DUK_ASSERT(hdr != NULL);
    hdr[0] = (duk_uint32_t) length;  /* valid pc range is [0, length[ */

    curr_pc = 0U;
    while (curr_pc < length) {
        new_size = (duk_size_t) (curr_offset + DUK_PC2LINE_MAX_DIFF_LENGTH);
        duk_hbuffer_resize(thr, h_buf, new_size);

        hdr = (duk_uint32_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, h_buf);
        DUK_ASSERT(hdr != NULL);
        DUK_ASSERT(curr_pc < length);
        hdr_index = 1 + (curr_pc / DUK_PC2LINE_SKIP) * 2;
        curr_line = (duk_int_fast32_t) instrs[curr_pc].line;
        hdr[hdr_index + 0] = (duk_uint32_t) curr_line;
        hdr[hdr_index + 1] = (duk_uint32_t) curr_offset;

#if 0
        DUK_DDD(DUK_DDDPRINT("hdr[%ld]: pc=%ld line=%ld offset=%ld",
                             (long) (curr_pc / DUK_PC2LINE_SKIP),
                             (long) curr_pc,
                             (long) hdr[hdr_index + 0],
                             (long) hdr[hdr_index + 1]));
#endif

        duk_memzero(be_ctx, sizeof(*be_ctx));
        be_ctx->data = ((duk_uint8_t *) hdr) + curr_offset;
        be_ctx->length = (duk_size_t) DUK_PC2LINE_MAX_DIFF_LENGTH;

        for (;;) {
            curr_pc++;
            if ( ((curr_pc % DUK_PC2LINE_SKIP) == 0) ||  /* end of diff run */
                 (curr_pc >= length) ) {                 /* end of bytecode */
                break;
            }
            DUK_ASSERT(curr_pc < length);
            next_line = (duk_int32_t) instrs[curr_pc].line;
            diff_line = next_line - curr_line;

#if 0
            DUK_DDD(DUK_DDDPRINT("curr_line=%ld, next_line=%ld -> diff_line=%ld",
                                 (long) curr_line, (long) next_line, (long) diff_line));
#endif

            if (diff_line == 0) {
                /* 0 */
                duk_be_encode(be_ctx, 0, 1);
            } else if (diff_line >= 1 && diff_line <= 4) {
                /* 1 0 <2 bits> */
                duk_be_encode(be_ctx, (duk_uint32_t) ((0x02 << 2) + (diff_line - 1)), 4);
            } else if (diff_line >= -0x80 && diff_line <= 0x7f) {
                /* 1 1 0 <8 bits> */
                DUK_ASSERT(diff_line + 0x80 >= 0 && diff_line + 0x80 <= 0xff);
                duk_be_encode(be_ctx, (duk_uint32_t) ((0x06 << 8) + (diff_line + 0x80)), 11);
            } else {
                /* 1 1 1 <32 bits>
                 * Encode in two parts to avoid bitencode 24-bit limitation
                 */
                duk_be_encode(be_ctx, (duk_uint32_t) ((0x07 << 16) + ((next_line >> 16) & 0xffff)), 19);
                duk_be_encode(be_ctx, (duk_uint32_t) (next_line & 0xffff), 16);
            }

            curr_line = next_line;
        }

        duk_be_finish(be_ctx);
        DUK_ASSERT(!be_ctx->truncated);

        /* be_ctx->offset == length of encoded bitstream */
        curr_offset += (duk_uint_fast32_t) be_ctx->offset;
    }

    /* compact */
    new_size = (duk_size_t) curr_offset;
    duk_hbuffer_resize(thr, h_buf, new_size);

    (void) duk_to_fixed_buffer(thr, -1, NULL);

    DUK_DDD(DUK_DDDPRINT("final pc2line data: pc_limit=%ld, length=%ld, %lf bits/opcode --> %!ixT",
                         (long) length, (long) new_size, (double) new_size * 8.0 / (double) length,
                         (duk_tval *) duk_get_tval(thr, -1)));
}

/* PC is unsigned.  If caller does PC arithmetic and gets a negative result,
 * it will map to a large PC which is out of bounds and causes a zero to be
 * returned.
 */
DUK_LOCAL duk_uint_fast32_t duk__hobject_pc2line_query_raw(duk_hthread *thr, duk_hbuffer_fixed *buf, duk_uint_fast32_t pc) {
    duk_bitdecoder_ctx bd_ctx_alloc;
    duk_bitdecoder_ctx *bd_ctx = &bd_ctx_alloc;
    duk_uint32_t *hdr;
    duk_uint_fast32_t start_offset;
    duk_uint_fast32_t pc_limit;
    duk_uint_fast32_t hdr_index;
    duk_uint_fast32_t pc_base;
    duk_uint_fast32_t n;
    duk_uint_fast32_t curr_line;

    DUK_ASSERT(buf != NULL);
    DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC((duk_hbuffer *) buf) && !DUK_HBUFFER_HAS_EXTERNAL((duk_hbuffer *) buf));
    DUK_UNREF(thr);

    /*
     *  Use the index in the header to find the right starting point
     */

    hdr_index = pc / DUK_PC2LINE_SKIP;
    pc_base = hdr_index * DUK_PC2LINE_SKIP;
    n = pc - pc_base;

    if (DUK_HBUFFER_FIXED_GET_SIZE(buf) <= sizeof(duk_uint32_t)) {
        DUK_DD(DUK_DDPRINT("pc2line lookup failed: buffer is smaller than minimal header"));
        goto pc2line_error;
    }

    hdr = (duk_uint32_t *) (void *) DUK_HBUFFER_FIXED_GET_DATA_PTR(thr->heap, buf);
    pc_limit = hdr[0];
    if (pc >= pc_limit) {
        /* Note: pc is unsigned and cannot be negative */
        DUK_DD(DUK_DDPRINT("pc2line lookup failed: pc out of bounds (pc=%ld, limit=%ld)",
                           (long) pc, (long) pc_limit));
        goto pc2line_error;
    }

    curr_line = hdr[1 + hdr_index * 2];
    start_offset = hdr[1 + hdr_index * 2 + 1];
    if ((duk_size_t) start_offset > DUK_HBUFFER_FIXED_GET_SIZE(buf)) {
        DUK_DD(DUK_DDPRINT("pc2line lookup failed: start_offset out of bounds (start_offset=%ld, buffer_size=%ld)",
                           (long) start_offset, (long) DUK_HBUFFER_GET_SIZE((duk_hbuffer *) buf)));
        goto pc2line_error;
    }

    /*
     *  Iterate the bitstream (line diffs) until PC is reached
     */

    duk_memzero(bd_ctx, sizeof(*bd_ctx));
    bd_ctx->data = ((duk_uint8_t *) hdr) + start_offset;
    bd_ctx->length = (duk_size_t) (DUK_HBUFFER_FIXED_GET_SIZE(buf) - start_offset);

#if 0
    DUK_DDD(DUK_DDDPRINT("pc2line lookup: pc=%ld -> hdr_index=%ld, pc_base=%ld, n=%ld, start_offset=%ld",
                         (long) pc, (long) hdr_index, (long) pc_base, (long) n, (long) start_offset));
#endif

    while (n > 0) {
#if 0
        DUK_DDD(DUK_DDDPRINT("lookup: n=%ld, curr_line=%ld", (long) n, (long) curr_line));
#endif

        if (duk_bd_decode_flag(bd_ctx)) {
            if (duk_bd_decode_flag(bd_ctx)) {
                if (duk_bd_decode_flag(bd_ctx)) {
                    /* 1 1 1 <32 bits> */
                    duk_uint_fast32_t t;
                    t = duk_bd_decode(bd_ctx, 16);  /* workaround: max nbits = 24 now */
                    t = (t << 16) + duk_bd_decode(bd_ctx, 16);
                    curr_line = t;
                } else {
                    /* 1 1 0 <8 bits> */
                    duk_uint_fast32_t t;
                    t = duk_bd_decode(bd_ctx, 8);
                    curr_line = curr_line + t - 0x80;
                }
            } else {
                /* 1 0 <2 bits> */
                duk_uint_fast32_t t;
                t = duk_bd_decode(bd_ctx, 2);
                curr_line = curr_line + t + 1;
            }
        } else {
            /* 0: no change */
        }

        n--;
    }

    DUK_DDD(DUK_DDDPRINT("pc2line lookup result: pc %ld -> line %ld", (long) pc, (long) curr_line));
    return curr_line;

 pc2line_error:
    DUK_D(DUK_DPRINT("pc2line conversion failed for pc=%ld", (long) pc));
    return 0;
}

DUK_INTERNAL duk_uint_fast32_t duk_hobject_pc2line_query(duk_hthread *thr, duk_idx_t idx_func, duk_uint_fast32_t pc) {
    duk_hbuffer_fixed *pc2line;
    duk_uint_fast32_t line;

    /* XXX: now that pc2line is used by the debugger quite heavily in
     * checked execution, this should be optimized to avoid value stack
     * and perhaps also implement some form of pc2line caching (see
     * future work in debugger.rst).
     */

    duk_xget_owndataprop_stridx_short(thr, idx_func, DUK_STRIDX_INT_PC2LINE);
    pc2line = (duk_hbuffer_fixed *) (void *) duk_get_hbuffer(thr, -1);
    if (pc2line != NULL) {
        DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC((duk_hbuffer *) pc2line) && !DUK_HBUFFER_HAS_EXTERNAL((duk_hbuffer *) pc2line));
        line = duk__hobject_pc2line_query_raw(thr, pc2line, (duk_uint_fast32_t) pc);
    } else {
        line = 0;
    }
    duk_pop(thr);

    return line;
}

#endif  /* DUK_USE_PC2LINE */
//#line 1 "duk_hobject_props.c"
/*
 *  duk_hobject property access functionality.
 *
 *  This is very central functionality for size, performance, and compliance.
 *  It is also rather intricate; see hobject-algorithms.rst for discussion on
 *  the algorithms and memory-management.rst for discussion on refcounts and
 *  side effect issues.
 *
 *  Notes:
 *
 *    - It might be tempting to assert "refcount nonzero" for objects
 *      being operated on, but that's not always correct: objects with
 *      a zero refcount may be operated on by the refcount implementation
 *      (finalization) for instance.  Hence, no refcount assertions are made.
 *
 *    - Many operations (memory allocation, identifier operations, etc)
 *      may cause arbitrary side effects (e.g. through GC and finalization).
 *      These side effects may invalidate duk_tval pointers which point to
 *      areas subject to reallocation (like value stack).  Heap objects
 *      themselves have stable pointers.  Holding heap object pointers or
 *      duk_tval copies is not problematic with respect to side effects;
 *      care must be taken when holding and using argument duk_tval pointers.
 *
 *    - If a finalizer is executed, it may operate on the the same object
 *      we're currently dealing with.  For instance, the finalizer might
 *      delete a certain property which has already been looked up and
 *      confirmed to exist.  Ideally finalizers would be disabled if GC
 *      happens during property access.  At the moment property table realloc
 *      disables finalizers, and all DECREFs may cause arbitrary changes so
 *      handle DECREF carefully.
 *
 *    - The order of operations for a DECREF matters.  When DECREF is executed,
 *      the entire object graph must be consistent; note that a refzero may
 *      lead to a mark-and-sweep through a refcount finalizer.  Use NORZ macros
 *      and an explicit DUK_REFZERO_CHECK_xxx() if achieving correct order is hard.
 */

/*
 *  XXX: array indices are mostly typed as duk_uint32_t here; duk_uarridx_t
 *  might be more appropriate.
 */

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

/*
 *  Local defines
 */

#define DUK__NO_ARRAY_INDEX             DUK_HSTRING_NO_ARRAY_INDEX

/* Marker values for hash part. */
#define DUK__HASH_UNUSED                DUK_HOBJECT_HASHIDX_UNUSED
#define DUK__HASH_DELETED               DUK_HOBJECT_HASHIDX_DELETED

/* Valstack space that suffices for all local calls, excluding any recursion
 * into ECMAScript or Duktape/C calls (Proxy, getters, etc).
 */
#define DUK__VALSTACK_SPACE             10

/* Valstack space allocated especially for proxy lookup which does a
 * recursive property lookup.
 */
#define DUK__VALSTACK_PROXY_LOOKUP      20

/*
 *  Local prototypes
 */

DUK_LOCAL_DECL duk_bool_t duk__check_arguments_map_for_get(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_propdesc *temp_desc);
DUK_LOCAL_DECL void duk__check_arguments_map_for_put(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_propdesc *temp_desc, duk_bool_t throw_flag);
DUK_LOCAL_DECL void duk__check_arguments_map_for_delete(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_propdesc *temp_desc);

DUK_LOCAL_DECL duk_bool_t duk__handle_put_array_length_smaller(duk_hthread *thr, duk_hobject *obj, duk_uint32_t old_len, duk_uint32_t new_len, duk_bool_t force_flag, duk_uint32_t *out_result_len);
DUK_LOCAL_DECL duk_bool_t duk__handle_put_array_length(duk_hthread *thr, duk_hobject *obj);

DUK_LOCAL_DECL duk_bool_t duk__get_propdesc(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_propdesc *out_desc, duk_small_uint_t flags);
DUK_LOCAL_DECL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_uint32_t arr_idx, duk_propdesc *out_desc, duk_small_uint_t flags);

DUK_LOCAL_DECL void duk__abandon_array_part(duk_hthread *thr, duk_hobject *obj);
DUK_LOCAL_DECL void duk__grow_props_for_array_item(duk_hthread *thr, duk_hobject *obj, duk_uint32_t highest_arr_idx);

/*
 *  Misc helpers
 */

/* Convert a duk_tval number (caller checks) to a 32-bit index.  Returns
 * DUK__NO_ARRAY_INDEX if the number is not whole or not a valid array
 * index.
 */
/* XXX: for fastints, could use a variant which assumes a double duk_tval
 * (and doesn't need to check for fastint again).
 */
DUK_LOCAL duk_uint32_t duk__tval_number_to_arr_idx(duk_tval *tv) {
    duk_double_t dbl;
    duk_uint32_t idx;

    DUK_ASSERT(tv != NULL);
    DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));

    /* -0 is accepted here as index 0 because ToString(-0) == "0" which is
     * in canonical form and thus an array index.
     */
    dbl = DUK_TVAL_GET_NUMBER(tv);
    idx = (duk_uint32_t) dbl;
    if (duk_double_equals((duk_double_t) idx, dbl)) {
            /* Is whole and within 32 bit range.  If the value happens to be 0xFFFFFFFF,
         * it's not a valid array index but will then match DUK__NO_ARRAY_INDEX.
         */
        return idx;
    }
    return DUK__NO_ARRAY_INDEX;
}

#if defined(DUK_USE_FASTINT)
/* Convert a duk_tval fastint (caller checks) to a 32-bit index. */
DUK_LOCAL duk_uint32_t duk__tval_fastint_to_arr_idx(duk_tval *tv) {
    duk_int64_t t;

    DUK_ASSERT(tv != NULL);
    DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv));

    t = DUK_TVAL_GET_FASTINT(tv);
    if (((duk_uint64_t) t & ~DUK_U64_CONSTANT(0xffffffff)) != 0) {
        /* Catches >0x100000000 and negative values. */
        return DUK__NO_ARRAY_INDEX;
    }

    /* If the value happens to be 0xFFFFFFFF, it's not a valid array index
     * but will then match DUK__NO_ARRAY_INDEX.
     */
    return (duk_uint32_t) t;
}
#endif  /* DUK_USE_FASTINT */

/* Convert a duk_tval on the value stack (in a trusted index we don't validate)
 * to a string or symbol using ES2015 ToPropertyKey():
 * http://www.ecma-international.org/ecma-262/6.0/#sec-topropertykey.
 *
 * Also check if it's a valid array index and return that (or DUK__NO_ARRAY_INDEX
 * if not).
 */
DUK_LOCAL duk_uint32_t duk__to_property_key(duk_hthread *thr, duk_idx_t idx, duk_hstring **out_h) {
    duk_uint32_t arr_idx;
    duk_hstring *h;
    duk_tval *tv_dst;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(out_h != NULL);
    DUK_ASSERT(duk_is_valid_index(thr, idx));
    DUK_ASSERT(idx < 0);

    /* XXX: The revised ES2015 ToPropertyKey() handling (ES5.1 was just
     * ToString()) involves a ToPrimitive(), a symbol check, and finally
     * a ToString().  Figure out the best way to have a good fast path
     * but still be compliant and share code.
     */

    tv_dst = DUK_GET_TVAL_NEGIDX(thr, idx);  /* intentionally unvalidated */
    if (DUK_TVAL_IS_STRING(tv_dst)) {
        /* Most important path: strings and plain symbols are used as
         * is.  For symbols the array index check below is unnecessary
         * (they're never valid array indices) but checking that the
         * string is a symbol would make the plain string path slower
         * unnecessarily.
         */
        h = DUK_TVAL_GET_STRING(tv_dst);
    } else {
        h = duk_to_property_key_hstring(thr, idx);
    }
    DUK_ASSERT(h != NULL);
    *out_h = h;

    arr_idx = DUK_HSTRING_GET_ARRIDX_FAST(h);
    return arr_idx;
}

DUK_LOCAL duk_uint32_t duk__push_tval_to_property_key(duk_hthread *thr, duk_tval *tv_key, duk_hstring **out_h) {
    duk_push_tval(thr, tv_key);  /* XXX: could use an unsafe push here */
    return duk__to_property_key(thr, -1, out_h);
}

/* String is an own (virtual) property of a plain buffer. */
DUK_LOCAL duk_bool_t duk__key_is_plain_buf_ownprop(duk_hthread *thr, duk_hbuffer *buf, duk_hstring *key, duk_uint32_t arr_idx) {
    DUK_UNREF(thr);

    /* Virtual index properties.  Checking explicitly for
     * 'arr_idx != DUK__NO_ARRAY_INDEX' is not necessary
     * because DUK__NO_ARRAY_INDEXi is always larger than
     * maximum allowed buffer size.
     */
    DUK_ASSERT(DUK__NO_ARRAY_INDEX >= DUK_HBUFFER_GET_SIZE(buf));
    if (arr_idx < DUK_HBUFFER_GET_SIZE(buf)) {
        return 1;
    }

    /* Other virtual properties. */
    return (key == DUK_HTHREAD_STRING_LENGTH(thr));
}

/*
 *  Helpers for managing property storage size
 */

/* Get default hash part size for a certain entry part size. */
#if defined(DUK_USE_HOBJECT_HASH_PART)
DUK_LOCAL duk_uint32_t duk__get_default_h_size(duk_uint32_t e_size) {
    DUK_ASSERT(e_size <= DUK_HOBJECT_MAX_PROPERTIES);

    if (e_size >= DUK_USE_HOBJECT_HASH_PROP_LIMIT) {
        duk_uint32_t res;
        duk_uint32_t tmp;

        /* Hash size should be 2^N where N is chosen so that 2^N is
         * larger than e_size.  Extra shifting is used to ensure hash
         * is relatively sparse.
         */
        tmp = e_size;
        res = 2;  /* Result will be 2 ** (N + 1). */
        while (tmp >= 0x40) {
            tmp >>= 6;
            res <<= 6;
        }
        while (tmp != 0) {
            tmp >>= 1;
            res <<= 1;
        }
        DUK_ASSERT((DUK_HOBJECT_MAX_PROPERTIES << 2U) > DUK_HOBJECT_MAX_PROPERTIES);  /* Won't wrap, even shifted by 2. */
        DUK_ASSERT(res > e_size);
        return res;
    } else {
        return 0;
    }
}
#endif  /* USE_PROP_HASH_PART */

/* Get minimum entry part growth for a certain size. */
DUK_LOCAL duk_uint32_t duk__get_min_grow_e(duk_uint32_t e_size) {
    duk_uint32_t res;

    res = (e_size + DUK_USE_HOBJECT_ENTRY_MINGROW_ADD) / DUK_USE_HOBJECT_ENTRY_MINGROW_DIVISOR;
    DUK_ASSERT(res >= 1);  /* important for callers */
    return res;
}

/* Get minimum array part growth for a certain size. */
DUK_LOCAL duk_uint32_t duk__get_min_grow_a(duk_uint32_t a_size) {
    duk_uint32_t res;

    res = (a_size + DUK_USE_HOBJECT_ARRAY_MINGROW_ADD) / DUK_USE_HOBJECT_ARRAY_MINGROW_DIVISOR;
    DUK_ASSERT(res >= 1);  /* important for callers */
    return res;
}

/* Count actually used entry part entries (non-NULL keys). */
DUK_LOCAL duk_uint32_t duk__count_used_e_keys(duk_hthread *thr, duk_hobject *obj) {
    duk_uint_fast32_t i;
    duk_uint_fast32_t n = 0;
    duk_hstring **e;

    DUK_ASSERT(obj != NULL);
    DUK_UNREF(thr);

    e = DUK_HOBJECT_E_GET_KEY_BASE(thr->heap, obj);
    for (i = 0; i < DUK_HOBJECT_GET_ENEXT(obj); i++) {
        if (*e++) {
            n++;
        }
    }
    return (duk_uint32_t) n;
}

/* Count actually used array part entries and array minimum size.
 * NOTE: 'out_min_size' can be computed much faster by starting from the
 * end and breaking out early when finding first used entry, but this is
 * not needed now.
 */
DUK_LOCAL void duk__compute_a_stats(duk_hthread *thr, duk_hobject *obj, duk_uint32_t *out_used, duk_uint32_t *out_min_size) {
    duk_uint_fast32_t i;
    duk_uint_fast32_t used = 0;
    duk_uint_fast32_t highest_idx = (duk_uint_fast32_t) -1;  /* see below */
    duk_tval *a;

    DUK_ASSERT(obj != NULL);
    DUK_ASSERT(out_used != NULL);
    DUK_ASSERT(out_min_size != NULL);
    DUK_UNREF(thr);

    a = DUK_HOBJECT_A_GET_BASE(thr->heap, obj);
    for (i = 0; i < DUK_HOBJECT_GET_ASIZE(obj); i++) {
        duk_tval *tv = a++;
        if (!DUK_TVAL_IS_UNUSED(tv)) {
            used++;
            highest_idx = i;
        }
    }

    /* Initial value for highest_idx is -1 coerced to unsigned.  This
     * is a bit odd, but (highest_idx + 1) will then wrap to 0 below
     * for out_min_size as intended.
     */

    *out_used = (duk_uint32_t) used;
    *out_min_size = (duk_uint32_t) (highest_idx + 1);  /* 0 if no used entries */
}

/* Check array density and indicate whether or not the array part should be abandoned. */
DUK_LOCAL duk_bool_t duk__abandon_array_density_check(duk_uint32_t a_used, duk_uint32_t a_size) {
    /*
     *  Array abandon check; abandon if:
     *
     *    new_used / new_size < limit
     *    new_used < limit * new_size        || limit is 3 bits fixed point
     *    new_used < limit' / 8 * new_size   || *8
     *    8*new_used < limit' * new_size     || :8
     *    new_used < limit' * (new_size / 8)
     *
     *  Here, new_used = a_used, new_size = a_size.
     *
     *  Note: some callers use approximate values for a_used and/or a_size
     *  (e.g. dropping a '+1' term).  This doesn't affect the usefulness
     *  of the check, but may confuse debugging.
     */

    return (a_used < DUK_USE_HOBJECT_ARRAY_ABANDON_LIMIT * (a_size >> 3));
}

/* Fast check for extending array: check whether or not a slow density check is required. */
DUK_LOCAL duk_bool_t duk__abandon_array_slow_check_required(duk_uint32_t arr_idx, duk_uint32_t old_size) {
    duk_uint32_t new_size_min;

    /*
     *  In a fast check we assume old_size equals old_used (i.e., existing
     *  array is fully dense).
     *
     *  Slow check if:
     *
     *    (new_size - old_size) / old_size > limit
     *    new_size - old_size > limit * old_size
     *    new_size > (1 + limit) * old_size        || limit' is 3 bits fixed point
     *    new_size > (1 + (limit' / 8)) * old_size || * 8
     *    8 * new_size > (8 + limit') * old_size   || : 8
     *    new_size > (8 + limit') * (old_size / 8)
     *    new_size > limit'' * (old_size / 8)      || limit'' = 9 -> max 25% increase
     *    arr_idx + 1 > limit'' * (old_size / 8)
     *
     *  This check doesn't work well for small values, so old_size is rounded
     *  up for the check (and the '+ 1' of arr_idx can be ignored in practice):
     *
     *    arr_idx > limit'' * ((old_size + 7) / 8)
     */

    new_size_min = arr_idx + 1;
    return (new_size_min >= DUK_USE_HOBJECT_ARRAY_ABANDON_MINSIZE) &&
           (arr_idx > DUK_USE_HOBJECT_ARRAY_FAST_RESIZE_LIMIT * ((old_size + 7) >> 3));
}

DUK_LOCAL duk_bool_t duk__abandon_array_check(duk_hthread *thr, duk_uint32_t arr_idx, duk_hobject *obj) {
    duk_uint32_t min_size;
    duk_uint32_t old_used;
    duk_uint32_t old_size;

    if (!duk__abandon_array_slow_check_required(arr_idx, DUK_HOBJECT_GET_ASIZE(obj))) {
        DUK_DDD(DUK_DDDPRINT("=> fast resize is OK"));
        return 0;
    }

    duk__compute_a_stats(thr, obj, &old_used, &old_size);

    DUK_DDD(DUK_DDDPRINT("abandon check, array stats: old_used=%ld, old_size=%ld, arr_idx=%ld",
                         (long) old_used, (long) old_size, (long) arr_idx));

    min_size = arr_idx + 1;
#if defined(DUK_USE_OBJSIZES16)
    if (min_size > DUK_UINT16_MAX) {
        goto do_abandon;
    }
#endif
    DUK_UNREF(min_size);

    /* Note: intentionally use approximations to shave a few instructions:
     *   a_used = old_used  (accurate: old_used + 1)
     *   a_size = arr_idx   (accurate: arr_idx + 1)
     */
    if (duk__abandon_array_density_check(old_used, arr_idx)) {
        DUK_DD(DUK_DDPRINT("write to new array entry beyond current length, "
                           "decided to abandon array part (would become too sparse)"));

        /* Abandoning requires a props allocation resize and
         * 'rechecks' the valstack, invalidating any existing
         * valstack value pointers.
         */
        goto do_abandon;
    }

    DUK_DDD(DUK_DDDPRINT("=> decided to keep array part"));
    return 0;

 do_abandon:
    duk__abandon_array_part(thr, obj);
    DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(obj));
    return 1;
}

DUK_LOCAL duk_tval *duk__obtain_arridx_slot_slowpath(duk_hthread *thr, duk_uint32_t arr_idx, duk_hobject *obj) {
    /*
     *  Array needs to grow, but we don't want it becoming too sparse.
     *  If it were to become sparse, abandon array part, moving all
     *  array entries into the entries part (for good).
     *
     *  Since we don't keep track of actual density (used vs. size) of
     *  the array part, we need to estimate somehow.  The check is made
     *  in two parts:
     *
     *    - Check whether the resize need is small compared to the
     *      current size (relatively); if so, resize without further
     *      checking (essentially we assume that the original part is
     *      "dense" so that the result would be dense enough).
     *
     *    - Otherwise, compute the resize using an actual density
     *      measurement based on counting the used array entries.
     */

    DUK_DDD(DUK_DDDPRINT("write to new array requires array resize, decide whether to do a "
                         "fast resize without abandon check (arr_idx=%ld, old_size=%ld)",
                         (long) arr_idx, (long) DUK_HOBJECT_GET_ASIZE(obj)));

    if (DUK_UNLIKELY(duk__abandon_array_check(thr, arr_idx, obj) != 0)) {
        DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(obj));
        return NULL;
    }

    DUK_DD(DUK_DDPRINT("write to new array entry beyond current length, "
                       "decided to extend current allocation"));

    /* In principle it's possible to run out of memory extending the
     * array but with the allocation going through if we were to abandon
     * the array part and try again.  In practice this should be rare
     * because abandoned arrays have a higher per-entry footprint.
     */

    duk__grow_props_for_array_item(thr, obj, arr_idx);

    DUK_ASSERT(DUK_HOBJECT_HAS_ARRAY_PART(obj));
    DUK_ASSERT(arr_idx < DUK_HOBJECT_GET_ASIZE(obj));
    return DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, arr_idx);
}

DUK_LOCAL DUK_INLINE duk_tval *duk__obtain_arridx_slot(duk_hthread *thr, duk_uint32_t arr_idx, duk_hobject *obj) {
    if (DUK_LIKELY(arr_idx < DUK_HOBJECT_GET_ASIZE(obj))) {
        return DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, arr_idx);
    } else {
        return duk__obtain_arridx_slot_slowpath(thr, arr_idx, obj);
    }
}

/*
 *  Proxy helpers
 */

#if defined(DUK_USE_ES6_PROXY)
DUK_INTERNAL duk_bool_t duk_hobject_proxy_check(duk_hobject *obj, duk_hobject **out_target, duk_hobject **out_handler) {
    duk_hproxy *h_proxy;

    DUK_ASSERT(obj != NULL);
    DUK_ASSERT(out_target != NULL);
    DUK_ASSERT(out_handler != NULL);

    /* Caller doesn't need to check exotic proxy behavior (but does so for
     * some fast paths).
     */
    if (DUK_LIKELY(!DUK_HOBJECT_IS_PROXY(obj))) {
        return 0;
    }
    h_proxy = (duk_hproxy *) obj;
    DUK_HPROXY_ASSERT_VALID(h_proxy);

    DUK_ASSERT(h_proxy->handler != NULL);
    DUK_ASSERT(h_proxy->target != NULL);
    *out_handler = h_proxy->handler;
    *out_target = h_proxy->target;

    return 1;
}
#endif  /* DUK_USE_ES6_PROXY */

/* Get Proxy target object.  If the argument is not a Proxy, return it as is.
 * If a Proxy is revoked, an error is thrown.
 */
#if defined(DUK_USE_ES6_PROXY)
DUK_INTERNAL duk_hobject *duk_hobject_resolve_proxy_target(duk_hobject *obj) {
    DUK_ASSERT(obj != NULL);

    /* Resolve Proxy targets until Proxy chain ends.  No explicit check for
     * a Proxy loop: user code cannot create such a loop (it would only be
     * possible by editing duk_hproxy references directly).
     */

    while (DUK_HOBJECT_IS_PROXY(obj)) {
        duk_hproxy *h_proxy;

        h_proxy = (duk_hproxy *) obj;
        DUK_HPROXY_ASSERT_VALID(h_proxy);
        obj = h_proxy->target;
        DUK_ASSERT(obj != NULL);
    }

    DUK_ASSERT(obj != NULL);
    return obj;
}
#endif  /* DUK_USE_ES6_PROXY */

#if defined(DUK_USE_ES6_PROXY)
DUK_LOCAL duk_bool_t duk__proxy_check_prop(duk_hthread *thr, duk_hobject *obj, duk_small_uint_t stridx_trap, duk_tval *tv_key, duk_hobject **out_target) {
    duk_hobject *h_handler;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(obj != NULL);
    DUK_ASSERT(tv_key != NULL);
    DUK_ASSERT(out_target != NULL);

    if (!duk_hobject_proxy_check(obj, out_target, &h_handler)) {
        return 0;
    }
    DUK_ASSERT(*out_target != NULL);
    DUK_ASSERT(h_handler != NULL);

    /* XXX: At the moment Duktape accesses internal keys like _Finalizer using a
     * normal property set/get which would allow a proxy handler to interfere with
     * such behavior and to get access to internal key strings.  This is not a problem
     * as such because internal key strings can be created in other ways too (e.g.
     * through buffers).  The best fix is to change Duktape internal lookups to
     * skip proxy behavior.  Until that, internal property accesses bypass the
     * proxy and are applied to the target (as if the handler did not exist).
     * This has some side effects, see test-bi-proxy-internal-keys.js.
     */

    if (DUK_TVAL_IS_STRING(tv_key)) {
        duk_hstring *h_key = (duk_hstring *) DUK_TVAL_GET_STRING(tv_key);
        DUK_ASSERT(h_key != NULL);
        if (DUK_HSTRING_HAS_HIDDEN(h_key)) {
            /* Symbol accesses must go through proxy lookup in ES2015.
             * Hidden symbols behave like Duktape 1.x internal keys
             * and currently won't.
             */
            DUK_DDD(DUK_DDDPRINT("hidden key, skip proxy handler and apply to target"));
            return 0;
        }
    }

    /* The handler is looked up with a normal property lookup; it may be an
     * accessor or the handler object itself may be a proxy object.  If the
     * handler is a proxy, we need to extend the valstack as we make a
     * recursive proxy check without a function call in between (in fact
     * there is no limit to the potential recursion here).
     *
     * (For sanity, proxy creation rejects another proxy object as either
     * the handler or the target at the moment so recursive proxy cases
     * are not realized now.)
     */

    /* XXX: C recursion limit if proxies are allowed as handler/target values */

    duk_require_stack(thr, DUK__VALSTACK_PROXY_LOOKUP);
    duk_push_hobject(thr, h_handler);
    if (duk_get_prop_stridx_short(thr, -1, stridx_trap)) {
        /* -> [ ... handler trap ] */
        duk_insert(thr, -2);  /* -> [ ... trap handler ] */

        /* stack prepped for func call: [ ... trap handler ] */
        return 1;
    } else {
        duk_pop_2_unsafe(thr);
        return 0;
    }
}
#endif  /* DUK_USE_ES6_PROXY */

/*
 *  Reallocate property allocation, moving properties to the new allocation.
 *
 *  Includes key compaction, rehashing, and can also optionally abandon
 *  the array part, 'migrating' array entries into the beginning of the
 *  new entry part.
 *
 *  There is no support for in-place reallocation or just compacting keys
 *  without resizing the property allocation.  This is intentional to keep
 *  code size minimal, but would be useful future work.
 *
 *  The implementation is relatively straightforward, except for the array
 *  abandonment process.  Array abandonment requires that new string keys
 *  are interned, which may trigger GC.  All keys interned so far must be
 *  reachable for GC at all times and correctly refcounted for; valstack is
 *  used for that now.
 *
 *  Also, a GC triggered during this reallocation process must not interfere
 *  with the object being resized.  This is currently controlled by preventing
 *  finalizers (as they may affect ANY object) and object compaction in
 *  mark-and-sweep.  It would suffice to protect only this particular object
 *  from compaction, however.  DECREF refzero cascades are side effect free
 *  and OK.
 *
 *  Note: because we need to potentially resize the valstack (as part
 *  of abandoning the array part), any tval pointers to the valstack
 *  will become invalid after this call.
 */

DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr,
                                            duk_hobject *obj,
                                            duk_uint32_t new_e_size,
                                            duk_uint32_t new_a_size,
                                            duk_uint32_t new_h_size,
                                            duk_bool_t abandon_array) {
    duk_small_uint_t prev_ms_base_flags;
    duk_uint32_t new_alloc_size;
    duk_uint32_t new_e_size_adjusted;
    duk_uint8_t *new_p;
    duk_hstring **new_e_k;
    duk_propvalue *new_e_pv;
    duk_uint8_t *new_e_f;
    duk_tval *new_a;
    duk_uint32_t *new_h;
    duk_uint32_t new_e_next;
    duk_uint_fast32_t i;
    duk_size_t array_copy_size;
#if defined(DUK_USE_ASSERTIONS)
    duk_bool_t prev_error_not_allowed;
#endif

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(obj != NULL);
    DUK_ASSERT(!abandon_array || new_a_size == 0);  /* if abandon_array, new_a_size must be 0 */
    DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, obj) != NULL || (DUK_HOBJECT_GET_ESIZE(obj) == 0 && DUK_HOBJECT_GET_ASIZE(obj) == 0));
    DUK_ASSERT(new_h_size == 0 || new_h_size >= new_e_size);  /* required to guarantee success of rehashing,
                                                               * intentionally use unadjusted new_e_size
                                                               */
    DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj));
    DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);

    DUK_STATS_INC(thr->heap, stats_object_realloc_props);

    /*
     *  Pre resize assertions.
     */

#if defined(DUK_USE_ASSERTIONS)
    /* XXX: pre-checks (such as no duplicate keys) */
#endif

    /*
     *  For property layout 1, tweak e_size to ensure that the whole entry
     *  part (key + val + flags) is a suitable multiple for alignment
     *  (platform specific).
     *
     *  Property layout 2 does not require this tweaking and is preferred
     *  on low RAM platforms requiring alignment.
     */

#if defined(DUK_USE_HOBJECT_LAYOUT_2) || defined(DUK_USE_HOBJECT_LAYOUT_3)
    DUK_DDD(DUK_DDDPRINT("using layout 2 or 3, no need to pad e_size: %ld", (long) new_e_size));
    new_e_size_adjusted = new_e_size;
#elif defined(DUK_USE_HOBJECT_LAYOUT_1) && (DUK_HOBJECT_ALIGN_TARGET == 1)
    DUK_DDD(DUK_DDDPRINT("using layout 1, but no need to pad e_size: %ld", (long) new_e_size));
    new_e_size_adjusted = new_e_size;
#elif defined(DUK_USE_HOBJECT_LAYOUT_1) && ((DUK_HOBJECT_ALIGN_TARGET == 4) || (DUK_HOBJECT_ALIGN_TARGET == 8))
    new_e_size_adjusted = (new_e_size + (duk_uint32_t) DUK_HOBJECT_ALIGN_TARGET - 1U) &
                          (~((duk_uint32_t) DUK_HOBJECT_ALIGN_TARGET - 1U));
    DUK_DDD(DUK_DDDPRINT("using layout 1, and alignment target is %ld, adjusted e_size: %ld -> %ld",
                         (long) DUK_HOBJECT_ALIGN_TARGET, (long) new_e_size, (long) new_e_size_adjusted));
    DUK_ASSERT(new_e_size_adjusted >= new_e_size);
#else
#error invalid hobject layout defines
#endif

    /*
     *  Debug logging after adjustment.
     */

    DUK_DDD(DUK_DDDPRINT("attempt to resize hobject %p props (%ld -> %ld bytes), from {p=%p,e_size=%ld,e_next=%ld,a_size=%ld,h_size=%ld} to "
                         "{e_size=%ld,a_size=%ld,h_size=%ld}, abandon_array=%ld, unadjusted new_e_size=%ld",
                         (void *) obj,
                         (long) DUK_HOBJECT_P_COMPUTE_SIZE(DUK_HOBJECT_GET_ESIZE(obj),
                                                           DUK_HOBJECT_GET_ASIZE(obj),
                                                           DUK_HOBJECT_GET_HSIZE(obj)),
                         (long) DUK_HOBJECT_P_COMPUTE_SIZE(new_e_size_adjusted, new_a_size, new_h_size),
                         (void *) DUK_HOBJECT_GET_PROPS(thr->heap, obj),
                         (long) DUK_HOBJECT_GET_ESIZE(obj),
                         (long) DUK_HOBJECT_GET_ENEXT(obj),
                         (long) DUK_HOBJECT_GET_ASIZE(obj),
                         (long) DUK_HOBJECT_GET_HSIZE(obj),
                         (long) new_e_size_adjusted,
                         (long) new_a_size,
                         (long) new_h_size,
                         (long) abandon_array,
                         (long) new_e_size));

    /*
     *  Property count check.  This is the only point where we ensure that
     *  we don't get more (allocated) property space that we can handle.
     *  There aren't hard limits as such, but some algorithms may fail
     *  if we get too close to the 4G property limit.
     *
     *  Since this works based on allocation size (not actually used size),
     *  the limit is a bit approximate but good enough in practice.
     */

    if (new_e_size_adjusted + new_a_size > DUK_HOBJECT_MAX_PROPERTIES) {
        DUK_ERROR_ALLOC_FAILED(thr);
        DUK_WO_NORETURN(return;);
    }
#if defined(DUK_USE_OBJSIZES16)
    if (new_e_size_adjusted > DUK_UINT16_MAX || new_a_size > DUK_UINT16_MAX) {
        /* If caller gave us sizes larger than what we can store,
         * fail memory safely with an internal error rather than
         * truncating the sizes.
         */
        DUK_ERROR_INTERNAL(thr);
        DUK_WO_NORETURN(return;);
    }
#endif

    /*
     *  Compute new alloc size and alloc new area.
     *
     *  The new area is not tracked in the heap at all, so it's critical
     *  we get to free/keep it in a controlled manner.
     */

#if defined(DUK_USE_ASSERTIONS)
    /* Whole path must be error throw free, but we may be called from
     * within error handling so can't assert for error_not_allowed == 0.
     */
    prev_error_not_allowed = thr->heap->error_not_allowed;
    thr->heap->error_not_allowed = 1;
#endif
    prev_ms_base_flags = thr->heap->ms_base_flags;
    thr->heap->ms_base_flags |=
            DUK_MS_FLAG_NO_OBJECT_COMPACTION;      /* Avoid attempt to compact the current object (all objects really). */
    thr->heap->pf_prevent_count++;                 /* Avoid finalizers. */
    DUK_ASSERT(thr->heap->pf_prevent_count != 0);  /* Wrap. */

    new_alloc_size = DUK_HOBJECT_P_COMPUTE_SIZE(new_e_size_adjusted, new_a_size, new_h_size);
    DUK_DDD(DUK_DDDPRINT("new hobject allocation size is %ld", (long) new_alloc_size));
    if (new_alloc_size == 0) {
        DUK_ASSERT(new_e_size_adjusted == 0);
        DUK_ASSERT(new_a_size == 0);
        DUK_ASSERT(new_h_size == 0);
        new_p = NULL;
    } else {
        /* Alloc may trigger mark-and-sweep but no compaction, and
         * cannot throw.
         */
#if 0  /* XXX: inject test */
        if (1) {
            new_p = NULL;
            goto alloc_failed;
        }
#endif
        new_p = (duk_uint8_t *) DUK_ALLOC(thr->heap, new_alloc_size);
        if (new_p == NULL) {
            /* NULL always indicates alloc failure because
             * new_alloc_size > 0.
             */
            goto alloc_failed;
        }
    }

    /* Set up pointers to the new property area: this is hidden behind a macro
     * because it is memory layout specific.
     */
    DUK_HOBJECT_P_SET_REALLOC_PTRS(new_p, new_e_k, new_e_pv, new_e_f, new_a, new_h,
                                   new_e_size_adjusted, new_a_size, new_h_size);
    DUK_UNREF(new_h);  /* happens when hash part dropped */
    new_e_next = 0;

    /* if new_p == NULL, all of these pointers are NULL */
    DUK_ASSERT((new_p != NULL) ||
               (new_e_k == NULL && new_e_pv == NULL && new_e_f == NULL &&
                new_a == NULL && new_h == NULL));

    DUK_DDD(DUK_DDDPRINT("new alloc size %ld, new_e_k=%p, new_e_pv=%p, new_e_f=%p, new_a=%p, new_h=%p",
                         (long) new_alloc_size, (void *) new_e_k, (void *) new_e_pv, (void *) new_e_f,
                         (void *) new_a, (void *) new_h));

    /*
     *  Migrate array part to start of entries if requested.
     *
     *  Note: from an enumeration perspective the order of entry keys matters.
     *  Array keys should appear wherever they appeared before the array abandon
     *  operation.  (This no longer matters much because keys are ES2015 sorted.)
     */

    if (abandon_array) {
        /* Assuming new_a_size == 0, and that entry part contains
         * no conflicting keys, refcounts do not need to be adjusted for
         * the values, as they remain exactly the same.
         *
         * The keys, however, need to be interned, incref'd, and be
         * reachable for GC.  Any intern attempt may trigger a GC and
         * claim any non-reachable strings, so every key must be reachable
         * at all times.  Refcounts must be correct to satisfy refcount
         * assertions.
         *
         * A longjmp must not occur here, as the new_p allocation would
         * leak.  Refcounts would come out correctly as the interned
         * strings are valstack tracked.
         */
        DUK_ASSERT(new_a_size == 0);

        DUK_STATS_INC(thr->heap, stats_object_abandon_array);

        for (i = 0; i < DUK_HOBJECT_GET_ASIZE(obj); i++) {
            duk_tval *tv1;
            duk_tval *tv2;
            duk_hstring *key;

            DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, obj) != NULL);

            tv1 = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, i);
            if (DUK_TVAL_IS_UNUSED(tv1)) {
                continue;
            }

            DUK_ASSERT(new_p != NULL && new_e_k != NULL &&
                       new_e_pv != NULL && new_e_f != NULL);

            /*
             *  Intern key via the valstack to ensure reachability behaves
             *  properly.  We must avoid longjmp's here so use non-checked
             *  primitives.
             *
             *  Note: duk_check_stack() potentially reallocs the valstack,
             *  invalidating any duk_tval pointers to valstack.  Callers
             *  must be careful.
             */

#if 0  /* XXX: inject test */
            if (1) {
                goto abandon_error;
            }
#endif
            /* Never shrinks; auto-adds DUK_VALSTACK_INTERNAL_EXTRA, which
             * is generous.
             */
            if (!duk_check_stack(thr, 1)) {
                goto abandon_error;
            }
            DUK_ASSERT_VALSTACK_SPACE(thr, 1);
            key = duk_heap_strtable_intern_u32(thr->heap, (duk_uint32_t) i);
            if (key == NULL) {
                goto abandon_error;
            }
            duk_push_hstring(thr, key);  /* keep key reachable for GC etc; guaranteed not to fail */

            /* Key is now reachable in the valstack, don't INCREF
             * the new allocation yet (we'll steal the refcounts
             * from the value stack once all keys are done).
             */

            new_e_k[new_e_next] = key;
            tv2 = &new_e_pv[new_e_next].v;  /* array entries are all plain values */
            DUK_TVAL_SET_TVAL(tv2, tv1);
            new_e_f[new_e_next] = DUK_PROPDESC_FLAG_WRITABLE |
                                  DUK_PROPDESC_FLAG_ENUMERABLE |
                                  DUK_PROPDESC_FLAG_CONFIGURABLE;
            new_e_next++;

            /* Note: new_e_next matches pushed temp key count, and nothing can
             * fail above between the push and this point.
             */
        }

        /* Steal refcounts from value stack. */
        DUK_DDD(DUK_DDDPRINT("abandon array: pop %ld key temps from valstack", (long) new_e_next));
        duk_pop_n_nodecref_unsafe(thr, (duk_idx_t) new_e_next);
    }

    /*
     *  Copy keys and values in the entry part (compacting them at the same time).
     */

    for (i = 0; i < DUK_HOBJECT_GET_ENEXT(obj); i++) {
        duk_hstring *key;

        DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, obj) != NULL);

        key = DUK_HOBJECT_E_GET_KEY(thr->heap, obj, i);
        if (key == NULL) {
            continue;
        }

        DUK_ASSERT(new_p != NULL && new_e_k != NULL &&
                   new_e_pv != NULL && new_e_f != NULL);

        new_e_k[new_e_next] = key;
        new_e_pv[new_e_next] = DUK_HOBJECT_E_GET_VALUE(thr->heap, obj, i);
        new_e_f[new_e_next] = DUK_HOBJECT_E_GET_FLAGS(thr->heap, obj, i);
        new_e_next++;
    }
    /* the entries [new_e_next, new_e_size_adjusted[ are left uninitialized on purpose (ok, not gc reachable) */

    /*
     *  Copy array elements to new array part.  If the new array part is
     *  larger, initialize the unused entries as UNUSED because they are
     *  GC reachable.
     */

#if defined(DUK_USE_ASSERTIONS)
    /* Caller must have decref'd values above new_a_size (if that is necessary). */
    if (!abandon_array) {
        for (i = new_a_size; i < DUK_HOBJECT_GET_ASIZE(obj); i++) {
            duk_tval *tv;
            tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, i);
            DUK_ASSERT(DUK_TVAL_IS_UNUSED(tv));
        }
    }
#endif
    if (new_a_size > DUK_HOBJECT_GET_ASIZE(obj)) {
        array_copy_size = sizeof(duk_tval) * DUK_HOBJECT_GET_ASIZE(obj);
    } else {
        array_copy_size = sizeof(duk_tval) * new_a_size;
    }

    DUK_ASSERT(new_a != NULL || array_copy_size == 0U);
    DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, obj) != NULL || array_copy_size == 0U);
    DUK_ASSERT(DUK_HOBJECT_GET_ASIZE(obj) > 0 || array_copy_size == 0U);
    duk_memcpy_unsafe((void *) new_a,
                      (const void *) DUK_HOBJECT_A_GET_BASE(thr->heap, obj),
                      array_copy_size);

    for (i = DUK_HOBJECT_GET_ASIZE(obj); i < new_a_size; i++) {
        duk_tval *tv = &new_a[i];
        DUK_TVAL_SET_UNUSED(tv);
    }

    /*
     *  Rebuild the hash part always from scratch (guaranteed to finish
     *  as long as caller gave consistent parameters).
     *
     *  Any resize of hash part requires rehashing.  In addition, by rehashing
     *  get rid of any elements marked deleted (DUK__HASH_DELETED) which is critical
     *  to ensuring the hash part never fills up.
     */

#if defined(DUK_USE_HOBJECT_HASH_PART)
    if (new_h_size == 0) {
        DUK_DDD(DUK_DDDPRINT("no hash part, no rehash"));
    } else {
        duk_uint32_t mask;

        DUK_ASSERT(new_h != NULL);

        /* fill new_h with u32 0xff = UNUSED */
        DUK_ASSERT(new_h_size > 0);
        duk_memset(new_h, 0xff, sizeof(duk_uint32_t) * new_h_size);

        DUK_ASSERT(new_e_next <= new_h_size);  /* equality not actually possible */

        mask = new_h_size - 1;
        for (i = 0; i < new_e_next; i++) {
            duk_hstring *key = new_e_k[i];
            duk_uint32_t j, step;

            DUK_ASSERT(key != NULL);
            j = DUK_HSTRING_GET_HASH(key) & mask;
            step = 1;  /* Cache friendly but clustering prone. */

            for (;;) {
                DUK_ASSERT(new_h[j] != DUK__HASH_DELETED);  /* should never happen */
                if (new_h[j] == DUK__HASH_UNUSED) {
                    DUK_DDD(DUK_DDDPRINT("rebuild hit %ld -> %ld", (long) j, (long) i));
                    new_h[j] = (duk_uint32_t) i;
                    break;
                }
                DUK_DDD(DUK_DDDPRINT("rebuild miss %ld, step %ld", (long) j, (long) step));
                j = (j + step) & mask;

                /* Guaranteed to finish (hash is larger than #props). */
            }
        }
    }
#endif  /* DUK_USE_HOBJECT_HASH_PART */

    /*
     *  Nice debug log.
     */

    DUK_DD(DUK_DDPRINT("resized hobject %p props (%ld -> %ld bytes), from {p=%p,e_size=%ld,e_next=%ld,a_size=%ld,h_size=%ld} to "
                       "{p=%p,e_size=%ld,e_next=%ld,a_size=%ld,h_size=%ld}, abandon_array=%ld, unadjusted new_e_size=%ld",
                       (void *) obj,
                       (long) DUK_HOBJECT_P_COMPUTE_SIZE(DUK_HOBJECT_GET_ESIZE(obj),
                                                         DUK_HOBJECT_GET_ASIZE(obj),
                                                         DUK_HOBJECT_GET_HSIZE(obj)),
                       (long) new_alloc_size,
                       (void *) DUK_HOBJECT_GET_PROPS(thr->heap, obj),
                       (long) DUK_HOBJECT_GET_ESIZE(obj),
                       (long) DUK_HOBJECT_GET_ENEXT(obj),
                       (long) DUK_HOBJECT_GET_ASIZE(obj),
                       (long) DUK_HOBJECT_GET_HSIZE(obj),
                       (void *) new_p,
                       (long) new_e_size_adjusted,
                       (long) new_e_next,
                       (long) new_a_size,
                       (long) new_h_size,
                       (long) abandon_array,
                       (long) new_e_size));

    /*
     *  All done, switch properties ('p') allocation to new one.
     */

    DUK_FREE_CHECKED(thr, DUK_HOBJECT_GET_PROPS(thr->heap, obj));  /* NULL obj->p is OK */
    DUK_HOBJECT_SET_PROPS(thr->heap, obj, new_p);
    DUK_HOBJECT_SET_ESIZE(obj, new_e_size_adjusted);
    DUK_HOBJECT_SET_ENEXT(obj, new_e_next);
    DUK_HOBJECT_SET_ASIZE(obj, new_a_size);
    DUK_HOBJECT_SET_HSIZE(obj, new_h_size);

    /* Clear array part flag only after switching. */
    if (abandon_array) {
        DUK_HOBJECT_CLEAR_ARRAY_PART(obj);
    }

    DUK_DDD(DUK_DDDPRINT("resize result: %!O", (duk_heaphdr *) obj));

    DUK_ASSERT(thr->heap->pf_prevent_count > 0);
    thr->heap->pf_prevent_count--;
    thr->heap->ms_base_flags = prev_ms_base_flags;
#if defined(DUK_USE_ASSERTIONS)
    DUK_ASSERT(thr->heap->error_not_allowed == 1);
    thr->heap->error_not_allowed = prev_error_not_allowed;
#endif

    /*
     *  Post resize assertions.
     */

#if defined(DUK_USE_ASSERTIONS)
    /* XXX: post-checks (such as no duplicate keys) */
#endif
    return;

    /*
     *  Abandon array failed.  We don't need to DECREF anything
     *  because the references in the new allocation are not
     *  INCREF'd until abandon is complete.  The string interned
     *  keys are on the value stack and are handled normally by
     *  unwind.
     */

 abandon_error:
 alloc_failed:
    DUK_D(DUK_DPRINT("object property table resize failed"));

    DUK_FREE_CHECKED(thr, new_p);  /* OK for NULL. */

    thr->heap->pf_prevent_count--;
    thr->heap->ms_base_flags = prev_ms_base_flags;
#if defined(DUK_USE_ASSERTIONS)
    DUK_ASSERT(thr->heap->error_not_allowed == 1);
    thr->heap->error_not_allowed = prev_error_not_allowed;
#endif

    DUK_ERROR_ALLOC_FAILED(thr);
    DUK_WO_NORETURN(return;);
}

/*
 *  Helpers to resize properties allocation on specific needs.
 */

DUK_INTERNAL void duk_hobject_resize_entrypart(duk_hthread *thr,
                                               duk_hobject *obj,
                                               duk_uint32_t new_e_size) {
    duk_uint32_t old_e_size;
    duk_uint32_t new_a_size;
    duk_uint32_t new_h_size;

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

    old_e_size = DUK_HOBJECT_GET_ESIZE(obj);
    if (old_e_size > new_e_size) {
        new_e_size = old_e_size;
    }
#if defined(DUK_USE_HOBJECT_HASH_PART)
    new_h_size = duk__get_default_h_size(new_e_size);
#else
    new_h_size = 0;
#endif
    new_a_size = DUK_HOBJECT_GET_ASIZE(obj);

    duk_hobject_realloc_props(thr, obj, new_e_size, new_a_size, new_h_size, 0);
}

/* Grow entry part allocation for one additional entry. */
DUK_LOCAL void duk__grow_props_for_new_entry_item(duk_hthread *thr, duk_hobject *obj) {
    duk_uint32_t old_e_used;  /* actually used, non-NULL entries */
    duk_uint32_t new_e_size_minimum;
    duk_uint32_t new_e_size;
    duk_uint32_t new_a_size;
    duk_uint32_t new_h_size;

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

    /* Duktape 0.11.0 and prior tried to optimize the resize by not
     * counting the number of actually used keys prior to the resize.
     * This worked mostly well but also caused weird leak-like behavior
     * as in: test-bug-object-prop-alloc-unbounded.js.  So, now we count
     * the keys explicitly to compute the new entry part size.
     */

    old_e_used = duk__count_used_e_keys(thr, obj);
    new_e_size_minimum = old_e_used + 1;
    new_e_size = old_e_used + duk__get_min_grow_e(old_e_used);
#if defined(DUK_USE_HOBJECT_HASH_PART)
    new_h_size = duk__get_default_h_size(new_e_size);
#else
    new_h_size = 0;
#endif
    new_a_size = DUK_HOBJECT_GET_ASIZE(obj);

#if defined(DUK_USE_OBJSIZES16)
    if (new_e_size > DUK_UINT16_MAX) {
        new_e_size = DUK_UINT16_MAX;
    }
    if (new_h_size > DUK_UINT16_MAX) {
        new_h_size = DUK_UINT16_MAX;
    }
    if (new_a_size > DUK_UINT16_MAX) {
        new_a_size = DUK_UINT16_MAX;
    }
#endif
    DUK_ASSERT(new_h_size == 0 || new_h_size >= new_e_size);

    if (!(new_e_size >= new_e_size_minimum)) {
        DUK_ERROR_ALLOC_FAILED(thr);
        DUK_WO_NORETURN(return;);
    }

    duk_hobject_realloc_props(thr, obj, new_e_size, new_a_size, new_h_size, 0);
}

/* Grow array part for a new highest array index. */
DUK_LOCAL void duk__grow_props_for_array_item(duk_hthread *thr, duk_hobject *obj, duk_uint32_t highest_arr_idx) {
    duk_uint32_t new_e_size;
    duk_uint32_t new_a_size;
    duk_uint32_t new_a_size_minimum;
    duk_uint32_t new_h_size;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(obj != NULL);
    DUK_ASSERT(highest_arr_idx >= DUK_HOBJECT_GET_ASIZE(obj));

    new_e_size = DUK_HOBJECT_GET_ESIZE(obj);
    new_h_size = DUK_HOBJECT_GET_HSIZE(obj);
    new_a_size_minimum = highest_arr_idx + 1;
    new_a_size = highest_arr_idx + duk__get_min_grow_a(highest_arr_idx);
    DUK_ASSERT(new_a_size >= highest_arr_idx + 1);  /* duk__get_min_grow_a() is always >= 1 */

#if defined(DUK_USE_OBJSIZES16)
    if (new_e_size > DUK_UINT16_MAX) {
        new_e_size = DUK_UINT16_MAX;
    }
    if (new_h_size > DUK_UINT16_MAX) {
        new_h_size = DUK_UINT16_MAX;
    }
    if (new_a_size > DUK_UINT16_MAX) {
        new_a_size = DUK_UINT16_MAX;
    }
#endif

    if (!(new_a_size >= new_a_size_minimum)) {
        DUK_ERROR_ALLOC_FAILED(thr);
        DUK_WO_NORETURN(return;);
    }

    duk_hobject_realloc_props(thr, obj, new_e_size, new_a_size, new_h_size, 0);
}

/* Abandon array part, moving array entries into entries part.
 * This requires a props resize, which is a heavy operation.
 * We also compact the entries part while we're at it, although
 * this is not strictly required.
 */
DUK_LOCAL void duk__abandon_array_part(duk_hthread *thr, duk_hobject *obj) {
    duk_uint32_t new_e_size_minimum;
    duk_uint32_t new_e_size;
    duk_uint32_t new_a_size;
    duk_uint32_t new_h_size;
    duk_uint32_t e_used;  /* actually used, non-NULL keys */
    duk_uint32_t a_used;
    duk_uint32_t a_size;

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

    e_used = duk__count_used_e_keys(thr, obj);
    duk__compute_a_stats(thr, obj, &a_used, &a_size);

    /*
     *  Must guarantee all actually used array entries will fit into
     *  new entry part.  Add one growth step to ensure we don't run out
     *  of space right away.
     */

    new_e_size_minimum = e_used + a_used;
    new_e_size = new_e_size_minimum + duk__get_min_grow_e(new_e_size_minimum);
    new_a_size = 0;
#if defined(DUK_USE_HOBJECT_HASH_PART)
    new_h_size = duk__get_default_h_size(new_e_size);
#else
    new_h_size = 0;
#endif

#if defined(DUK_USE_OBJSIZES16)
    if (new_e_size > DUK_UINT16_MAX) {
        new_e_size = DUK_UINT16_MAX;
    }
    if (new_h_size > DUK_UINT16_MAX) {
        new_h_size = DUK_UINT16_MAX;
    }
    if (new_a_size > DUK_UINT16_MAX) {
        new_a_size = DUK_UINT16_MAX;
    }
#endif

    if (!(new_e_size >= new_e_size_minimum)) {
        DUK_ERROR_ALLOC_FAILED(thr);
        DUK_WO_NORETURN(return;);
    }

    DUK_DD(DUK_DDPRINT("abandon array part for hobject %p, "
                       "array stats before: e_used=%ld, a_used=%ld, a_size=%ld; "
                       "resize to e_size=%ld, a_size=%ld, h_size=%ld",
                       (void *) obj, (long) e_used, (long) a_used, (long) a_size,
                       (long) new_e_size, (long) new_a_size, (long) new_h_size));

    duk_hobject_realloc_props(thr, obj, new_e_size, new_a_size, new_h_size, 1);
}

/*
 *  Compact an object.  Minimizes allocation size for objects which are
 *  not likely to be extended.  This is useful for internal and non-
 *  extensible objects, but can also be called for non-extensible objects.
 *  May abandon the array part if it is computed to be too sparse.
 *
 *  This call is relatively expensive, as it needs to scan both the
 *  entries and the array part.
 *
 *  The call may fail due to allocation error.
 */

DUK_INTERNAL void duk_hobject_compact_props(duk_hthread *thr, duk_hobject *obj) {
    duk_uint32_t e_size;       /* currently used -> new size */
    duk_uint32_t a_size;       /* currently required */
    duk_uint32_t a_used;       /* actually used */
    duk_uint32_t h_size;
    duk_bool_t abandon_array;

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

#if defined(DUK_USE_ROM_OBJECTS)
    if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)) {
        DUK_DD(DUK_DDPRINT("ignore attempt to compact a rom object"));
        return;
    }
#endif

    e_size = duk__count_used_e_keys(thr, obj);
    duk__compute_a_stats(thr, obj, &a_used, &a_size);

    DUK_DD(DUK_DDPRINT("compacting hobject, used e keys %ld, used a keys %ld, min a size %ld, "
                       "resized array density would be: %ld/%ld = %lf",
                       (long) e_size, (long) a_used, (long) a_size,
                       (long) a_used, (long) a_size,
                       (double) a_used / (double) a_size));

    if (duk__abandon_array_density_check(a_used, a_size)) {
        DUK_DD(DUK_DDPRINT("decided to abandon array during compaction, a_used=%ld, a_size=%ld",
                           (long) a_used, (long) a_size));
        abandon_array = 1;
        e_size += a_used;
        a_size = 0;
    } else {
        DUK_DD(DUK_DDPRINT("decided to keep array during compaction"));
        abandon_array = 0;
    }

#if defined(DUK_USE_HOBJECT_HASH_PART)
    if (e_size >= DUK_USE_HOBJECT_HASH_PROP_LIMIT) {
        h_size = duk__get_default_h_size(e_size);
    } else {
        h_size = 0;
    }
#else
    h_size = 0;
#endif

    DUK_DD(DUK_DDPRINT("compacting hobject -> new e_size %ld, new a_size=%ld, new h_size=%ld, abandon_array=%ld",
                       (long) e_size, (long) a_size, (long) h_size, (long) abandon_array));

    duk_hobject_realloc_props(thr, obj, e_size, a_size, h_size, abandon_array);
}

/*
 *  Find an existing key from entry part either by linear scan or by
 *  using the hash index (if it exists).
 *
 *  Sets entry index (and possibly the hash index) to output variables,
 *  which allows the caller to update the entry and hash entries in-place.
 *  If entry is not found, both values are set to -1.  If entry is found
 *  but there is no hash part, h_idx is set to -1.
 */

DUK_INTERNAL duk_bool_t duk_hobject_find_entry(duk_heap *heap, duk_hobject *obj, duk_hstring *key, duk_int_t *e_idx, duk_int_t *h_idx) {
    DUK_ASSERT(obj != NULL);
    DUK_ASSERT(key != NULL);
    DUK_ASSERT(e_idx != NULL);
    DUK_ASSERT(h_idx != NULL);
    DUK_UNREF(heap);

    if (DUK_LIKELY(DUK_HOBJECT_GET_HSIZE(obj) == 0))
    {
        /* Linear scan: more likely because most objects are small.
         * This is an important fast path.
         *
         * XXX: this might be worth inlining for property lookups.
         */
        duk_uint_fast32_t i;
        duk_uint_fast32_t n;
        duk_hstring **h_keys_base;
        DUK_DDD(DUK_DDDPRINT("duk_hobject_find_entry() using linear scan for lookup"));

        h_keys_base = DUK_HOBJECT_E_GET_KEY_BASE(heap, obj);
        n = DUK_HOBJECT_GET_ENEXT(obj);
        for (i = 0; i < n; i++) {
            if (h_keys_base[i] == key) {
                *e_idx = (duk_int_t) i;
                *h_idx = -1;
                return 1;
            }
        }
    }
#if defined(DUK_USE_HOBJECT_HASH_PART)
    else
    {
        /* hash lookup */
        duk_uint32_t n;
        duk_uint32_t i, step;
        duk_uint32_t *h_base;
        duk_uint32_t mask;

        DUK_DDD(DUK_DDDPRINT("duk_hobject_find_entry() using hash part for lookup"));

        h_base = DUK_HOBJECT_H_GET_BASE(heap, obj);
        n = DUK_HOBJECT_GET_HSIZE(obj);
        mask = n - 1;
        i = DUK_HSTRING_GET_HASH(key) & mask;
        step = 1;  /* Cache friendly but clustering prone. */

        for (;;) {
            duk_uint32_t t;

            DUK_ASSERT_DISABLE(i >= 0);  /* unsigned */
            DUK_ASSERT(i < DUK_HOBJECT_GET_HSIZE(obj));
            t = h_base[i];
            DUK_ASSERT(t == DUK__HASH_UNUSED || t == DUK__HASH_DELETED ||
                       (t < DUK_HOBJECT_GET_ESIZE(obj)));  /* t >= 0 always true, unsigned */

            if (t == DUK__HASH_UNUSED) {
                break;
            } else if (t == DUK__HASH_DELETED) {
                DUK_DDD(DUK_DDDPRINT("lookup miss (deleted) i=%ld, t=%ld",
                                     (long) i, (long) t));
            } else {
                DUK_ASSERT(t < DUK_HOBJECT_GET_ESIZE(obj));
                if (DUK_HOBJECT_E_GET_KEY(heap, obj, t) == key) {
                    DUK_DDD(DUK_DDDPRINT("lookup hit i=%ld, t=%ld -> key %p",
                                         (long) i, (long) t, (void *) key));
                    *e_idx = (duk_int_t) t;
                    *h_idx = (duk_int_t) i;
                    return 1;
                }
                DUK_DDD(DUK_DDDPRINT("lookup miss i=%ld, t=%ld",
                                     (long) i, (long) t));
            }
            i = (i + step) & mask;

            /* Guaranteed to finish (hash is larger than #props). */
        }
    }
#endif  /* DUK_USE_HOBJECT_HASH_PART */

    /* Not found, leave e_idx and h_idx unset. */
    return 0;
}

/* For internal use: get non-accessor entry value */
DUK_INTERNAL duk_tval *duk_hobject_find_entry_tval_ptr(duk_heap *heap, duk_hobject *obj, duk_hstring *key) {
    duk_int_t e_idx;
    duk_int_t h_idx;

    DUK_ASSERT(obj != NULL);
    DUK_ASSERT(key != NULL);
    DUK_UNREF(heap);

    if (duk_hobject_find_entry(heap, obj, key, &e_idx, &h_idx)) {
        DUK_ASSERT(e_idx >= 0);
        if (!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(heap, obj, e_idx)) {
            return DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(heap, obj, e_idx);
        }
    }
    return NULL;
}

DUK_INTERNAL duk_tval *duk_hobject_find_entry_tval_ptr_stridx(duk_heap *heap, duk_hobject *obj, duk_small_uint_t stridx) {
    return duk_hobject_find_entry_tval_ptr(heap, obj, DUK_HEAP_GET_STRING(heap, stridx));
}

/* For internal use: get non-accessor entry value and attributes */
DUK_INTERNAL duk_tval *duk_hobject_find_entry_tval_ptr_and_attrs(duk_heap *heap, duk_hobject *obj, duk_hstring *key, duk_uint_t *out_attrs) {
    duk_int_t e_idx;
    duk_int_t h_idx;

    DUK_ASSERT(obj != NULL);
    DUK_ASSERT(key != NULL);
    DUK_ASSERT(out_attrs != NULL);
    DUK_UNREF(heap);

    if (duk_hobject_find_entry(heap, obj, key, &e_idx, &h_idx)) {
        DUK_ASSERT(e_idx >= 0);
        if (!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(heap, obj, e_idx)) {
            *out_attrs = DUK_HOBJECT_E_GET_FLAGS(heap, obj, e_idx);
            return DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(heap, obj, e_idx);
        }
    }
    /* If not found, out_attrs is left unset. */
    return NULL;
}

/* For internal use: get array part value */
DUK_INTERNAL duk_tval *duk_hobject_find_array_entry_tval_ptr(duk_heap *heap, duk_hobject *obj, duk_uarridx_t i) {
    duk_tval *tv;

    DUK_ASSERT(obj != NULL);
    DUK_UNREF(heap);

    if (!DUK_HOBJECT_HAS_ARRAY_PART(obj)) {
        return NULL;
    }
    if (i >= DUK_HOBJECT_GET_ASIZE(obj)) {
        return NULL;
    }
    tv = DUK_HOBJECT_A_GET_VALUE_PTR(heap, obj, i);
    return tv;
}

/*
 *  Allocate and initialize a new entry, resizing the properties allocation
 *  if necessary.  Returns entry index (e_idx) or throws an error if alloc fails.
 *
 *  Sets the key of the entry (increasing the key's refcount), and updates
 *  the hash part if it exists.  Caller must set value and flags, and update
 *  the entry value refcount.  A decref for the previous value is not necessary.
 */

DUK_LOCAL duk_int_t duk__hobject_alloc_entry_checked(duk_hthread *thr, duk_hobject *obj, duk_hstring *key) {
    duk_uint32_t idx;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(obj != NULL);
    DUK_ASSERT(key != NULL);
    DUK_ASSERT(DUK_HOBJECT_GET_ENEXT(obj) <= DUK_HOBJECT_GET_ESIZE(obj));

#if defined(DUK_USE_ASSERTIONS)
    /* key must not already exist in entry part */
    {
        duk_uint_fast32_t i;
        for (i = 0; i < DUK_HOBJECT_GET_ENEXT(obj); i++) {
            DUK_ASSERT(DUK_HOBJECT_E_GET_KEY(thr->heap, obj, i) != key);
        }
    }
#endif

    if (DUK_HOBJECT_GET_ENEXT(obj) >= DUK_HOBJECT_GET_ESIZE(obj)) {
        /* only need to guarantee 1 more slot, but allocation growth is in chunks */
        DUK_DDD(DUK_DDDPRINT("entry part full, allocate space for one more entry"));
        duk__grow_props_for_new_entry_item(thr, obj);
    }
    DUK_ASSERT(DUK_HOBJECT_GET_ENEXT(obj) < DUK_HOBJECT_GET_ESIZE(obj));
    idx = DUK_HOBJECT_POSTINC_ENEXT(obj);

    /* previous value is assumed to be garbage, so don't touch it */
    DUK_HOBJECT_E_SET_KEY(thr->heap, obj, idx, key);
    DUK_HSTRING_INCREF(thr, key);

#if defined(DUK_USE_HOBJECT_HASH_PART)
    if (DUK_UNLIKELY(DUK_HOBJECT_GET_HSIZE(obj) > 0)) {
        duk_uint32_t n, mask;
        duk_uint32_t i, step;
        duk_uint32_t *h_base = DUK_HOBJECT_H_GET_BASE(thr->heap, obj);

        n = DUK_HOBJECT_GET_HSIZE(obj);
        mask = n - 1;
        i = DUK_HSTRING_GET_HASH(key) & mask;
        step = 1;  /* Cache friendly but clustering prone. */

        for (;;) {
            duk_uint32_t t = h_base[i];
            if (t == DUK__HASH_UNUSED || t == DUK__HASH_DELETED) {
                DUK_DDD(DUK_DDDPRINT("duk__hobject_alloc_entry_checked() inserted key into hash part, %ld -> %ld",
                                     (long) i, (long) idx));
                DUK_ASSERT_DISABLE(i >= 0);  /* unsigned */
                DUK_ASSERT(i < DUK_HOBJECT_GET_HSIZE(obj));
                DUK_ASSERT_DISABLE(idx >= 0);
                DUK_ASSERT(idx < DUK_HOBJECT_GET_ESIZE(obj));
                h_base[i] = idx;
                break;
            }
            DUK_DDD(DUK_DDDPRINT("duk__hobject_alloc_entry_checked() miss %ld", (long) i));
            i = (i + step) & mask;

            /* Guaranteed to finish (hash is larger than #props). */
        }
    }
#endif  /* DUK_USE_HOBJECT_HASH_PART */

    /* Note: we could return the hash index here too, but it's not
     * needed right now.
     */

    DUK_ASSERT_DISABLE(idx >= 0);
    DUK_ASSERT(idx < DUK_HOBJECT_GET_ESIZE(obj));
    DUK_ASSERT(idx < DUK_HOBJECT_GET_ENEXT(obj));
    return (duk_int_t) idx;
}

/*
 *  Object internal value
 *
 *  Returned value is guaranteed to be reachable / incref'd, caller does not need
 *  to incref OR decref.  No proxies or accessors are invoked, no prototype walk.
 */

DUK_INTERNAL duk_tval *duk_hobject_get_internal_value_tval_ptr(duk_heap *heap, duk_hobject *obj) {
    return duk_hobject_find_entry_tval_ptr_stridx(heap, obj, DUK_STRIDX_INT_VALUE);
}

DUK_LOCAL duk_heaphdr *duk_hobject_get_internal_value_heaphdr(duk_heap *heap, duk_hobject *obj) {
    duk_tval *tv;

    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(obj != NULL);

    tv = duk_hobject_get_internal_value_tval_ptr(heap, obj);
    if (tv != NULL) {
        duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv);
        DUK_ASSERT(h != NULL);
        return h;
    }

    return NULL;
}

DUK_INTERNAL duk_hstring *duk_hobject_get_internal_value_string(duk_heap *heap, duk_hobject *obj) {
    duk_hstring *h;

    h = (duk_hstring *) duk_hobject_get_internal_value_heaphdr(heap, obj);
    if (h != NULL) {
        DUK_ASSERT(DUK_HEAPHDR_IS_STRING((duk_heaphdr *) h));
    }
    return h;
}

DUK_LOCAL duk_hobject *duk__hobject_get_entry_object_stridx(duk_heap *heap, duk_hobject *obj, duk_small_uint_t stridx) {
    duk_tval *tv;
    duk_hobject *h;

    tv = duk_hobject_find_entry_tval_ptr_stridx(heap, obj, stridx);
    if (tv != NULL && DUK_TVAL_IS_OBJECT(tv)) {
        h = DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(h != NULL);
        return h;
    }
    return NULL;
}

DUK_INTERNAL duk_harray *duk_hobject_get_formals(duk_hthread *thr, duk_hobject *obj) {
    duk_harray *h;

    h = (duk_harray *) duk__hobject_get_entry_object_stridx(thr->heap, obj, DUK_STRIDX_INT_FORMALS);
    if (h != NULL) {
        DUK_ASSERT(DUK_HOBJECT_IS_ARRAY((duk_hobject *) h));
        DUK_ASSERT(h->length <= DUK_HOBJECT_GET_ASIZE((duk_hobject *) h));
    }
    return h;
}

DUK_INTERNAL duk_hobject *duk_hobject_get_varmap(duk_hthread *thr, duk_hobject *obj) {
    duk_hobject *h;

    h = duk__hobject_get_entry_object_stridx(thr->heap, obj, DUK_STRIDX_INT_VARMAP);
    return h;
}

/*
 *  Arguments handling helpers (argument map mainly).
 *
 *  An arguments object has exotic behavior for some numeric indices.
 *  Accesses may translate to identifier operations which may have
 *  arbitrary side effects (potentially invalidating any duk_tval
 *  pointers).
 */

/* Lookup 'key' from arguments internal 'map', perform a variable lookup
 * if mapped, and leave the result on top of stack (and return non-zero).
 * Used in E5 Section 10.6 algorithms [[Get]] and [[GetOwnProperty]].
 */
DUK_LOCAL
duk_bool_t duk__lookup_arguments_map(duk_hthread *thr,
                                     duk_hobject *obj,
                                     duk_hstring *key,
                                     duk_propdesc *temp_desc,
                                     duk_hobject **out_map,
                                     duk_hobject **out_varenv) {
    duk_hobject *map;
    duk_hobject *varenv;
    duk_bool_t rc;

    DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);

    DUK_DDD(DUK_DDDPRINT("arguments map lookup: thr=%p, obj=%p, key=%p, temp_desc=%p "
                         "(obj -> %!O, key -> %!O)",
                         (void *) thr, (void *) obj, (void *) key, (void *) temp_desc,
                         (duk_heaphdr *) obj, (duk_heaphdr *) key));

    if (!duk_hobject_get_own_propdesc(thr, obj, DUK_HTHREAD_STRING_INT_MAP(thr), temp_desc, DUK_GETDESC_FLAG_PUSH_VALUE)) {
        DUK_DDD(DUK_DDDPRINT("-> no 'map'"));
        return 0;
    }

    map = duk_require_hobject(thr, -1);
    DUK_ASSERT(map != NULL);
    duk_pop_unsafe(thr);  /* map is reachable through obj */

    if (!duk_hobject_get_own_propdesc(thr, map, key, temp_desc, DUK_GETDESC_FLAG_PUSH_VALUE)) {
        DUK_DDD(DUK_DDDPRINT("-> 'map' exists, but key not in map"));
        return 0;
    }

    /* [... varname] */
    DUK_DDD(DUK_DDDPRINT("-> 'map' exists, and contains key, key is mapped to argument/variable binding %!T",
                         (duk_tval *) duk_get_tval(thr, -1)));
    DUK_ASSERT(duk_is_string(thr, -1));  /* guaranteed when building arguments */

    /* get varenv for varname (callee's declarative lexical environment) */
    rc = duk_hobject_get_own_propdesc(thr, obj, DUK_HTHREAD_STRING_INT_VARENV(thr), temp_desc, DUK_GETDESC_FLAG_PUSH_VALUE);
    DUK_UNREF(rc);
    DUK_ASSERT(rc != 0);  /* arguments MUST have an initialized lexical environment reference */
    varenv = duk_require_hobject(thr, -1);
    DUK_ASSERT(varenv != NULL);
    duk_pop_unsafe(thr);  /* varenv remains reachable through 'obj' */

    DUK_DDD(DUK_DDDPRINT("arguments varenv is: %!dO", (duk_heaphdr *) varenv));

    /* success: leave varname in stack */
    *out_map = map;
    *out_varenv = varenv;
    return 1;  /* [... varname] */
}

/* Lookup 'key' from arguments internal 'map', and leave replacement value
 * on stack top if mapped (and return non-zero).
 * Used in E5 Section 10.6 algorithm for [[GetOwnProperty]] (used by [[Get]]).
 */
DUK_LOCAL duk_bool_t duk__check_arguments_map_for_get(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_propdesc *temp_desc) {
    duk_hobject *map;
    duk_hobject *varenv;
    duk_hstring *varname;

    DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);

    if (!duk__lookup_arguments_map(thr, obj, key, temp_desc, &map, &varenv)) {
        DUK_DDD(DUK_DDDPRINT("arguments: key not mapped, no exotic get behavior"));
        return 0;
    }

    /* [... varname] */

    varname = duk_require_hstring(thr, -1);
    DUK_ASSERT(varname != NULL);
    duk_pop_unsafe(thr);  /* varname is still reachable */

    DUK_DDD(DUK_DDDPRINT("arguments object automatic getvar for a bound variable; "
                         "key=%!O, varname=%!O",
                         (duk_heaphdr *) key,
                         (duk_heaphdr *) varname));

    (void) duk_js_getvar_envrec(thr, varenv, varname, 1 /*throw*/);

    /* [... value this_binding] */

    duk_pop_unsafe(thr);

    /* leave result on stack top */
    return 1;
}

/* Lookup 'key' from arguments internal 'map', perform a variable write if mapped.
 * Used in E5 Section 10.6 algorithm for [[DefineOwnProperty]] (used by [[Put]]).
 * Assumes stack top contains 'put' value (which is NOT popped).
 */
DUK_LOCAL void duk__check_arguments_map_for_put(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_propdesc *temp_desc, duk_bool_t throw_flag) {
    duk_hobject *map;
    duk_hobject *varenv;
    duk_hstring *varname;

    DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);

    if (!duk__lookup_arguments_map(thr, obj, key, temp_desc, &map, &varenv)) {
        DUK_DDD(DUK_DDDPRINT("arguments: key not mapped, no exotic put behavior"));
        return;
    }

    /* [... put_value varname] */

    varname = duk_require_hstring(thr, -1);
    DUK_ASSERT(varname != NULL);
    duk_pop_unsafe(thr);  /* varname is still reachable */

    DUK_DDD(DUK_DDDPRINT("arguments object automatic putvar for a bound variable; "
                         "key=%!O, varname=%!O, value=%!T",
                         (duk_heaphdr *) key,
                         (duk_heaphdr *) varname,
                         (duk_tval *) duk_require_tval(thr, -1)));

    /* [... put_value] */

    /*
     *  Note: although arguments object variable mappings are only established
     *  for non-strict functions (and a call to a non-strict function created
     *  the arguments object in question), an inner strict function may be doing
     *  the actual property write.  Hence the throw_flag applied here comes from
     *  the property write call.
     */

    duk_js_putvar_envrec(thr, varenv, varname, duk_require_tval(thr, -1), throw_flag);

    /* [... put_value] */
}

/* Lookup 'key' from arguments internal 'map', delete mapping if found.
 * Used in E5 Section 10.6 algorithm for [[Delete]].  Note that the
 * variable/argument itself (where the map points) is not deleted.
 */
DUK_LOCAL void duk__check_arguments_map_for_delete(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_propdesc *temp_desc) {
    duk_hobject *map;

    DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);

    if (!duk_hobject_get_own_propdesc(thr, obj, DUK_HTHREAD_STRING_INT_MAP(thr), temp_desc, DUK_GETDESC_FLAG_PUSH_VALUE)) {
        DUK_DDD(DUK_DDDPRINT("arguments: key not mapped, no exotic delete behavior"));
        return;
    }

    map = duk_require_hobject(thr, -1);
    DUK_ASSERT(map != NULL);
    duk_pop_unsafe(thr);  /* map is reachable through obj */

    DUK_DDD(DUK_DDDPRINT("-> have 'map', delete key %!O from map (if exists)); ignore result",
                         (duk_heaphdr *) key));

    /* Note: no recursion issue, we can trust 'map' to behave */
    DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_BEHAVIOR(map));
    DUK_DDD(DUK_DDDPRINT("map before deletion: %!O", (duk_heaphdr *) map));
    (void) duk_hobject_delprop_raw(thr, map, key, 0);  /* ignore result */
    DUK_DDD(DUK_DDDPRINT("map after deletion: %!O", (duk_heaphdr *) map));
}

/*
 *  ECMAScript compliant [[GetOwnProperty]](P), for internal use only.
 *
 *  If property is found:
 *    - Fills descriptor fields to 'out_desc'
 *    - If DUK_GETDESC_FLAG_PUSH_VALUE is set, pushes a value related to the
 *      property onto the stack ('undefined' for accessor properties).
 *    - Returns non-zero
 *
 *  If property is not found:
 *    - 'out_desc' is left in untouched state (possibly garbage)
 *    - Nothing is pushed onto the stack (not even with DUK_GETDESC_FLAG_PUSH_VALUE
 *      set)
 *    - Returns zero
 *
 *  Notes:
 *
 *    - Getting a property descriptor may cause an allocation (and hence
 *      GC) to take place, hence reachability and refcount of all related
 *      values matter.  Reallocation of value stack, properties, etc may
 *      invalidate many duk_tval pointers (concretely, those which reside
 *      in memory areas subject to reallocation).  However, heap object
 *      pointers are never affected (heap objects have stable pointers).
 *
 *    - The value of a plain property is always reachable and has a non-zero
 *      reference count.
 *
 *    - The value of a virtual property is not necessarily reachable from
 *      elsewhere and may have a refcount of zero.  Hence we push it onto
 *      the valstack for the caller, which ensures it remains reachable
 *      while it is needed.
 *
 *    - There are no virtual accessor properties.  Hence, all getters and
 *      setters are always related to concretely stored properties, which
 *      ensures that the get/set functions in the resulting descriptor are
 *      reachable and have non-zero refcounts.  Should there be virtual
 *      accessor properties later, this would need to change.
 */

DUK_LOCAL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_uint32_t arr_idx, duk_propdesc *out_desc, duk_small_uint_t flags) {
    duk_tval *tv;

    DUK_DDD(DUK_DDDPRINT("duk_hobject_get_own_propdesc: thr=%p, obj=%p, key=%p, out_desc=%p, flags=%lx, "
                         "arr_idx=%ld (obj -> %!O, key -> %!O)",
                         (void *) thr, (void *) obj, (void *) key, (void *) out_desc,
                         (long) flags, (long) arr_idx,
                         (duk_heaphdr *) obj, (duk_heaphdr *) key));

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT(obj != NULL);
    DUK_ASSERT(key != NULL);
    DUK_ASSERT(out_desc != NULL);
    DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);

    DUK_STATS_INC(thr->heap, stats_getownpropdesc_count);

    /* Each code path returning 1 (= found) must fill in all the output
     * descriptor fields.  We don't do it beforehand because it'd be
     * unnecessary work if the property isn't found and would happen
     * multiple times for an inheritance chain.
     */
    DUK_ASSERT_SET_GARBAGE(out_desc, sizeof(*out_desc));
#if 0
    out_desc->flags = 0;
    out_desc->get = NULL;
    out_desc->set = NULL;
    out_desc->e_idx = -1;
    out_desc->h_idx = -1;
    out_desc->a_idx = -1;
#endif

    /*
     *  Try entries part first because it's the common case.
     *
     *  Array part lookups are usually handled by the array fast path, and
     *  are not usually inherited.  Array and entry parts never contain the
     *  same keys so the entry part vs. array part order doesn't matter.
     */

    if (duk_hobject_find_entry(thr->heap, obj, key, &out_desc->e_idx, &out_desc->h_idx)) {
        duk_int_t e_idx = out_desc->e_idx;
        DUK_ASSERT(out_desc->e_idx >= 0);
        out_desc->a_idx = -1;
        out_desc->flags = DUK_HOBJECT_E_GET_FLAGS(thr->heap, obj, e_idx);
        out_desc->get = NULL;
        out_desc->set = NULL;
        if (DUK_UNLIKELY(out_desc->flags & DUK_PROPDESC_FLAG_ACCESSOR)) {
            DUK_DDD(DUK_DDDPRINT("-> found accessor property in entry part"));
            out_desc->get = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, obj, e_idx);
            out_desc->set = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, obj, e_idx);
            if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) {
                /* a dummy undefined value is pushed to make valstack
                 * behavior uniform for caller
                 */
                duk_push_undefined(thr);
            }
        } else {
            DUK_DDD(DUK_DDDPRINT("-> found plain property in entry part"));
            tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, e_idx);
            if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) {
                duk_push_tval(thr, tv);
            }
        }
        goto prop_found;
    }

    /*
     *  Try array part.
     */

    if (DUK_HOBJECT_HAS_ARRAY_PART(obj) && arr_idx != DUK__NO_ARRAY_INDEX) {
        if (arr_idx < DUK_HOBJECT_GET_ASIZE(obj)) {
            tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, arr_idx);
            if (!DUK_TVAL_IS_UNUSED(tv)) {
                DUK_DDD(DUK_DDDPRINT("-> found in array part"));
                if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) {
                    duk_push_tval(thr, tv);
                }
                /* implicit attributes */
                out_desc->flags = DUK_PROPDESC_FLAG_WRITABLE |
                                  DUK_PROPDESC_FLAG_CONFIGURABLE |
                                  DUK_PROPDESC_FLAG_ENUMERABLE;
                out_desc->get = NULL;
                out_desc->set = NULL;
                out_desc->e_idx = -1;
                out_desc->h_idx = -1;
                out_desc->a_idx = (duk_int_t) arr_idx;  /* XXX: limit 2G due to being signed */
                goto prop_found;
            }
        }
    }

    DUK_DDD(DUK_DDDPRINT("-> not found as a concrete property"));

    /*
     *  Not found as a concrete property, check for virtual properties.
     */

    if (!DUK_HOBJECT_HAS_VIRTUAL_PROPERTIES(obj)) {
        /* Quick skip. */
        goto prop_not_found;
    }

    if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)) {
        duk_harray *a;

        DUK_DDD(DUK_DDDPRINT("array object exotic property get for key: %!O, arr_idx: %ld",
                             (duk_heaphdr *) key, (long) arr_idx));

        a = (duk_harray *) obj;
        DUK_HARRAY_ASSERT_VALID(a);

        if (key == DUK_HTHREAD_STRING_LENGTH(thr)) {
            DUK_DDD(DUK_DDDPRINT("-> found, key is 'length', length exotic behavior"));

            if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) {
                duk_push_uint(thr, (duk_uint_t) a->length);
            }
            out_desc->flags = DUK_PROPDESC_FLAG_VIRTUAL;
            if (DUK_HARRAY_LENGTH_WRITABLE(a)) {
                out_desc->flags |= DUK_PROPDESC_FLAG_WRITABLE;
            }
            out_desc->get = NULL;
            out_desc->set = NULL;
            out_desc->e_idx = -1;
            out_desc->h_idx = -1;
            out_desc->a_idx = -1;

            DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj));
            goto prop_found_noexotic;  /* cannot be arguments exotic */
        }
    } else if (DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(obj)) {
        DUK_DDD(DUK_DDDPRINT("string object exotic property get for key: %!O, arr_idx: %ld",
                             (duk_heaphdr *) key, (long) arr_idx));

        /* XXX: charlen; avoid multiple lookups? */

        if (arr_idx != DUK__NO_ARRAY_INDEX) {
            duk_hstring *h_val;

            DUK_DDD(DUK_DDDPRINT("array index exists"));

            h_val = duk_hobject_get_internal_value_string(thr->heap, obj);
            DUK_ASSERT(h_val);
            if (arr_idx < DUK_HSTRING_GET_CHARLEN(h_val)) {
                DUK_DDD(DUK_DDDPRINT("-> found, array index inside string"));
                if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) {
                    duk_push_hstring(thr, h_val);
                    duk_substring(thr, -1, arr_idx, arr_idx + 1);  /* [str] -> [substr] */
                }
                out_desc->flags = DUK_PROPDESC_FLAG_ENUMERABLE |  /* E5 Section 15.5.5.2 */
                                  DUK_PROPDESC_FLAG_VIRTUAL;
                out_desc->get = NULL;
                out_desc->set = NULL;
                out_desc->e_idx = -1;
                out_desc->h_idx = -1;
                out_desc->a_idx = -1;

                DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj));
                goto prop_found_noexotic;  /* cannot be arguments exotic */
            } else {
                /* index is above internal string length -> property is fully normal */
                DUK_DDD(DUK_DDDPRINT("array index outside string -> normal property"));
            }
        } else if (key == DUK_HTHREAD_STRING_LENGTH(thr)) {
            duk_hstring *h_val;

            DUK_DDD(DUK_DDDPRINT("-> found, key is 'length', length exotic behavior"));

            h_val = duk_hobject_get_internal_value_string(thr->heap, obj);
            DUK_ASSERT(h_val != NULL);
            if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) {
                duk_push_uint(thr, (duk_uint_t) DUK_HSTRING_GET_CHARLEN(h_val));
            }
            out_desc->flags = DUK_PROPDESC_FLAG_VIRTUAL;  /* E5 Section 15.5.5.1 */
            out_desc->get = NULL;
            out_desc->set = NULL;
            out_desc->e_idx = -1;
            out_desc->h_idx = -1;
            out_desc->a_idx = -1;

            DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj));
            goto prop_found_noexotic;  /* cannot be arguments exotic */
        }
    }
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
    else if (DUK_HOBJECT_IS_BUFOBJ(obj)) {
        duk_hbufobj *h_bufobj;
        duk_uint_t byte_off;
        duk_small_uint_t elem_size;

        h_bufobj = (duk_hbufobj *) obj;
        DUK_HBUFOBJ_ASSERT_VALID(h_bufobj);
        DUK_DDD(DUK_DDDPRINT("bufobj property get for key: %!O, arr_idx: %ld",
                             (duk_heaphdr *) key, (long) arr_idx));

        if (arr_idx != DUK__NO_ARRAY_INDEX && DUK_HBUFOBJ_HAS_VIRTUAL_INDICES(h_bufobj)) {
            DUK_DDD(DUK_DDDPRINT("array index exists"));

            /* Careful with wrapping: arr_idx upshift may easily wrap, whereas
             * length downshift won't.
             */
            if (arr_idx < (h_bufobj->length >> h_bufobj->shift)) {
                byte_off = arr_idx << h_bufobj->shift;  /* no wrap assuming h_bufobj->length is valid */
                elem_size = (duk_small_uint_t) (1U << h_bufobj->shift);
                if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) {
                    duk_uint8_t *data;

                    if (h_bufobj->buf != NULL && DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_bufobj, byte_off + elem_size)) {
                        data = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_bufobj->buf) + h_bufobj->offset + byte_off;
                        duk_hbufobj_push_validated_read(thr, h_bufobj, data, elem_size);
                    } else {
                        DUK_D(DUK_DPRINT("bufobj access out of underlying buffer, ignoring (read zero)"));
                        duk_push_uint(thr, 0);
                    }
                }
                out_desc->flags = DUK_PROPDESC_FLAG_WRITABLE |
                                  DUK_PROPDESC_FLAG_VIRTUAL;
                if (DUK_HOBJECT_GET_CLASS_NUMBER(obj) != DUK_HOBJECT_CLASS_ARRAYBUFFER) {
                    /* ArrayBuffer indices are non-standard and are
                     * non-enumerable to avoid their serialization.
                     */
                    out_desc->flags |= DUK_PROPDESC_FLAG_ENUMERABLE;
                }
                out_desc->get = NULL;
                out_desc->set = NULL;
                out_desc->e_idx = -1;
                out_desc->h_idx = -1;
                out_desc->a_idx = -1;

                DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj));
                goto prop_found_noexotic;  /* cannot be e.g. arguments exotic, since exotic 'traits' are mutually exclusive */
            } else {
                /* index is above internal buffer length -> property is fully normal */
                DUK_DDD(DUK_DDDPRINT("array index outside buffer -> normal property"));
            }
        } else if (key == DUK_HTHREAD_STRING_LENGTH(thr) && DUK_HBUFOBJ_HAS_VIRTUAL_INDICES(h_bufobj)) {
            DUK_DDD(DUK_DDDPRINT("-> found, key is 'length', length exotic behavior"));

            if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) {
                /* Length in elements: take into account shift, but
                 * intentionally don't check the underlying buffer here.
                 */
                duk_push_uint(thr, h_bufobj->length >> h_bufobj->shift);
            }
            out_desc->flags = DUK_PROPDESC_FLAG_VIRTUAL;
            out_desc->get = NULL;
            out_desc->set = NULL;
            out_desc->e_idx = -1;
            out_desc->h_idx = -1;
            out_desc->a_idx = -1;

            DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj));
            goto prop_found_noexotic;  /* cannot be arguments exotic */
        }
    }
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

    /* Array properties have exotic behavior but they are concrete,
     * so no special handling here.
     *
     * Arguments exotic behavior (E5 Section 10.6, [[GetOwnProperty]]
     * is only relevant as a post-check implemented below; hence no
     * check here.
     */

    /*
     *  Not found as concrete or virtual.
     */

 prop_not_found:
    DUK_DDD(DUK_DDDPRINT("-> not found (virtual, entry part, or array part)"));
    DUK_STATS_INC(thr->heap, stats_getownpropdesc_miss);
    return 0;

    /*
     *  Found.
     *
     *  Arguments object has exotic post-processing, see E5 Section 10.6,
     *  description of [[GetOwnProperty]] variant for arguments.
     */

 prop_found:
    DUK_DDD(DUK_DDDPRINT("-> property found, checking for arguments exotic post-behavior"));

    /* Notes:
     *  - Only numbered indices are relevant, so arr_idx fast reject is good
     *    (this is valid unless there are more than 4**32-1 arguments).
     *  - Since variable lookup has no side effects, this can be skipped if
     *    DUK_GETDESC_FLAG_PUSH_VALUE is not set.
     */

    if (DUK_UNLIKELY(DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj) &&
                     arr_idx != DUK__NO_ARRAY_INDEX &&
                     (flags & DUK_GETDESC_FLAG_PUSH_VALUE))) {
        duk_propdesc temp_desc;

        /* Magically bound variable cannot be an accessor.  However,
         * there may be an accessor property (or a plain property) in
         * place with magic behavior removed.  This happens e.g. when
         * a magic property is redefined with defineProperty().
         * Cannot assert for "not accessor" here.
         */

        /* replaces top of stack with new value if necessary */
        DUK_ASSERT((flags & DUK_GETDESC_FLAG_PUSH_VALUE) != 0);

        /* This can perform a variable lookup but only into a declarative
         * environment which has no side effects.
         */
        if (duk__check_arguments_map_for_get(thr, obj, key, &temp_desc)) {
            DUK_DDD(DUK_DDDPRINT("-> arguments exotic behavior overrides result: %!T -> %!T",
                                 (duk_tval *) duk_get_tval(thr, -2),
                                 (duk_tval *) duk_get_tval(thr, -1)));
            /* [... old_result result] -> [... result] */
            duk_remove_m2(thr);
        }
    }

 prop_found_noexotic:
    DUK_STATS_INC(thr->heap, stats_getownpropdesc_hit);
    return 1;
}

DUK_INTERNAL duk_bool_t duk_hobject_get_own_propdesc(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_propdesc *out_desc, duk_small_uint_t flags) {
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(obj != NULL);
    DUK_ASSERT(key != NULL);
    DUK_ASSERT(out_desc != NULL);
    DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);

    return duk__get_own_propdesc_raw(thr, obj, key, DUK_HSTRING_GET_ARRIDX_SLOW(key), out_desc, flags);
}

/*
 *  ECMAScript compliant [[GetProperty]](P), for internal use only.
 *
 *  If property is found:
 *    - Fills descriptor fields to 'out_desc'
 *    - If DUK_GETDESC_FLAG_PUSH_VALUE is set, pushes a value related to the
 *      property onto the stack ('undefined' for accessor properties).
 *    - Returns non-zero
 *
 *  If property is not found:
 *    - 'out_desc' is left in untouched state (possibly garbage)
 *    - Nothing is pushed onto the stack (not even with DUK_GETDESC_FLAG_PUSH_VALUE
 *      set)
 *    - Returns zero
 *
 *  May cause arbitrary side effects and invalidate (most) duk_tval
 *  pointers.
 */

DUK_LOCAL duk_bool_t duk__get_propdesc(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_propdesc *out_desc, duk_small_uint_t flags) {
    duk_hobject *curr;
    duk_uint32_t arr_idx;
    duk_uint_t sanity;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT(obj != NULL);
    DUK_ASSERT(key != NULL);
    DUK_ASSERT(out_desc != NULL);
    DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);

    DUK_STATS_INC(thr->heap, stats_getpropdesc_count);

    arr_idx = DUK_HSTRING_GET_ARRIDX_FAST(key);

    DUK_DDD(DUK_DDDPRINT("duk__get_propdesc: thr=%p, obj=%p, key=%p, out_desc=%p, flags=%lx, "
                         "arr_idx=%ld (obj -> %!O, key -> %!O)",
                         (void *) thr, (void *) obj, (void *) key, (void *) out_desc,
                         (long) flags, (long) arr_idx,
                         (duk_heaphdr *) obj, (duk_heaphdr *) key));

    curr = obj;
    DUK_ASSERT(curr != NULL);
    sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
    do {
        if (duk__get_own_propdesc_raw(thr, curr, key, arr_idx, out_desc, flags)) {
            /* stack contains value (if requested), 'out_desc' is set */
            DUK_STATS_INC(thr->heap, stats_getpropdesc_hit);
            return 1;
        }

        /* not found in 'curr', next in prototype chain; impose max depth */
        if (DUK_UNLIKELY(sanity-- == 0)) {
            if (flags & DUK_GETDESC_FLAG_IGNORE_PROTOLOOP) {
                /* treat like property not found */
                break;
            } else {
                DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT);
                DUK_WO_NORETURN(return 0;);
            }
        }
        curr = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, curr);
    } while (curr != NULL);

    /* out_desc is left untouched (possibly garbage), caller must use return
     * value to determine whether out_desc can be looked up
     */

    DUK_STATS_INC(thr->heap, stats_getpropdesc_miss);
    return 0;
}

/*
 *  Shallow fast path checks for accessing array elements with numeric
 *  indices.  The goal is to try to avoid coercing an array index to an
 *  (interned) string for the most common lookups, in particular, for
 *  standard Array objects.
 *
 *  Interning is avoided but only for a very narrow set of cases:
 *    - Object has array part, index is within array allocation, and
 *      value is not unused (= key exists)
 *    - Object has no interfering exotic behavior (e.g. arguments or
 *      string object exotic behaviors interfere, array exotic
 *      behavior does not).
 *
 *  Current shortcoming: if key does not exist (even if it is within
 *  the array allocation range) a slow path lookup with interning is
 *  always required.  This can probably be fixed so that there is a
 *  quick fast path for non-existent elements as well, at least for
 *  standard Array objects.
 */

#if defined(DUK_USE_ARRAY_PROP_FASTPATH)
DUK_LOCAL duk_tval *duk__getprop_shallow_fastpath_array_tval(duk_hthread *thr, duk_hobject *obj, duk_tval *tv_key) {
    duk_tval *tv;
    duk_uint32_t idx;

    DUK_UNREF(thr);

    if (!(DUK_HOBJECT_HAS_ARRAY_PART(obj) &&
         !DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj) &&
         !DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(obj) &&
         !DUK_HOBJECT_IS_BUFOBJ(obj) &&
         !DUK_HOBJECT_IS_PROXY(obj))) {
        /* Must have array part and no conflicting exotic behaviors.
         * Doesn't need to have array special behavior, e.g. Arguments
         * object has array part.
         */
        return NULL;
    }

    /* Arrays never have other exotic behaviors. */

    DUK_DDD(DUK_DDDPRINT("fast path attempt (no exotic string/arguments/buffer "
                         "behavior, object has array part)"));

#if defined(DUK_USE_FASTINT)
    if (DUK_TVAL_IS_FASTINT(tv_key)) {
        idx = duk__tval_fastint_to_arr_idx(tv_key);
    } else
#endif
    if (DUK_TVAL_IS_DOUBLE(tv_key)) {
        idx = duk__tval_number_to_arr_idx(tv_key);
    } else {
        DUK_DDD(DUK_DDDPRINT("key is not a number"));
        return NULL;
    }

    /* If index is not valid, idx will be DUK__NO_ARRAY_INDEX which
     * is 0xffffffffUL.  We don't need to check for that explicitly
     * because 0xffffffffUL will never be inside object 'a_size'.
     */

    if (idx >= DUK_HOBJECT_GET_ASIZE(obj)) {
        DUK_DDD(DUK_DDDPRINT("key is not an array index or outside array part"));
        return NULL;
    }
    DUK_ASSERT(idx != 0xffffffffUL);
    DUK_ASSERT(idx != DUK__NO_ARRAY_INDEX);

    /* XXX: for array instances we could take a shortcut here and assume
     * Array.prototype doesn't contain an array index property.
     */

    DUK_DDD(DUK_DDDPRINT("key is a valid array index and inside array part"));
    tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, idx);
    if (!DUK_TVAL_IS_UNUSED(tv)) {
        DUK_DDD(DUK_DDDPRINT("-> fast path successful"));
        return tv;
    }

    DUK_DDD(DUK_DDDPRINT("fast path attempt failed, fall back to slow path"));
    return NULL;
}

DUK_LOCAL duk_bool_t duk__putprop_shallow_fastpath_array_tval(duk_hthread *thr, duk_hobject *obj, duk_tval *tv_key, duk_tval *tv_val) {
    duk_tval *tv;
    duk_harray *a;
    duk_uint32_t idx;
    duk_uint32_t old_len, new_len;

    if (!(DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj) &&
          DUK_HOBJECT_HAS_ARRAY_PART(obj) &&
          DUK_HOBJECT_HAS_EXTENSIBLE(obj))) {
        return 0;
    }
    DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj));  /* caller ensures */

    a = (duk_harray *) obj;
    DUK_HARRAY_ASSERT_VALID(a);

#if defined(DUK_USE_FASTINT)
    if (DUK_TVAL_IS_FASTINT(tv_key)) {
        idx = duk__tval_fastint_to_arr_idx(tv_key);
    } else
#endif
    if (DUK_TVAL_IS_DOUBLE(tv_key)) {
        idx = duk__tval_number_to_arr_idx(tv_key);
    } else {
        DUK_DDD(DUK_DDDPRINT("key is not a number"));
        return 0;
    }

    /* If index is not valid, idx will be DUK__NO_ARRAY_INDEX which
     * is 0xffffffffUL.  We don't need to check for that explicitly
     * because 0xffffffffUL will never be inside object 'a_size'.
     */

    if (idx >= DUK_HOBJECT_GET_ASIZE(obj)) {  /* for resizing of array part, use slow path */
        return 0;
    }
    DUK_ASSERT(idx != 0xffffffffUL);
    DUK_ASSERT(idx != DUK__NO_ARRAY_INDEX);

    old_len = a->length;

    if (idx >= old_len) {
        DUK_DDD(DUK_DDDPRINT("write new array entry requires length update "
                             "(arr_idx=%ld, old_len=%ld)",
                             (long) idx, (long) old_len));
        if (DUK_HARRAY_LENGTH_NONWRITABLE(a)) {
            /* The correct behavior here is either a silent error
             * or a TypeError, depending on strictness.  Fall back
             * to the slow path to handle the situation.
             */
            return 0;
        }
        new_len = idx + 1;

        ((duk_harray *) obj)->length = new_len;
    }

    tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, idx);
    DUK_TVAL_SET_TVAL_UPDREF(thr, tv, tv_val);  /* side effects */

    DUK_DDD(DUK_DDDPRINT("array fast path success for index %ld", (long) idx));
    return 1;
}
#endif  /* DUK_USE_ARRAY_PROP_FASTPATH */

/*
 *  Fast path for bufobj getprop/putprop
 */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_LOCAL duk_bool_t duk__getprop_fastpath_bufobj_tval(duk_hthread *thr, duk_hobject *obj, duk_tval *tv_key) {
    duk_uint32_t idx;
    duk_hbufobj *h_bufobj;
    duk_uint_t byte_off;
    duk_small_uint_t elem_size;
    duk_uint8_t *data;

    if (!DUK_HOBJECT_IS_BUFOBJ(obj)) {
        return 0;
    }
    h_bufobj = (duk_hbufobj *) obj;
    if (!DUK_HBUFOBJ_HAS_VIRTUAL_INDICES(h_bufobj)) {
        return 0;
    }

#if defined(DUK_USE_FASTINT)
    if (DUK_TVAL_IS_FASTINT(tv_key)) {
        idx = duk__tval_fastint_to_arr_idx(tv_key);
    } else
#endif
    if (DUK_TVAL_IS_DOUBLE(tv_key)) {
        idx = duk__tval_number_to_arr_idx(tv_key);
    } else {
        return 0;
    }

    /* If index is not valid, idx will be DUK__NO_ARRAY_INDEX which
     * is 0xffffffffUL.  We don't need to check for that explicitly
     * because 0xffffffffUL will never be inside bufobj length.
     */

    /* Careful with wrapping (left shifting idx would be unsafe). */
    if (idx >= (h_bufobj->length >> h_bufobj->shift)) {
        return 0;
    }
    DUK_ASSERT(idx != DUK__NO_ARRAY_INDEX);

    byte_off = idx << h_bufobj->shift;  /* no wrap assuming h_bufobj->length is valid */
    elem_size = (duk_small_uint_t) (1U << h_bufobj->shift);

    if (h_bufobj->buf != NULL && DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_bufobj, byte_off + elem_size)) {
        data = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_bufobj->buf) + h_bufobj->offset + byte_off;
        duk_hbufobj_push_validated_read(thr, h_bufobj, data, elem_size);
    } else {
        DUK_D(DUK_DPRINT("bufobj access out of underlying buffer, ignoring (read zero)"));
        duk_push_uint(thr, 0);
    }

    return 1;
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_LOCAL duk_bool_t duk__putprop_fastpath_bufobj_tval(duk_hthread *thr, duk_hobject *obj, duk_tval *tv_key, duk_tval *tv_val) {
    duk_uint32_t idx;
    duk_hbufobj *h_bufobj;
    duk_uint_t byte_off;
    duk_small_uint_t elem_size;
    duk_uint8_t *data;

    if (!(DUK_HOBJECT_IS_BUFOBJ(obj) &&
          DUK_TVAL_IS_NUMBER(tv_val))) {
        return 0;
    }
    DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj));  /* caller ensures; rom objects are never bufobjs now */

    h_bufobj = (duk_hbufobj *) obj;
    if (!DUK_HBUFOBJ_HAS_VIRTUAL_INDICES(h_bufobj)) {
        return 0;
    }

#if defined(DUK_USE_FASTINT)
    if (DUK_TVAL_IS_FASTINT(tv_key)) {
        idx = duk__tval_fastint_to_arr_idx(tv_key);
    } else
#endif
    if (DUK_TVAL_IS_DOUBLE(tv_key)) {
        idx = duk__tval_number_to_arr_idx(tv_key);
    } else {
        return 0;
    }

    /* If index is not valid, idx will be DUK__NO_ARRAY_INDEX which
     * is 0xffffffffUL.  We don't need to check for that explicitly
     * because 0xffffffffUL will never be inside bufobj length.
     */

    /* Careful with wrapping (left shifting idx would be unsafe). */
    if (idx >= (h_bufobj->length >> h_bufobj->shift)) {
        return 0;
    }
    DUK_ASSERT(idx != DUK__NO_ARRAY_INDEX);

    byte_off = idx << h_bufobj->shift;  /* no wrap assuming h_bufobj->length is valid */
    elem_size = (duk_small_uint_t) (1U << h_bufobj->shift);

    /* Value is required to be a number in the fast path so there
     * are no side effects in write coercion.
     */
    duk_push_tval(thr, tv_val);
    DUK_ASSERT(duk_is_number(thr, -1));

    if (h_bufobj->buf != NULL && DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_bufobj, byte_off + elem_size)) {
        data = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_bufobj->buf) + h_bufobj->offset + byte_off;
        duk_hbufobj_validated_write(thr, h_bufobj, data, elem_size);
    } else {
        DUK_D(DUK_DPRINT("bufobj access out of underlying buffer, ignoring (write skipped)"));
    }

    duk_pop_unsafe(thr);
    return 1;
}
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

/*
 *  GETPROP: ECMAScript property read.
 */

DUK_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key) {
    duk_tval tv_obj_copy;
    duk_tval tv_key_copy;
    duk_hobject *curr = NULL;
    duk_hstring *key = NULL;
    duk_uint32_t arr_idx = DUK__NO_ARRAY_INDEX;
    duk_propdesc desc;
    duk_uint_t sanity;

    DUK_DDD(DUK_DDDPRINT("getprop: thr=%p, obj=%p, key=%p (obj -> %!T, key -> %!T)",
                         (void *) thr, (void *) tv_obj, (void *) tv_key,
                         (duk_tval *) tv_obj, (duk_tval *) tv_key));

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT(tv_obj != NULL);
    DUK_ASSERT(tv_key != NULL);

    DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);

    DUK_STATS_INC(thr->heap, stats_getprop_all);

    /*
     *  Make a copy of tv_obj, tv_key, and tv_val to avoid any issues of
     *  them being invalidated by a valstack resize.
     *
     *  XXX: this is now an overkill for many fast paths.  Rework this
     *  to be faster (although switching to a valstack discipline might
     *  be a better solution overall).
     */

    DUK_TVAL_SET_TVAL(&tv_obj_copy, tv_obj);
    DUK_TVAL_SET_TVAL(&tv_key_copy, tv_key);
    tv_obj = &tv_obj_copy;
    tv_key = &tv_key_copy;

    /*
     *  Coercion and fast path processing
     */

    switch (DUK_TVAL_GET_TAG(tv_obj)) {
    case DUK_TAG_UNDEFINED:
    case DUK_TAG_NULL: {
        /* Note: unconditional throw */
        DUK_DDD(DUK_DDDPRINT("base object is undefined or null -> reject"));
#if defined(DUK_USE_PARANOID_ERRORS)
        DUK_ERROR_TYPE(thr, DUK_STR_INVALID_BASE);
#else
        DUK_ERROR_FMT2(thr, DUK_ERR_TYPE_ERROR, "cannot read property %s of %s",
                       duk_push_string_tval_readable(thr, tv_key), duk_push_string_tval_readable(thr, tv_obj));
#endif
        DUK_WO_NORETURN(return 0;);
        break;
    }

    case DUK_TAG_BOOLEAN: {
        DUK_DDD(DUK_DDDPRINT("base object is a boolean, start lookup from boolean prototype"));
        curr = thr->builtins[DUK_BIDX_BOOLEAN_PROTOTYPE];
        break;
    }

    case DUK_TAG_STRING: {
        duk_hstring *h = DUK_TVAL_GET_STRING(tv_obj);
        duk_int_t pop_count;

        if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) {
            /* Symbols (ES2015 or hidden) don't have virtual properties. */
            DUK_DDD(DUK_DDDPRINT("base object is a symbol, start lookup from symbol prototype"));
            curr = thr->builtins[DUK_BIDX_SYMBOL_PROTOTYPE];
            break;
        }

#if defined(DUK_USE_FASTINT)
        if (DUK_TVAL_IS_FASTINT(tv_key)) {
            arr_idx = duk__tval_fastint_to_arr_idx(tv_key);
            DUK_DDD(DUK_DDDPRINT("base object string, key is a fast-path fastint; arr_idx %ld", (long) arr_idx));
            pop_count = 0;
        } else
#endif
        if (DUK_TVAL_IS_NUMBER(tv_key)) {
            arr_idx = duk__tval_number_to_arr_idx(tv_key);
            DUK_DDD(DUK_DDDPRINT("base object string, key is a fast-path number; arr_idx %ld", (long) arr_idx));
            pop_count = 0;
        } else {
            arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
            DUK_ASSERT(key != NULL);
            DUK_DDD(DUK_DDDPRINT("base object string, key is a non-fast-path number; after "
                                 "coercion key is %!T, arr_idx %ld",
                                 (duk_tval *) duk_get_tval(thr, -1), (long) arr_idx));
            pop_count = 1;
        }

        if (arr_idx != DUK__NO_ARRAY_INDEX &&
            arr_idx < DUK_HSTRING_GET_CHARLEN(h)) {
            duk_pop_n_unsafe(thr, pop_count);
            duk_push_hstring(thr, h);
            duk_substring(thr, -1, arr_idx, arr_idx + 1);  /* [str] -> [substr] */

            DUK_STATS_INC(thr->heap, stats_getprop_stringidx);
            DUK_DDD(DUK_DDDPRINT("-> %!T (base is string, key is an index inside string length "
                                 "after coercion -> return char)",
                                 (duk_tval *) duk_get_tval(thr, -1)));
            return 1;
        }

        if (pop_count == 0) {
            /* This is a pretty awkward control flow, but we need to recheck the
             * key coercion here.
             */
            arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
            DUK_ASSERT(key != NULL);
            DUK_DDD(DUK_DDDPRINT("base object string, key is a non-fast-path number; after "
                                 "coercion key is %!T, arr_idx %ld",
                                 (duk_tval *) duk_get_tval(thr, -1), (long) arr_idx));
        }

        if (key == DUK_HTHREAD_STRING_LENGTH(thr)) {
            duk_pop_unsafe(thr);  /* [key] -> [] */
            duk_push_uint(thr, (duk_uint_t) DUK_HSTRING_GET_CHARLEN(h));  /* [] -> [res] */

            DUK_STATS_INC(thr->heap, stats_getprop_stringlen);
            DUK_DDD(DUK_DDDPRINT("-> %!T (base is string, key is 'length' after coercion -> "
                                 "return string length)",
                                 (duk_tval *) duk_get_tval(thr, -1)));
            return 1;
        }

        DUK_DDD(DUK_DDDPRINT("base object is a string, start lookup from string prototype"));
        curr = thr->builtins[DUK_BIDX_STRING_PROTOTYPE];
        goto lookup;  /* avoid double coercion */
    }

    case DUK_TAG_OBJECT: {
#if defined(DUK_USE_ARRAY_PROP_FASTPATH)
        duk_tval *tmp;
#endif

        curr = DUK_TVAL_GET_OBJECT(tv_obj);
        DUK_ASSERT(curr != NULL);

        /* XXX: array .length fast path (important in e.g. loops)? */

#if defined(DUK_USE_ARRAY_PROP_FASTPATH)
        tmp = duk__getprop_shallow_fastpath_array_tval(thr, curr, tv_key);
        if (tmp) {
            duk_push_tval(thr, tmp);

            DUK_DDD(DUK_DDDPRINT("-> %!T (base is object, key is a number, array part "
                                 "fast path)",
                                 (duk_tval *) duk_get_tval(thr, -1)));
            DUK_STATS_INC(thr->heap, stats_getprop_arrayidx);
            return 1;
        }
#endif

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
        if (duk__getprop_fastpath_bufobj_tval(thr, curr, tv_key) != 0) {
            /* Read value pushed on stack. */
            DUK_DDD(DUK_DDDPRINT("-> %!T (base is bufobj, key is a number, bufobj "
                                 "fast path)",
                                 (duk_tval *) duk_get_tval(thr, -1)));
            DUK_STATS_INC(thr->heap, stats_getprop_bufobjidx);
            return 1;
        }
#endif

#if defined(DUK_USE_ES6_PROXY)
        if (DUK_UNLIKELY(DUK_HOBJECT_IS_PROXY(curr))) {
            duk_hobject *h_target;

            if (duk__proxy_check_prop(thr, curr, DUK_STRIDX_GET, tv_key, &h_target)) {
                /* -> [ ... trap handler ] */
                DUK_DDD(DUK_DDDPRINT("-> proxy object 'get' for key %!T", (duk_tval *) tv_key));
                DUK_STATS_INC(thr->heap, stats_getprop_proxy);
                duk_push_hobject(thr, h_target);  /* target */
                duk_push_tval(thr, tv_key);       /* P */
                duk_push_tval(thr, tv_obj);       /* Receiver: Proxy object */
                duk_call_method(thr, 3 /*nargs*/);

                /* Target object must be checked for a conflicting
                 * non-configurable property.
                 */
                arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
                DUK_ASSERT(key != NULL);

                if (duk__get_own_propdesc_raw(thr, h_target, key, arr_idx, &desc, DUK_GETDESC_FLAG_PUSH_VALUE)) {
                    duk_tval *tv_hook = duk_require_tval(thr, -3);  /* value from hook */
                    duk_tval *tv_targ = duk_require_tval(thr, -1);  /* value from target */
                    duk_bool_t datadesc_reject;
                    duk_bool_t accdesc_reject;

                    DUK_DDD(DUK_DDDPRINT("proxy 'get': target has matching property %!O, check for "
                                         "conflicting property; tv_hook=%!T, tv_targ=%!T, desc.flags=0x%08lx, "
                                         "desc.get=%p, desc.set=%p",
                                         (duk_heaphdr *) key, (duk_tval *) tv_hook, (duk_tval *) tv_targ,
                                         (unsigned long) desc.flags,
                                         (void *) desc.get, (void *) desc.set));

                    datadesc_reject = !(desc.flags & DUK_PROPDESC_FLAG_ACCESSOR) &&
                                      !(desc.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) &&
                                      !(desc.flags & DUK_PROPDESC_FLAG_WRITABLE) &&
                                      !duk_js_samevalue(tv_hook, tv_targ);
                    accdesc_reject = (desc.flags & DUK_PROPDESC_FLAG_ACCESSOR) &&
                                     !(desc.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) &&
                                     (desc.get == NULL) &&
                                     !DUK_TVAL_IS_UNDEFINED(tv_hook);
                    if (datadesc_reject || accdesc_reject) {
                        DUK_ERROR_TYPE(thr, DUK_STR_PROXY_REJECTED);
                        DUK_WO_NORETURN(return 0;);
                    }

                    duk_pop_2_unsafe(thr);
                } else {
                    duk_pop_unsafe(thr);
                }
                return 1;  /* return value */
            }

            curr = h_target;  /* resume lookup from target */
            DUK_TVAL_SET_OBJECT(tv_obj, curr);
        }
#endif  /* DUK_USE_ES6_PROXY */

        if (DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(curr)) {
            arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
            DUK_ASSERT(key != NULL);

            DUK_STATS_INC(thr->heap, stats_getprop_arguments);
            if (duk__check_arguments_map_for_get(thr, curr, key, &desc)) {
                DUK_DDD(DUK_DDDPRINT("-> %!T (base is object with arguments exotic behavior, "
                                     "key matches magically bound property -> skip standard "
                                     "Get with replacement value)",
                                     (duk_tval *) duk_get_tval(thr, -1)));

                /* no need for 'caller' post-check, because 'key' must be an array index */

                duk_remove_m2(thr);  /* [key result] -> [result] */
                return 1;
            }

            goto lookup;  /* avoid double coercion */
        }
        break;
    }

    /* Buffer has virtual properties similar to string, but indexed values
     * are numbers, not 1-byte buffers/strings which would perform badly.
     */
    case DUK_TAG_BUFFER: {
        duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv_obj);
        duk_int_t pop_count;

        /*
         *  Because buffer values are often looped over, a number fast path
         *  is important.
         */

#if defined(DUK_USE_FASTINT)
        if (DUK_TVAL_IS_FASTINT(tv_key)) {
            arr_idx = duk__tval_fastint_to_arr_idx(tv_key);
            DUK_DDD(DUK_DDDPRINT("base object buffer, key is a fast-path fastint; arr_idx %ld", (long) arr_idx));
            pop_count = 0;
        }
        else
#endif
        if (DUK_TVAL_IS_NUMBER(tv_key)) {
            arr_idx = duk__tval_number_to_arr_idx(tv_key);
            DUK_DDD(DUK_DDDPRINT("base object buffer, key is a fast-path number; arr_idx %ld", (long) arr_idx));
            pop_count = 0;
        } else {
            arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
            DUK_ASSERT(key != NULL);
            DUK_DDD(DUK_DDDPRINT("base object buffer, key is a non-fast-path number; after "
                                 "coercion key is %!T, arr_idx %ld",
                                 (duk_tval *) duk_get_tval(thr, -1), (long) arr_idx));
            pop_count = 1;
        }

        if (arr_idx != DUK__NO_ARRAY_INDEX &&
            arr_idx < DUK_HBUFFER_GET_SIZE(h)) {
            duk_pop_n_unsafe(thr, pop_count);
            duk_push_uint(thr, ((duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h))[arr_idx]);
            DUK_STATS_INC(thr->heap, stats_getprop_bufferidx);
            DUK_DDD(DUK_DDDPRINT("-> %!T (base is buffer, key is an index inside buffer length "
                                 "after coercion -> return byte as number)",
                                 (duk_tval *) duk_get_tval(thr, -1)));
            return 1;
        }

        if (pop_count == 0) {
            /* This is a pretty awkward control flow, but we need to recheck the
             * key coercion here.
             */
            arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
            DUK_ASSERT(key != NULL);
            DUK_DDD(DUK_DDDPRINT("base object buffer, key is a non-fast-path number; after "
                                 "coercion key is %!T, arr_idx %ld",
                                 (duk_tval *) duk_get_tval(thr, -1), (long) arr_idx));
        }

        if (key == DUK_HTHREAD_STRING_LENGTH(thr)) {
            duk_pop_unsafe(thr);  /* [key] -> [] */
            duk_push_uint(thr, (duk_uint_t) DUK_HBUFFER_GET_SIZE(h));  /* [] -> [res] */
            DUK_STATS_INC(thr->heap, stats_getprop_bufferlen);

            DUK_DDD(DUK_DDDPRINT("-> %!T (base is buffer, key is 'length' "
                                 "after coercion -> return buffer length)",
                                 (duk_tval *) duk_get_tval(thr, -1)));
            return 1;
        }

        DUK_DDD(DUK_DDDPRINT("base object is a buffer, start lookup from Uint8Array prototype"));
        curr = thr->builtins[DUK_BIDX_UINT8ARRAY_PROTOTYPE];
        goto lookup;  /* avoid double coercion */
    }

    case DUK_TAG_POINTER: {
        DUK_DDD(DUK_DDDPRINT("base object is a pointer, start lookup from pointer prototype"));
        curr = thr->builtins[DUK_BIDX_POINTER_PROTOTYPE];
        break;
    }

    case DUK_TAG_LIGHTFUNC: {
        /* Lightfuncs inherit getter .name and .length from %NativeFunctionPrototype%. */
        DUK_DDD(DUK_DDDPRINT("base object is a lightfunc, start lookup from function prototype"));
        curr = thr->builtins[DUK_BIDX_NATIVE_FUNCTION_PROTOTYPE];
        break;
    }

#if defined(DUK_USE_FASTINT)
    case DUK_TAG_FASTINT:
#endif
    default: {
        /* number */
        DUK_DDD(DUK_DDDPRINT("base object is a number, start lookup from number prototype"));
        DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv_obj));
        DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_obj));
        curr = thr->builtins[DUK_BIDX_NUMBER_PROTOTYPE];
        break;
    }
    }

    /* key coercion (unless already coerced above) */
    DUK_ASSERT(key == NULL);
    arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
    DUK_ASSERT(key != NULL);
    /*
     *  Property lookup
     */

 lookup:
    /* [key] (coerced) */
    DUK_ASSERT(curr != NULL);
    DUK_ASSERT(key != NULL);

    sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
    do {
        if (!duk__get_own_propdesc_raw(thr, curr, key, arr_idx, &desc, DUK_GETDESC_FLAG_PUSH_VALUE)) {
            goto next_in_chain;
        }

        if (desc.get != NULL) {
            /* accessor with defined getter */
            DUK_ASSERT((desc.flags & DUK_PROPDESC_FLAG_ACCESSOR) != 0);

            duk_pop_unsafe(thr);              /* [key undefined] -> [key] */
            duk_push_hobject(thr, desc.get);
            duk_push_tval(thr, tv_obj);       /* note: original, uncoerced base */
#if defined(DUK_USE_NONSTD_GETTER_KEY_ARGUMENT)
            duk_dup_m3(thr);
            duk_call_method(thr, 1);          /* [key getter this key] -> [key retval] */
#else
            duk_call_method(thr, 0);          /* [key getter this] -> [key retval] */
#endif
        } else {
            /* [key value] or [key undefined] */

            /* data property or accessor without getter */
            DUK_ASSERT(((desc.flags & DUK_PROPDESC_FLAG_ACCESSOR) == 0) ||
                       (desc.get == NULL));

            /* if accessor without getter, return value is undefined */
            DUK_ASSERT(((desc.flags & DUK_PROPDESC_FLAG_ACCESSOR) == 0) ||
                       duk_is_undefined(thr, -1));

            /* Note: for an accessor without getter, falling through to
             * check for "caller" exotic behavior is unnecessary as
             * "undefined" will never activate the behavior.  But it does
             * no harm, so we'll do it anyway.
             */
        }

        goto found;  /* [key result] */

     next_in_chain:
        /* XXX: option to pretend property doesn't exist if sanity limit is
         * hit might be useful.
         */
        if (DUK_UNLIKELY(sanity-- == 0)) {
            DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT);
            DUK_WO_NORETURN(return 0;);
        }
        curr = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, curr);
    } while (curr != NULL);

    /*
     *  Not found
     */

    duk_to_undefined(thr, -1);  /* [key] -> [undefined] (default value) */

    DUK_DDD(DUK_DDDPRINT("-> %!T (not found)", (duk_tval *) duk_get_tval(thr, -1)));
    return 0;

    /*
     *  Found; post-processing (Function and arguments objects)
     */

 found:
    /* [key result] */

#if !defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
    /* Special behavior for 'caller' property of (non-bound) function objects
     * and non-strict Arguments objects: if 'caller' -value- (!) is a strict
     * mode function, throw a TypeError (E5 Sections 15.3.5.4, 10.6).
     * Quite interestingly, a non-strict function with no formal arguments
     * will get an arguments object -without- special 'caller' behavior!
     *
     * The E5.1 spec is a bit ambiguous if this special behavior applies when
     * a bound function is the base value (not the 'caller' value): Section
     * 15.3.4.5 (describing bind()) states that [[Get]] for bound functions
     * matches that of Section 15.3.5.4 ([[Get]] for Function instances).
     * However, Section 13.3.5.4 has "NOTE: Function objects created using
     * Function.prototype.bind use the default [[Get]] internal method."
     * The current implementation assumes this means that bound functions
     * should not have the special [[Get]] behavior.
     *
     * The E5.1 spec is also a bit unclear if the TypeError throwing is
     * applied if the 'caller' value is a strict bound function.  The
     * current implementation will throw even for both strict non-bound
     * and strict bound functions.
     *
     * See test-dev-strict-func-as-caller-prop-value.js for quite extensive
     * tests.
     *
     * This exotic behavior is disabled when the non-standard 'caller' property
     * is enabled, as it conflicts with the free use of 'caller'.
     */
    if (key == DUK_HTHREAD_STRING_CALLER(thr) &&
        DUK_TVAL_IS_OBJECT(tv_obj)) {
        duk_hobject *orig = DUK_TVAL_GET_OBJECT(tv_obj);
        DUK_ASSERT(orig != NULL);

        if (DUK_HOBJECT_IS_NONBOUND_FUNCTION(orig) ||
            DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(orig)) {
            duk_hobject *h;

            /* XXX: The TypeError is currently not applied to bound
             * functions because the 'strict' flag is not copied by
             * bind().  This may or may not be correct, the specification
             * only refers to the value being a "strict mode Function
             * object" which is ambiguous.
             */
            DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(orig));

            h = duk_get_hobject(thr, -1);  /* NULL if not an object */
            if (h &&
                DUK_HOBJECT_IS_FUNCTION(h) &&
                DUK_HOBJECT_HAS_STRICT(h)) {
                /* XXX: sufficient to check 'strict', assert for 'is function' */
                DUK_ERROR_TYPE(thr, DUK_STR_STRICT_CALLER_READ);
                DUK_WO_NORETURN(return 0;);
            }
        }
    }
#endif   /* !DUK_USE_NONSTD_FUNC_CALLER_PROPERTY */

    duk_remove_m2(thr);  /* [key result] -> [result] */

    DUK_DDD(DUK_DDDPRINT("-> %!T (found)", (duk_tval *) duk_get_tval(thr, -1)));
    return 1;
}

/*
 *  HASPROP: ECMAScript property existence check ("in" operator).
 *
 *  Interestingly, the 'in' operator does not do any coercion of
 *  the target object.
 */

DUK_INTERNAL duk_bool_t duk_hobject_hasprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key) {
    duk_tval tv_key_copy;
    duk_hobject *obj;
    duk_hstring *key;
    duk_uint32_t arr_idx;
    duk_bool_t rc;
    duk_propdesc desc;

    DUK_DDD(DUK_DDDPRINT("hasprop: thr=%p, obj=%p, key=%p (obj -> %!T, key -> %!T)",
                         (void *) thr, (void *) tv_obj, (void *) tv_key,
                         (duk_tval *) tv_obj, (duk_tval *) tv_key));

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT(tv_obj != NULL);
    DUK_ASSERT(tv_key != NULL);
    DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);

    DUK_TVAL_SET_TVAL(&tv_key_copy, tv_key);
    tv_key = &tv_key_copy;

    /*
     *  The 'in' operator requires an object as its right hand side,
     *  throwing a TypeError unconditionally if this is not the case.
     *
     *  However, lightfuncs need to behave like fully fledged objects
     *  here to be maximally transparent, so we need to handle them
     *  here.  Same goes for plain buffers which behave like ArrayBuffers.
     */

    /* XXX: Refactor key coercion so that it's only called once.  It can't
     * be trivially lifted here because the object must be type checked
     * first.
     */

    if (DUK_TVAL_IS_OBJECT(tv_obj)) {
        obj = DUK_TVAL_GET_OBJECT(tv_obj);
        DUK_ASSERT(obj != NULL);

        arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
    } else if (DUK_TVAL_IS_BUFFER(tv_obj)) {
        arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
        if (duk__key_is_plain_buf_ownprop(thr, DUK_TVAL_GET_BUFFER(tv_obj), key, arr_idx)) {
            rc = 1;
            goto pop_and_return;
        }
        obj = thr->builtins[DUK_BIDX_UINT8ARRAY_PROTOTYPE];
    } else if (DUK_TVAL_IS_LIGHTFUNC(tv_obj)) {
        arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);

        /* If not found, resume existence check from %NativeFunctionPrototype%.
         * We can just substitute the value in this case; nothing will
         * need the original base value (as would be the case with e.g.
         * setters/getters.
         */
        obj = thr->builtins[DUK_BIDX_NATIVE_FUNCTION_PROTOTYPE];
    } else {
        /* Note: unconditional throw */
        DUK_DDD(DUK_DDDPRINT("base object is not an object -> reject"));
        DUK_ERROR_TYPE(thr, DUK_STR_INVALID_BASE);
        DUK_WO_NORETURN(return 0;);
    }

    /* XXX: fast path for arrays? */

    DUK_ASSERT(key != NULL);
    DUK_ASSERT(obj != NULL);
    DUK_UNREF(arr_idx);

#if defined(DUK_USE_ES6_PROXY)
    if (DUK_UNLIKELY(DUK_HOBJECT_IS_PROXY(obj))) {
        duk_hobject *h_target;
        duk_bool_t tmp_bool;

        /* XXX: the key in 'key in obj' is string coerced before we're called
         * (which is the required behavior in E5/E5.1/E6) so the key is a string
         * here already.
         */

        if (duk__proxy_check_prop(thr, obj, DUK_STRIDX_HAS, tv_key, &h_target)) {
            /* [ ... key trap handler ] */
            DUK_DDD(DUK_DDDPRINT("-> proxy object 'has' for key %!T", (duk_tval *) tv_key));
            duk_push_hobject(thr, h_target);  /* target */
            duk_push_tval(thr, tv_key);       /* P */
            duk_call_method(thr, 2 /*nargs*/);
            tmp_bool = duk_to_boolean_top_pop(thr);
            if (!tmp_bool) {
                /* Target object must be checked for a conflicting
                 * non-configurable property.
                 */

                if (duk__get_own_propdesc_raw(thr, h_target, key, arr_idx, &desc, 0 /*flags*/)) {  /* don't push value */
                    DUK_DDD(DUK_DDDPRINT("proxy 'has': target has matching property %!O, check for "
                                         "conflicting property; desc.flags=0x%08lx, "
                                         "desc.get=%p, desc.set=%p",
                                         (duk_heaphdr *) key, (unsigned long) desc.flags,
                                         (void *) desc.get, (void *) desc.set));
                    /* XXX: Extensibility check for target uses IsExtensible().  If we
                     * implemented the isExtensible trap and didn't reject proxies as
                     * proxy targets, it should be respected here.
                     */
                    if (!((desc.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) &&  /* property is configurable and */
                          DUK_HOBJECT_HAS_EXTENSIBLE(h_target))) {          /* ... target is extensible */
                        DUK_ERROR_TYPE(thr, DUK_STR_PROXY_REJECTED);
                        DUK_WO_NORETURN(return 0;);
                    }
                }
            }

            duk_pop_unsafe(thr);  /* [ key ] -> [] */
            return tmp_bool;
        }

        obj = h_target;  /* resume check from proxy target */
    }
#endif  /* DUK_USE_ES6_PROXY */

    /* XXX: inline into a prototype walking loop? */

    rc = duk__get_propdesc(thr, obj, key, &desc, 0 /*flags*/);  /* don't push value */
    /* fall through */

 pop_and_return:
    duk_pop_unsafe(thr);  /* [ key ] -> [] */
    return rc;
}

/*
 *  HASPROP variant used internally.
 *
 *  This primitive must never throw an error, callers rely on this.
 *  In particular, don't throw an error for prototype loops; instead,
 *  pretend like the property doesn't exist if a prototype sanity limit
 *  is reached.
 *
 *  Does not implement proxy behavior: if applied to a proxy object,
 *  returns key existence on the proxy object itself.
 */

DUK_INTERNAL duk_bool_t duk_hobject_hasprop_raw(duk_hthread *thr, duk_hobject *obj, duk_hstring *key) {
    duk_propdesc dummy;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT(obj != NULL);
    DUK_ASSERT(key != NULL);

    DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);

    return duk__get_propdesc(thr, obj, key, &dummy, DUK_GETDESC_FLAG_IGNORE_PROTOLOOP);  /* don't push value */
}

/*
 *  Helper: handle Array object 'length' write which automatically
 *  deletes properties, see E5 Section 15.4.5.1, step 3.  This is
 *  quite tricky to get right.
 *
 *  Used by duk_hobject_putprop().
 */

/* Coerce a new .length candidate to a number and check that it's a valid
 * .length.
 */
DUK_LOCAL duk_uint32_t duk__to_new_array_length_checked(duk_hthread *thr, duk_tval *tv) {
    duk_uint32_t res;
    duk_double_t d;

#if !defined(DUK_USE_PREFER_SIZE)
#if defined(DUK_USE_FASTINT)
    /* When fastints are enabled, the most interesting case is assigning
     * a fastint to .length (e.g. arr.length = 0).
     */
    if (DUK_TVAL_IS_FASTINT(tv)) {
        /* Very common case. */
        duk_int64_t fi;
        fi = DUK_TVAL_GET_FASTINT(tv);
        if (fi < 0 || fi > DUK_I64_CONSTANT(0xffffffff)) {
            goto fail_range;
        }
        return (duk_uint32_t) fi;
    }
#else  /* DUK_USE_FASTINT */
    /* When fastints are not enabled, the most interesting case is any
     * number.
     */
    if (DUK_TVAL_IS_DOUBLE(tv)) {
        d = DUK_TVAL_GET_NUMBER(tv);
    }
#endif  /* DUK_USE_FASTINT */
    else
#endif  /* !DUK_USE_PREFER_SIZE */
    {
        /* In all other cases, and when doing a size optimized build,
         * fall back to the comprehensive handler.
         */
        d = duk_js_tonumber(thr, tv);
    }

    /* Refuse to update an Array's 'length' to a value outside the
     * 32-bit range.  Negative zero is accepted as zero.
     */
    res = duk_double_to_uint32_t(d);
    if (!duk_double_equals((duk_double_t) res, d)) {
        goto fail_range;
    }

    return res;

 fail_range:
    DUK_ERROR_RANGE(thr, DUK_STR_INVALID_ARRAY_LENGTH);
    DUK_WO_NORETURN(return 0;);
}

/* Delete elements required by a smaller length, taking into account
 * potentially non-configurable elements.  Returns non-zero if all
 * elements could be deleted, and zero if all or some elements could
 * not be deleted.  Also writes final "target length" to 'out_result_len'.
 * This is the length value that should go into the 'length' property
 * (must be set by the caller).  Never throws an error.
 */
DUK_LOCAL
duk_bool_t duk__handle_put_array_length_smaller(duk_hthread *thr,
                                                duk_hobject *obj,
                                                duk_uint32_t old_len,
                                                duk_uint32_t new_len,
                                                duk_bool_t force_flag,
                                                duk_uint32_t *out_result_len) {
    duk_uint32_t target_len;
    duk_uint_fast32_t i;
    duk_uint32_t arr_idx;
    duk_hstring *key;
    duk_tval *tv;
    duk_bool_t rc;

    DUK_DDD(DUK_DDDPRINT("new array length smaller than old (%ld -> %ld), "
                         "probably need to remove elements",
                         (long) old_len, (long) new_len));

    /*
     *  New length is smaller than old length, need to delete properties above
     *  the new length.
     *
     *  If array part exists, this is straightforward: array entries cannot
     *  be non-configurable so this is guaranteed to work.
     *
     *  If array part does not exist, array-indexed values are scattered
     *  in the entry part, and some may not be configurable (preventing length
     *  from becoming lower than their index + 1).  To handle the algorithm
     *  in E5 Section 15.4.5.1, step l correctly, we scan the entire property
     *  set twice.
     */

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(obj != NULL);
    DUK_ASSERT(new_len < old_len);
    DUK_ASSERT(out_result_len != NULL);
    DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);

    DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj));
    DUK_ASSERT(DUK_HOBJECT_IS_ARRAY(obj));

    if (DUK_HOBJECT_HAS_ARRAY_PART(obj)) {
        /*
         *  All defined array-indexed properties are in the array part
         *  (we assume the array part is comprehensive), and all array
         *  entries are writable, configurable, and enumerable.  Thus,
         *  nothing can prevent array entries from being deleted.
         */

        DUK_DDD(DUK_DDDPRINT("have array part, easy case"));

        if (old_len < DUK_HOBJECT_GET_ASIZE(obj)) {
            /* XXX: assertion that entries >= old_len are already unused */
            i = old_len;
        } else {
            i = DUK_HOBJECT_GET_ASIZE(obj);
        }
        DUK_ASSERT(i <= DUK_HOBJECT_GET_ASIZE(obj));

        while (i > new_len) {
            i--;
            tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, i);
            DUK_TVAL_SET_UNUSED_UPDREF(thr, tv);  /* side effects */
        }

        *out_result_len = new_len;
        return 1;
    } else {
        /*
         *  Entries part is a bit more complex.
         */

        /* Stage 1: find highest preventing non-configurable entry (if any).
         * When forcing, ignore non-configurability.
         */

        DUK_DDD(DUK_DDDPRINT("no array part, slow case"));

        DUK_DDD(DUK_DDDPRINT("array length write, no array part, stage 1: find target_len "
                             "(highest preventing non-configurable entry (if any))"));

        target_len = new_len;
        if (force_flag) {
            DUK_DDD(DUK_DDDPRINT("array length write, no array part; force flag -> skip stage 1"));
            goto skip_stage1;
        }
        for (i = 0; i < DUK_HOBJECT_GET_ENEXT(obj); i++) {
            key = DUK_HOBJECT_E_GET_KEY(thr->heap, obj, i);
            if (!key) {
                DUK_DDD(DUK_DDDPRINT("skip entry index %ld: null key", (long) i));
                continue;
            }
            if (!DUK_HSTRING_HAS_ARRIDX(key)) {
                DUK_DDD(DUK_DDDPRINT("skip entry index %ld: key not an array index", (long) i));
                continue;
            }

            DUK_ASSERT(DUK_HSTRING_HAS_ARRIDX(key));  /* XXX: macro checks for array index flag, which is unnecessary here */
            arr_idx = DUK_HSTRING_GET_ARRIDX_SLOW(key);
            DUK_ASSERT(arr_idx != DUK__NO_ARRAY_INDEX);
            DUK_ASSERT(arr_idx < old_len);  /* consistency requires this */

            if (arr_idx < new_len) {
                DUK_DDD(DUK_DDDPRINT("skip entry index %ld: key is array index %ld, below new_len",
                                     (long) i, (long) arr_idx));
                continue;
            }
            if (DUK_HOBJECT_E_SLOT_IS_CONFIGURABLE(thr->heap, obj, i)) {
                DUK_DDD(DUK_DDDPRINT("skip entry index %ld: key is a relevant array index %ld, but configurable",
                                     (long) i, (long) arr_idx));
                continue;
            }

            /* relevant array index is non-configurable, blocks write */
            if (arr_idx >= target_len) {
                DUK_DDD(DUK_DDDPRINT("entry at index %ld has arr_idx %ld, is not configurable, "
                                     "update target_len %ld -> %ld",
                                     (long) i, (long) arr_idx, (long) target_len,
                                     (long) (arr_idx + 1)));
                target_len = arr_idx + 1;
            }
        }
     skip_stage1:

        /* stage 2: delete configurable entries above target length */

        DUK_DDD(DUK_DDDPRINT("old_len=%ld, new_len=%ld, target_len=%ld",
                             (long) old_len, (long) new_len, (long) target_len));

        DUK_DDD(DUK_DDDPRINT("array length write, no array part, stage 2: remove "
                             "entries >= target_len"));

        for (i = 0; i < DUK_HOBJECT_GET_ENEXT(obj); i++) {
            key = DUK_HOBJECT_E_GET_KEY(thr->heap, obj, i);
            if (!key) {
                DUK_DDD(DUK_DDDPRINT("skip entry index %ld: null key", (long) i));
                continue;
            }
            if (!DUK_HSTRING_HAS_ARRIDX(key)) {
                DUK_DDD(DUK_DDDPRINT("skip entry index %ld: key not an array index", (long) i));
                continue;
            }

            DUK_ASSERT(DUK_HSTRING_HAS_ARRIDX(key));  /* XXX: macro checks for array index flag, which is unnecessary here */
            arr_idx = DUK_HSTRING_GET_ARRIDX_SLOW(key);
            DUK_ASSERT(arr_idx != DUK__NO_ARRAY_INDEX);
            DUK_ASSERT(arr_idx < old_len);  /* consistency requires this */

            if (arr_idx < target_len) {
                DUK_DDD(DUK_DDDPRINT("skip entry index %ld: key is array index %ld, below target_len",
                                     (long) i, (long) arr_idx));
                continue;
            }
            DUK_ASSERT(force_flag || DUK_HOBJECT_E_SLOT_IS_CONFIGURABLE(thr->heap, obj, i));  /* stage 1 guarantees */

            DUK_DDD(DUK_DDDPRINT("delete entry index %ld: key is array index %ld",
                                 (long) i, (long) arr_idx));

            /*
             *  Slow delete, but we don't care as we're already in a very slow path.
             *  The delete always succeeds: key has no exotic behavior, property
             *  is configurable, and no resize occurs.
             */
            rc = duk_hobject_delprop_raw(thr, obj, key, force_flag ? DUK_DELPROP_FLAG_FORCE : 0);
            DUK_UNREF(rc);
            DUK_ASSERT(rc != 0);
        }

        /* stage 3: update length (done by caller), decide return code */

        DUK_DDD(DUK_DDDPRINT("array length write, no array part, stage 3: update length (done by caller)"));

        *out_result_len = target_len;

        if (target_len == new_len) {
            DUK_DDD(DUK_DDDPRINT("target_len matches new_len, return success"));
            return 1;
        }
        DUK_DDD(DUK_DDDPRINT("target_len does not match new_len (some entry prevented "
                             "full length adjustment), return error"));
        return 0;
    }

    DUK_UNREACHABLE();
}

/* XXX: is valstack top best place for argument? */
DUK_LOCAL duk_bool_t duk__handle_put_array_length(duk_hthread *thr, duk_hobject *obj) {
    duk_harray *a;
    duk_uint32_t old_len;
    duk_uint32_t new_len;
    duk_uint32_t result_len;
    duk_bool_t rc;

    DUK_DDD(DUK_DDDPRINT("handling a put operation to array 'length' exotic property, "
                         "new val: %!T",
                         (duk_tval *) duk_get_tval(thr, -1)));

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

    DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);

    DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj));
    DUK_ASSERT(DUK_HOBJECT_IS_ARRAY(obj));
    a = (duk_harray *) obj;
    DUK_HARRAY_ASSERT_VALID(a);

    DUK_ASSERT(duk_is_valid_index(thr, -1));

    /*
     *  Get old and new length
     */

    old_len = a->length;
    new_len = duk__to_new_array_length_checked(thr, DUK_GET_TVAL_NEGIDX(thr, -1));
    DUK_DDD(DUK_DDDPRINT("old_len=%ld, new_len=%ld", (long) old_len, (long) new_len));

    /*
     *  Writability check
     */

    if (DUK_HARRAY_LENGTH_NONWRITABLE(a)) {
        DUK_DDD(DUK_DDDPRINT("length is not writable, fail"));
        return 0;
    }

    /*
     *  New length not lower than old length => no changes needed
     *  (not even array allocation).
     */

    if (new_len >= old_len) {
        DUK_DDD(DUK_DDDPRINT("new length is same or higher than old length, just update length, no deletions"));
        a->length = new_len;
        return 1;
    }

    DUK_DDD(DUK_DDDPRINT("new length is lower than old length, probably must delete entries"));

    /*
     *  New length lower than old length => delete elements, then
     *  update length.
     *
     *  Note: even though a bunch of elements have been deleted, the 'desc' is
     *  still valid as properties haven't been resized (and entries compacted).
     */

    rc = duk__handle_put_array_length_smaller(thr, obj, old_len, new_len, 0 /*force_flag*/, &result_len);
    DUK_ASSERT(result_len >= new_len && result_len <= old_len);

    a->length = result_len;

    /* XXX: shrink array allocation or entries compaction here? */

    return rc;
}

/*
 *  PUTPROP: ECMAScript property write.
 *
 *  Unlike ECMAScript primitive which returns nothing, returns 1 to indicate
 *  success and 0 to indicate failure (assuming throw is not set).
 *
 *  This is an extremely tricky function.  Some examples:
 *
 *    * Currently a decref may trigger a GC, which may compact an object's
 *      property allocation.  Consequently, any entry indices (e_idx) will
 *      be potentially invalidated by a decref.
 *
 *    * Exotic behaviors (strings, arrays, arguments object) require,
 *      among other things:
 *
 *      - Preprocessing before and postprocessing after an actual property
 *        write.  For example, array index write requires pre-checking the
 *        array 'length' property for access control, and may require an
 *        array 'length' update after the actual write has succeeded (but
 *        not if it fails).
 *
 *      - Deletion of multiple entries, as a result of array 'length' write.
 *
 *    * Input values are taken as pointers which may point to the valstack.
 *      If valstack is resized because of the put (this may happen at least
 *      when the array part is abandoned), the pointers can be invalidated.
 *      (We currently make a copy of all of the input values to avoid issues.)
 */

DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, duk_tval *tv_val, duk_bool_t throw_flag) {
    duk_tval tv_obj_copy;
    duk_tval tv_key_copy;
    duk_tval tv_val_copy;
    duk_hobject *orig = NULL;  /* NULL if tv_obj is primitive */
    duk_hobject *curr;
    duk_hstring *key = NULL;
    duk_propdesc desc;
    duk_tval *tv;
    duk_uint32_t arr_idx;
    duk_bool_t rc;
    duk_int_t e_idx;
    duk_uint_t sanity;
    duk_uint32_t new_array_length = 0;  /* 0 = no update */

    DUK_DDD(DUK_DDDPRINT("putprop: thr=%p, obj=%p, key=%p, val=%p, throw=%ld "
                         "(obj -> %!T, key -> %!T, val -> %!T)",
                         (void *) thr, (void *) tv_obj, (void *) tv_key, (void *) tv_val,
                         (long) throw_flag, (duk_tval *) tv_obj, (duk_tval *) tv_key, (duk_tval *) tv_val));

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT(tv_obj != NULL);
    DUK_ASSERT(tv_key != NULL);
    DUK_ASSERT(tv_val != NULL);

    DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);

    DUK_STATS_INC(thr->heap, stats_putprop_all);

    /*
     *  Make a copy of tv_obj, tv_key, and tv_val to avoid any issues of
     *  them being invalidated by a valstack resize.
     *
     *  XXX: this is an overkill for some paths, so optimize this later
     *  (or maybe switch to a stack arguments model entirely).
     */

    DUK_TVAL_SET_TVAL(&tv_obj_copy, tv_obj);
    DUK_TVAL_SET_TVAL(&tv_key_copy, tv_key);
    DUK_TVAL_SET_TVAL(&tv_val_copy, tv_val);
    tv_obj = &tv_obj_copy;
    tv_key = &tv_key_copy;
    tv_val = &tv_val_copy;

    /*
     *  Coercion and fast path processing.
     */

    switch (DUK_TVAL_GET_TAG(tv_obj)) {
    case DUK_TAG_UNDEFINED:
    case DUK_TAG_NULL: {
        /* Note: unconditional throw */
        DUK_DDD(DUK_DDDPRINT("base object is undefined or null -> reject (object=%!iT)",
                             (duk_tval *) tv_obj));
#if defined(DUK_USE_PARANOID_ERRORS)
        DUK_ERROR_TYPE(thr, DUK_STR_INVALID_BASE);
#else
        DUK_ERROR_FMT2(thr, DUK_ERR_TYPE_ERROR, "cannot write property %s of %s",
                       duk_push_string_tval_readable(thr, tv_key), duk_push_string_tval_readable(thr, tv_obj));
#endif
        DUK_WO_NORETURN(return 0;);
        break;
    }

    case DUK_TAG_BOOLEAN: {
        DUK_DDD(DUK_DDDPRINT("base object is a boolean, start lookup from boolean prototype"));
        curr = thr->builtins[DUK_BIDX_BOOLEAN_PROTOTYPE];
        break;
    }

    case DUK_TAG_STRING: {
        duk_hstring *h = DUK_TVAL_GET_STRING(tv_obj);

        /*
         *  Note: currently no fast path for array index writes.
         *  They won't be possible anyway as strings are immutable.
         */

        DUK_ASSERT(key == NULL);
        arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
        DUK_ASSERT(key != NULL);

        if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) {
            /* Symbols (ES2015 or hidden) don't have virtual properties. */
            curr = thr->builtins[DUK_BIDX_SYMBOL_PROTOTYPE];
            goto lookup;
        }

        if (key == DUK_HTHREAD_STRING_LENGTH(thr)) {
            goto fail_not_writable;
        }

        if (arr_idx != DUK__NO_ARRAY_INDEX &&
            arr_idx < DUK_HSTRING_GET_CHARLEN(h)) {
            goto fail_not_writable;
        }

        DUK_DDD(DUK_DDDPRINT("base object is a string, start lookup from string prototype"));
        curr = thr->builtins[DUK_BIDX_STRING_PROTOTYPE];
        goto lookup;  /* avoid double coercion */
    }

    case DUK_TAG_OBJECT: {
        orig = DUK_TVAL_GET_OBJECT(tv_obj);
        DUK_ASSERT(orig != NULL);

#if defined(DUK_USE_ROM_OBJECTS)
        /* With this check in place fast paths won't need read-only
         * object checks.  This is technically incorrect if there are
         * setters that cause no writes to ROM objects, but current
         * built-ins don't have such setters.
         */
        if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) orig)) {
            DUK_DD(DUK_DDPRINT("attempt to putprop on read-only target object"));
            goto fail_not_writable_no_pop;  /* Must avoid duk_pop() in exit path */
        }
#endif

        /* The fast path for array property put is not fully compliant:
         * If one places conflicting number-indexed properties into
         * Array.prototype (for example, a non-writable Array.prototype[7])
         * the fast path will incorrectly ignore them.
         *
         * This fast path could be made compliant by falling through
         * to the slow path if the previous value was UNUSED.  This would
         * also remove the need to check for extensibility.  Right now a
         * non-extensible array is slower than an extensible one as far
         * as writes are concerned.
         *
         * The fast path behavior is documented in more detail here:
         * tests/ecmascript/test-misc-array-fast-write.js
         */

        /* XXX: array .length? */

#if defined(DUK_USE_ARRAY_PROP_FASTPATH)
        if (duk__putprop_shallow_fastpath_array_tval(thr, orig, tv_key, tv_val) != 0) {
            DUK_DDD(DUK_DDDPRINT("array fast path success"));
            DUK_STATS_INC(thr->heap, stats_putprop_arrayidx);
            return 1;
        }
#endif

#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
        if (duk__putprop_fastpath_bufobj_tval(thr, orig, tv_key, tv_val) != 0) {
            DUK_DDD(DUK_DDDPRINT("base is bufobj, key is a number, bufobj fast path"));
            DUK_STATS_INC(thr->heap, stats_putprop_bufobjidx);
            return 1;
        }
#endif

#if defined(DUK_USE_ES6_PROXY)
        if (DUK_UNLIKELY(DUK_HOBJECT_IS_PROXY(orig))) {
            duk_hobject *h_target;
            duk_bool_t tmp_bool;

            if (duk__proxy_check_prop(thr, orig, DUK_STRIDX_SET, tv_key, &h_target)) {
                /* -> [ ... trap handler ] */
                DUK_DDD(DUK_DDDPRINT("-> proxy object 'set' for key %!T", (duk_tval *) tv_key));
                DUK_STATS_INC(thr->heap, stats_putprop_proxy);
                duk_push_hobject(thr, h_target);  /* target */
                duk_push_tval(thr, tv_key);       /* P */
                duk_push_tval(thr, tv_val);       /* V */
                duk_push_tval(thr, tv_obj);       /* Receiver: Proxy object */
                duk_call_method(thr, 4 /*nargs*/);
                tmp_bool = duk_to_boolean_top_pop(thr);
                if (!tmp_bool) {
                    goto fail_proxy_rejected;
                }

                /* Target object must be checked for a conflicting
                 * non-configurable property.
                 */
                arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
                DUK_ASSERT(key != NULL);

                if (duk__get_own_propdesc_raw(thr, h_target, key, arr_idx, &desc, DUK_GETDESC_FLAG_PUSH_VALUE)) {
                    duk_tval *tv_targ = duk_require_tval(thr, -1);
                    duk_bool_t datadesc_reject;
                    duk_bool_t accdesc_reject;

                    DUK_DDD(DUK_DDDPRINT("proxy 'set': target has matching property %!O, check for "
                                         "conflicting property; tv_val=%!T, tv_targ=%!T, desc.flags=0x%08lx, "
                                         "desc.get=%p, desc.set=%p",
                                         (duk_heaphdr *) key, (duk_tval *) tv_val, (duk_tval *) tv_targ,
                                         (unsigned long) desc.flags,
                                         (void *) desc.get, (void *) desc.set));

                    datadesc_reject = !(desc.flags & DUK_PROPDESC_FLAG_ACCESSOR) &&
                                      !(desc.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) &&
                                      !(desc.flags & DUK_PROPDESC_FLAG_WRITABLE) &&
                                      !duk_js_samevalue(tv_val, tv_targ);
                    accdesc_reject = (desc.flags & DUK_PROPDESC_FLAG_ACCESSOR) &&
                                     !(desc.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) &&
                                     (desc.set == NULL);
                    if (datadesc_reject || accdesc_reject) {
                        DUK_ERROR_TYPE(thr, DUK_STR_PROXY_REJECTED);
                        DUK_WO_NORETURN(return 0;);
                    }

                    duk_pop_2_unsafe(thr);
                } else {
                    duk_pop_unsafe(thr);
                }
                return 1;  /* success */
            }

            orig = h_target;  /* resume write to target */
            DUK_TVAL_SET_OBJECT(tv_obj, orig);
        }
#endif  /* DUK_USE_ES6_PROXY */

        curr = orig;
        break;
    }

    case DUK_TAG_BUFFER: {
        duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv_obj);
        duk_int_t pop_count = 0;

        /*
         *  Because buffer values may be looped over and read/written
         *  from, an array index fast path is important.
         */

#if defined(DUK_USE_FASTINT)
        if (DUK_TVAL_IS_FASTINT(tv_key)) {
            arr_idx = duk__tval_fastint_to_arr_idx(tv_key);
            DUK_DDD(DUK_DDDPRINT("base object buffer, key is a fast-path fastint; arr_idx %ld", (long) arr_idx));
            pop_count = 0;
        } else
#endif
        if (DUK_TVAL_IS_NUMBER(tv_key)) {
            arr_idx = duk__tval_number_to_arr_idx(tv_key);
            DUK_DDD(DUK_DDDPRINT("base object buffer, key is a fast-path number; arr_idx %ld", (long) arr_idx));
            pop_count = 0;
        } else {
            arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
            DUK_ASSERT(key != NULL);
            DUK_DDD(DUK_DDDPRINT("base object buffer, key is a non-fast-path number; after "
                                 "coercion key is %!T, arr_idx %ld",
                                 (duk_tval *) duk_get_tval(thr, -1), (long) arr_idx));
            pop_count = 1;
        }

        if (arr_idx != DUK__NO_ARRAY_INDEX &&
            arr_idx < DUK_HBUFFER_GET_SIZE(h)) {
            duk_uint8_t *data;
            DUK_DDD(DUK_DDDPRINT("writing to buffer data at index %ld", (long) arr_idx));
            data = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h);

            /* XXX: duk_to_int() ensures we'll get 8 lowest bits as
             * as input is within duk_int_t range (capped outside it).
             */
#if defined(DUK_USE_FASTINT)
            /* Buffer writes are often integers. */
            if (DUK_TVAL_IS_FASTINT(tv_val)) {
                data[arr_idx] = (duk_uint8_t) DUK_TVAL_GET_FASTINT_U32(tv_val);
            }
            else
#endif
            {
                duk_push_tval(thr, tv_val);
                data[arr_idx] = (duk_uint8_t) duk_to_uint32(thr, -1);
                pop_count++;
            }

            duk_pop_n_unsafe(thr, pop_count);
            DUK_DDD(DUK_DDDPRINT("result: success (buffer data write)"));
            DUK_STATS_INC(thr->heap, stats_putprop_bufferidx);
            return 1;
        }

        if (pop_count == 0) {
            /* This is a pretty awkward control flow, but we need to recheck the
             * key coercion here.
             */
            arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
            DUK_ASSERT(key != NULL);
            DUK_DDD(DUK_DDDPRINT("base object buffer, key is a non-fast-path number; after "
                                 "coercion key is %!T, arr_idx %ld",
                                 (duk_tval *) duk_get_tval(thr, -1), (long) arr_idx));
        }

        if (key == DUK_HTHREAD_STRING_LENGTH(thr)) {
            goto fail_not_writable;
        }

        DUK_DDD(DUK_DDDPRINT("base object is a buffer, start lookup from Uint8Array prototype"));
        curr = thr->builtins[DUK_BIDX_UINT8ARRAY_PROTOTYPE];
        goto lookup;  /* avoid double coercion */
    }

    case DUK_TAG_POINTER: {
        DUK_DDD(DUK_DDDPRINT("base object is a pointer, start lookup from pointer prototype"));
        curr = thr->builtins[DUK_BIDX_POINTER_PROTOTYPE];
        break;
    }

    case DUK_TAG_LIGHTFUNC: {
        /* Lightfuncs have no own properties and are considered non-extensible.
         * However, the write may be captured by an inherited setter which
         * means we can't stop the lookup here.
         */
        DUK_DDD(DUK_DDDPRINT("base object is a lightfunc, start lookup from function prototype"));
        curr = thr->builtins[DUK_BIDX_NATIVE_FUNCTION_PROTOTYPE];
        break;
    }

#if defined(DUK_USE_FASTINT)
    case DUK_TAG_FASTINT:
#endif
    default: {
        /* number */
        DUK_DDD(DUK_DDDPRINT("base object is a number, start lookup from number prototype"));
        DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_obj));
        curr = thr->builtins[DUK_BIDX_NUMBER_PROTOTYPE];
        break;
    }
    }

    DUK_ASSERT(key == NULL);
    arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
    DUK_ASSERT(key != NULL);

 lookup:

    /*
     *  Check whether the property already exists in the prototype chain.
     *  Note that the actual write goes into the original base object
     *  (except if an accessor property captures the write).
     */

    /* [key] */

    DUK_ASSERT(curr != NULL);
    sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
    do {
        if (!duk__get_own_propdesc_raw(thr, curr, key, arr_idx, &desc, 0 /*flags*/)) {  /* don't push value */
            goto next_in_chain;
        }

        if (desc.flags & DUK_PROPDESC_FLAG_ACCESSOR) {
            /*
             *  Found existing accessor property (own or inherited).
             *  Call setter with 'this' set to orig, and value as the only argument.
             *  Setter calls are OK even for ROM objects.
             *
             *  Note: no exotic arguments object behavior, because [[Put]] never
             *  calls [[DefineOwnProperty]] (E5 Section 8.12.5, step 5.b).
             */

            duk_hobject *setter;

            DUK_DD(DUK_DDPRINT("put to an own or inherited accessor, calling setter"));

            setter = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, curr, desc.e_idx);
            if (!setter) {
                goto fail_no_setter;
            }
            duk_push_hobject(thr, setter);
            duk_push_tval(thr, tv_obj);  /* note: original, uncoerced base */
            duk_push_tval(thr, tv_val);  /* [key setter this val] */
#if defined(DUK_USE_NONSTD_SETTER_KEY_ARGUMENT)
            duk_dup_m4(thr);
            duk_call_method(thr, 2);     /* [key setter this val key] -> [key retval] */
#else
            duk_call_method(thr, 1);     /* [key setter this val] -> [key retval] */
#endif
            duk_pop_unsafe(thr);         /* ignore retval -> [key] */
            goto success_no_arguments_exotic;
        }

        if (orig == NULL) {
            /*
             *  Found existing own or inherited plain property, but original
             *  base is a primitive value.
             */
            DUK_DD(DUK_DDPRINT("attempt to create a new property in a primitive base object"));
            goto fail_base_primitive;
        }

        if (curr != orig) {
            /*
             *  Found existing inherited plain property.
             *  Do an access control check, and if OK, write
             *  new property to 'orig'.
             */
            if (!DUK_HOBJECT_HAS_EXTENSIBLE(orig)) {
                DUK_DD(DUK_DDPRINT("found existing inherited plain property, but original object is not extensible"));
                goto fail_not_extensible;
            }
            if (!(desc.flags & DUK_PROPDESC_FLAG_WRITABLE)) {
                DUK_DD(DUK_DDPRINT("found existing inherited plain property, original object is extensible, but inherited property is not writable"));
                goto fail_not_writable;
            }
            DUK_DD(DUK_DDPRINT("put to new property, object extensible, inherited property found and is writable"));
            goto create_new;
        } else {
            /*
             *  Found existing own (non-inherited) plain property.
             *  Do an access control check and update in place.
             */

            if (!(desc.flags & DUK_PROPDESC_FLAG_WRITABLE)) {
                DUK_DD(DUK_DDPRINT("found existing own (non-inherited) plain property, but property is not writable"));
                goto fail_not_writable;
            }
            if (desc.flags & DUK_PROPDESC_FLAG_VIRTUAL) {
                DUK_DD(DUK_DDPRINT("found existing own (non-inherited) virtual property, property is writable"));

                if (DUK_HOBJECT_IS_ARRAY(curr)) {
                    /*
                     *  Write to 'length' of an array is a very complex case
                     *  handled in a helper which updates both the array elements
                     *  and writes the new 'length'.  The write may result in an
                     *  unconditional RangeError or a partial write (indicated
                     *  by a return code).
                     *
                     *  Note: the helper has an unnecessary writability check
                     *  for 'length', we already know it is writable.
                     */
                    DUK_ASSERT(key == DUK_HTHREAD_STRING_LENGTH(thr));  /* only virtual array property */

                    DUK_DDD(DUK_DDDPRINT("writing existing 'length' property to array exotic, invoke complex helper"));

                    /* XXX: the helper currently assumes stack top contains new
                     * 'length' value and the whole calling convention is not very
                     * compatible with what we need.
                     */

                    duk_push_tval(thr, tv_val);  /* [key val] */
                    rc = duk__handle_put_array_length(thr, orig);
                    duk_pop_unsafe(thr);  /* [key val] -> [key] */
                    if (!rc) {
                        goto fail_array_length_partial;
                    }

                    /* key is 'length', cannot match argument exotic behavior */
                    goto success_no_arguments_exotic;
                }
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
                else if (DUK_HOBJECT_IS_BUFOBJ(curr)) {
                    duk_hbufobj *h_bufobj;
                    duk_uint_t byte_off;
                    duk_small_uint_t elem_size;

                    h_bufobj = (duk_hbufobj *) curr;
                    DUK_HBUFOBJ_ASSERT_VALID(h_bufobj);

                    DUK_DD(DUK_DDPRINT("writable virtual property is in buffer object"));

                    /* Careful with wrapping: arr_idx upshift may easily wrap, whereas
                     * length downshift won't.
                     */
                    if (arr_idx < (h_bufobj->length >> h_bufobj->shift) && DUK_HBUFOBJ_HAS_VIRTUAL_INDICES(h_bufobj)) {
                        duk_uint8_t *data;
                        DUK_DDD(DUK_DDDPRINT("writing to buffer data at index %ld", (long) arr_idx));

                        DUK_ASSERT(arr_idx != DUK__NO_ARRAY_INDEX);  /* index/length check guarantees */
                        byte_off = arr_idx << h_bufobj->shift;       /* no wrap assuming h_bufobj->length is valid */
                        elem_size = (duk_small_uint_t) (1U << h_bufobj->shift);

                        /* Coerce to number before validating pointers etc so that the
                         * number coercions in duk_hbufobj_validated_write() are
                         * guaranteed to be side effect free and not invalidate the
                         * pointer checks we do here.
                         */
                        duk_push_tval(thr, tv_val);
                        (void) duk_to_number_m1(thr);

                        if (h_bufobj->buf != NULL && DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_bufobj, byte_off + elem_size)) {
                            data = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_bufobj->buf) + h_bufobj->offset + byte_off;
                            duk_hbufobj_validated_write(thr, h_bufobj, data, elem_size);
                        } else {
                            DUK_D(DUK_DPRINT("bufobj access out of underlying buffer, ignoring (write skipped)"));
                        }
                        duk_pop_unsafe(thr);
                        goto success_no_arguments_exotic;
                    }
                }
#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */

                DUK_D(DUK_DPRINT("should not happen, key %!O", key));
                goto fail_internal;  /* should not happen */
            }
            DUK_DD(DUK_DDPRINT("put to existing own plain property, property is writable"));
            goto update_old;
        }
        DUK_UNREACHABLE();

     next_in_chain:
        /* XXX: option to pretend property doesn't exist if sanity limit is
         * hit might be useful.
         */
        if (DUK_UNLIKELY(sanity-- == 0)) {
            DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT);
            DUK_WO_NORETURN(return 0;);
        }
        curr = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, curr);
    } while (curr != NULL);

    /*
     *  Property not found in prototype chain.
     */

    DUK_DDD(DUK_DDDPRINT("property not found in prototype chain"));

    if (orig == NULL) {
        DUK_DD(DUK_DDPRINT("attempt to create a new property in a primitive base object"));
        goto fail_base_primitive;
    }

    if (!DUK_HOBJECT_HAS_EXTENSIBLE(orig)) {
        DUK_DD(DUK_DDPRINT("put to a new property (not found in prototype chain), but original object not extensible"));
        goto fail_not_extensible;
    }

    goto create_new;

 update_old:

    /*
     *  Update an existing property of the base object.
     */

    /* [key] */

    DUK_DDD(DUK_DDDPRINT("update an existing property of the original object"));

    DUK_ASSERT(orig != NULL);
#if defined(DUK_USE_ROM_OBJECTS)
    /* This should not happen because DUK_TAG_OBJECT case checks
     * for this already, but check just in case.
     */
    if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) orig)) {
        goto fail_not_writable;
    }
#endif

    /* Although there are writable virtual properties (e.g. plain buffer
     * and buffer object number indices), they are handled before we come
     * here.
     */
    DUK_ASSERT((desc.flags & DUK_PROPDESC_FLAG_VIRTUAL) == 0);
    DUK_ASSERT(desc.a_idx >= 0 || desc.e_idx >= 0);

    /* Array own property .length is handled above. */
    DUK_ASSERT(!(DUK_HOBJECT_IS_ARRAY(orig) && key == DUK_HTHREAD_STRING_LENGTH(thr)));

    if (desc.e_idx >= 0) {
        tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, orig, desc.e_idx);
        DUK_DDD(DUK_DDDPRINT("previous entry value: %!iT", (duk_tval *) tv));
        DUK_TVAL_SET_TVAL_UPDREF(thr, tv, tv_val);  /* side effects; e_idx may be invalidated */
        /* don't touch property attributes or hash part */
        DUK_DD(DUK_DDPRINT("put to an existing entry at index %ld -> new value %!iT",
                           (long) desc.e_idx, (duk_tval *) tv));
    } else {
        /* Note: array entries are always writable, so the writability check
         * above is pointless for them.  The check could be avoided with some
         * refactoring but is probably not worth it.
         */

        DUK_ASSERT(desc.a_idx >= 0);
        tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, orig, desc.a_idx);
        DUK_DDD(DUK_DDDPRINT("previous array value: %!iT", (duk_tval *) tv));
        DUK_TVAL_SET_TVAL_UPDREF(thr, tv, tv_val);  /* side effects; a_idx may be invalidated */
        DUK_DD(DUK_DDPRINT("put to an existing array entry at index %ld -> new value %!iT",
                           (long) desc.a_idx, (duk_tval *) tv));
    }

    /* Regardless of whether property is found in entry or array part,
     * it may have arguments exotic behavior (array indices may reside
     * in entry part for abandoned / non-existent array parts).
     */
    goto success_with_arguments_exotic;

 create_new:

    /*
     *  Create a new property in the original object.
     *
     *  Exotic properties need to be reconsidered here from a write
     *  perspective (not just property attributes perspective).
     *  However, the property does not exist in the object already,
     *  so this limits the kind of exotic properties that apply.
     */

    /* [key] */

    DUK_DDD(DUK_DDDPRINT("create new property to original object"));

    DUK_ASSERT(orig != NULL);

    /* Array own property .length is handled above. */
    DUK_ASSERT(!(DUK_HOBJECT_IS_ARRAY(orig) && key == DUK_HTHREAD_STRING_LENGTH(thr)));

#if defined(DUK_USE_ROM_OBJECTS)
    /* This should not happen because DUK_TAG_OBJECT case checks
     * for this already, but check just in case.
     */
    if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) orig)) {
        goto fail_not_writable;
    }
#endif

    /* Not possible because array object 'length' is present
     * from its creation and cannot be deleted, and is thus
     * caught as an existing property above.
     */
    DUK_ASSERT(!(DUK_HOBJECT_HAS_EXOTIC_ARRAY(orig) &&
                 key == DUK_HTHREAD_STRING_LENGTH(thr)));

    if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(orig) &&
        arr_idx != DUK__NO_ARRAY_INDEX) {
        /* automatic length update */
        duk_uint32_t old_len;
        duk_harray *a;

        a = (duk_harray *) orig;
        DUK_HARRAY_ASSERT_VALID(a);

        old_len = a->length;

        if (arr_idx >= old_len) {
            DUK_DDD(DUK_DDDPRINT("write new array entry requires length update "
                                 "(arr_idx=%ld, old_len=%ld)",
                                 (long) arr_idx, (long) old_len));

            if (DUK_HARRAY_LENGTH_NONWRITABLE(a)) {
                DUK_DD(DUK_DDPRINT("attempt to extend array, but array 'length' is not writable"));
                goto fail_not_writable;
            }

            /* Note: actual update happens once write has been completed
             * without error below.  The write should always succeed
             * from a specification viewpoint, but we may e.g. run out
             * of memory.  It's safer in this order.
             */

            DUK_ASSERT(arr_idx != 0xffffffffUL);
            new_array_length = arr_idx + 1;  /* flag for later write */
        } else {
            DUK_DDD(DUK_DDDPRINT("write new array entry does not require length update "
                                 "(arr_idx=%ld, old_len=%ld)",
                                 (long) arr_idx, (long) old_len));
        }
    }

 /* write_to_array_part: */

    /*
     *  Write to array part?
     *
     *  Note: array abandonding requires a property resize which uses
     *  'rechecks' valstack for temporaries and may cause any existing
     *  valstack pointers to be invalidated.  To protect against this,
     *  tv_obj, tv_key, and tv_val are copies of the original inputs.
     */

    if (arr_idx != DUK__NO_ARRAY_INDEX && DUK_HOBJECT_HAS_ARRAY_PART(orig)) {
        tv = duk__obtain_arridx_slot(thr, arr_idx, orig);
        if (tv == NULL) {
            DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(orig));
            goto write_to_entry_part;
        }

        /* prev value must be unused, no decref */
        DUK_ASSERT(DUK_TVAL_IS_UNUSED(tv));
        DUK_TVAL_SET_TVAL(tv, tv_val);
        DUK_TVAL_INCREF(thr, tv);
        DUK_DD(DUK_DDPRINT("put to new array entry: %ld -> %!T",
                           (long) arr_idx, (duk_tval *) tv));

        /* Note: array part values are [[Writable]], [[Enumerable]],
         * and [[Configurable]] which matches the required attributes
         * here.
         */
        goto entry_updated;
    }

 write_to_entry_part:

    /*
     *  Write to entry part
     */

    /* entry allocation updates hash part and increases the key
     * refcount; may need a props allocation resize but doesn't
     * 'recheck' the valstack.
     */
    e_idx = duk__hobject_alloc_entry_checked(thr, orig, key);
    DUK_ASSERT(e_idx >= 0);

    tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, orig, e_idx);
    /* prev value can be garbage, no decref */
    DUK_TVAL_SET_TVAL(tv, tv_val);
    DUK_TVAL_INCREF(thr, tv);
    DUK_HOBJECT_E_SET_FLAGS(thr->heap, orig, e_idx, DUK_PROPDESC_FLAGS_WEC);
    goto entry_updated;

 entry_updated:

    /*
     *  Possible pending array length update, which must only be done
     *  if the actual entry write succeeded.
     */

    if (new_array_length > 0) {
        /* Note: zero works as a "no update" marker because the new length
         * can never be zero after a new property is written.
         */

        DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_ARRAY(orig));

        DUK_DDD(DUK_DDDPRINT("write successful, pending array length update to: %ld",
                             (long) new_array_length));

        ((duk_harray *) orig)->length = new_array_length;
    }

    /*
     *  Arguments exotic behavior not possible for new properties: all
     *  magically bound properties are initially present in the arguments
     *  object, and if they are deleted, the binding is also removed from
     *  parameter map.
     */

    goto success_no_arguments_exotic;

 success_with_arguments_exotic:

    /*
     *  Arguments objects have exotic [[DefineOwnProperty]] which updates
     *  the internal 'map' of arguments for writes to currently mapped
     *  arguments.  More conretely, writes to mapped arguments generate
     *  a write to a bound variable.
     *
     *  The [[Put]] algorithm invokes [[DefineOwnProperty]] for existing
     *  data properties and new properties, but not for existing accessors.
     *  Hence, in E5 Section 10.6 ([[DefinedOwnProperty]] algorithm), we
     *  have a Desc with 'Value' (and possibly other properties too), and
     *  we end up in step 5.b.i.
     */

    if (arr_idx != DUK__NO_ARRAY_INDEX &&
        DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(orig)) {
        /* Note: only numbered indices are relevant, so arr_idx fast reject
         * is good (this is valid unless there are more than 4**32-1 arguments).
         */

        DUK_DDD(DUK_DDDPRINT("putprop successful, arguments exotic behavior needed"));

        /* Note: we can reuse 'desc' here */

        /* XXX: top of stack must contain value, which helper doesn't touch,
         * rework to use tv_val directly?
         */

        duk_push_tval(thr, tv_val);
        (void) duk__check_arguments_map_for_put(thr, orig, key, &desc, throw_flag);
        duk_pop_unsafe(thr);
    }
    /* fall thru */

 success_no_arguments_exotic:
    /* shared exit path now */
    DUK_DDD(DUK_DDDPRINT("result: success"));
    duk_pop_unsafe(thr);  /* remove key */
    return 1;

#if defined(DUK_USE_ES6_PROXY)
 fail_proxy_rejected:
    DUK_DDD(DUK_DDDPRINT("result: error, proxy rejects"));
    if (throw_flag) {
        DUK_ERROR_TYPE(thr, DUK_STR_PROXY_REJECTED);
        DUK_WO_NORETURN(return 0;);
    }
    /* Note: no key on stack */
    return 0;
#endif

 fail_base_primitive:
    DUK_DDD(DUK_DDDPRINT("result: error, base primitive"));
    if (throw_flag) {
#if defined(DUK_USE_PARANOID_ERRORS)
        DUK_ERROR_TYPE(thr, DUK_STR_INVALID_BASE);
#else
        DUK_ERROR_FMT2(thr, DUK_ERR_TYPE_ERROR, "cannot write property %s of %s",
                       duk_push_string_tval_readable(thr, tv_key), duk_push_string_tval_readable(thr, tv_obj));
#endif
        DUK_WO_NORETURN(return 0;);
    }
    duk_pop_unsafe(thr);  /* remove key */
    return 0;

 fail_not_extensible:
    DUK_DDD(DUK_DDDPRINT("result: error, not extensible"));
    if (throw_flag) {
        DUK_ERROR_TYPE(thr, DUK_STR_NOT_EXTENSIBLE);
        DUK_WO_NORETURN(return 0;);
    }
    duk_pop_unsafe(thr);  /* remove key */
    return 0;

 fail_not_writable:
    DUK_DDD(DUK_DDDPRINT("result: error, not writable"));
    if (throw_flag) {
        DUK_ERROR_TYPE(thr, DUK_STR_NOT_WRITABLE);
        DUK_WO_NORETURN(return 0;);
    }
    duk_pop_unsafe(thr);  /* remove key */
    return 0;

#if defined(DUK_USE_ROM_OBJECTS)
 fail_not_writable_no_pop:
    DUK_DDD(DUK_DDDPRINT("result: error, not writable"));
    if (throw_flag) {
        DUK_ERROR_TYPE(thr, DUK_STR_NOT_WRITABLE);
        DUK_WO_NORETURN(return 0;);
    }
    return 0;
#endif

 fail_array_length_partial:
    DUK_DD(DUK_DDPRINT("result: error, array length write only partially successful"));
    if (throw_flag) {
        DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONFIGURABLE);
        DUK_WO_NORETURN(return 0;);
    }
    duk_pop_unsafe(thr);  /* remove key */
    return 0;

 fail_no_setter:
    DUK_DDD(DUK_DDDPRINT("result: error, accessor property without setter"));
    if (throw_flag) {
        DUK_ERROR_TYPE(thr, DUK_STR_SETTER_UNDEFINED);
        DUK_WO_NORETURN(return 0;);
    }
    duk_pop_unsafe(thr);  /* remove key */
    return 0;

 fail_internal:
    DUK_DDD(DUK_DDDPRINT("result: error, internal"));
    if (throw_flag) {
        DUK_ERROR_INTERNAL(thr);
        DUK_WO_NORETURN(return 0;);
    }
    duk_pop_unsafe(thr);  /* remove key */
    return 0;
}

/*
 *  ECMAScript compliant [[Delete]](P, Throw).
 */

DUK_INTERNAL duk_bool_t duk_hobject_delprop_raw(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t flags) {
    duk_propdesc desc;
    duk_tval *tv;
    duk_uint32_t arr_idx;
    duk_bool_t throw_flag;
    duk_bool_t force_flag;

    throw_flag = (flags & DUK_DELPROP_FLAG_THROW);
    force_flag = (flags & DUK_DELPROP_FLAG_FORCE);

    DUK_DDD(DUK_DDDPRINT("delprop_raw: thr=%p, obj=%p, key=%p, throw=%ld, force=%ld (obj -> %!O, key -> %!O)",
                         (void *) thr, (void *) obj, (void *) key, (long) throw_flag, (long) force_flag,
                         (duk_heaphdr *) obj, (duk_heaphdr *) key));

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT(obj != NULL);
    DUK_ASSERT(key != NULL);

    DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);

    arr_idx = DUK_HSTRING_GET_ARRIDX_FAST(key);

    /* 0 = don't push current value */
    if (!duk__get_own_propdesc_raw(thr, obj, key, arr_idx, &desc, 0 /*flags*/)) {  /* don't push value */
        DUK_DDD(DUK_DDDPRINT("property not found, succeed always"));
        goto success;
    }

#if defined(DUK_USE_ROM_OBJECTS)
    if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)) {
        DUK_DD(DUK_DDPRINT("attempt to delprop on read-only target object"));
        goto fail_not_configurable;
    }
#endif

    if ((desc.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) == 0 && !force_flag) {
        goto fail_not_configurable;
    }
    if (desc.a_idx < 0 && desc.e_idx < 0) {
        /* Currently there are no deletable virtual properties, but
         * with force_flag we might attempt to delete one.
         */
        DUK_DD(DUK_DDPRINT("delete failed: property found, force flag, but virtual (and implicitly non-configurable)"));
        goto fail_virtual;
    }

    if (desc.a_idx >= 0) {
        DUK_ASSERT(desc.e_idx < 0);

        tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, desc.a_idx);
        DUK_TVAL_SET_UNUSED_UPDREF(thr, tv);  /* side effects */
        goto success;
    } else {
        DUK_ASSERT(desc.a_idx < 0);

        /* remove hash entry (no decref) */
#if defined(DUK_USE_HOBJECT_HASH_PART)
        if (desc.h_idx >= 0) {
            duk_uint32_t *h_base = DUK_HOBJECT_H_GET_BASE(thr->heap, obj);

            DUK_DDD(DUK_DDDPRINT("removing hash entry at h_idx %ld", (long) desc.h_idx));
            DUK_ASSERT(DUK_HOBJECT_GET_HSIZE(obj) > 0);
            DUK_ASSERT((duk_uint32_t) desc.h_idx < DUK_HOBJECT_GET_HSIZE(obj));
            h_base[desc.h_idx] = DUK__HASH_DELETED;
        } else {
            DUK_ASSERT(DUK_HOBJECT_GET_HSIZE(obj) == 0);
        }
#else
        DUK_ASSERT(DUK_HOBJECT_GET_HSIZE(obj) == 0);
#endif

        /* Remove value.  This requires multiple writes so avoid side
         * effects via no-refzero macros so that e_idx is not
         * invalidated.
         */
        DUK_DDD(DUK_DDDPRINT("before removing value, e_idx %ld, key %p, key at slot %p",
                             (long) desc.e_idx, (void *) key, (void *) DUK_HOBJECT_E_GET_KEY(thr->heap, obj, desc.e_idx)));
        DUK_DDD(DUK_DDDPRINT("removing value at e_idx %ld", (long) desc.e_idx));
        if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, desc.e_idx)) {
            duk_hobject *tmp;

            tmp = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, obj, desc.e_idx);
            DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, obj, desc.e_idx, NULL);
            DUK_UNREF(tmp);
            DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, tmp);

            tmp = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, obj, desc.e_idx);
            DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, obj, desc.e_idx, NULL);
            DUK_UNREF(tmp);
            DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, tmp);
        } else {
            tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, desc.e_idx);
            DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ(thr, tv);
        }
#if 0
        /* Not strictly necessary because if key == NULL, flag MUST be ignored. */
        DUK_HOBJECT_E_SET_FLAGS(thr->heap, obj, desc.e_idx, 0);
#endif

        /* Remove key. */
        DUK_DDD(DUK_DDDPRINT("before removing key, e_idx %ld, key %p, key at slot %p",
                             (long) desc.e_idx, (void *) key, (void *) DUK_HOBJECT_E_GET_KEY(thr->heap, obj, desc.e_idx)));
        DUK_DDD(DUK_DDDPRINT("removing key at e_idx %ld", (long) desc.e_idx));
        DUK_ASSERT(key == DUK_HOBJECT_E_GET_KEY(thr->heap, obj, desc.e_idx));
        DUK_HOBJECT_E_SET_KEY(thr->heap, obj, desc.e_idx, NULL);
        DUK_HSTRING_DECREF_NORZ(thr, key);

        /* Trigger refzero side effects only when we're done as a
         * finalizer might operate on the object and affect the
         * e_idx we're supposed to use.
         */
        DUK_REFZERO_CHECK_SLOW(thr);
        goto success;
    }

    DUK_UNREACHABLE();

 success:
    /*
     *  Argument exotic [[Delete]] behavior (E5 Section 10.6) is
     *  a post-check, keeping arguments internal 'map' in sync with
     *  any successful deletes (note that property does not need to
     *  exist for delete to 'succeed').
     *
     *  Delete key from 'map'.  Since 'map' only contains array index
     *  keys, we can use arr_idx for a fast skip.
     */

    DUK_DDD(DUK_DDDPRINT("delete successful, check for arguments exotic behavior"));

    if (arr_idx != DUK__NO_ARRAY_INDEX && DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj)) {
        /* Note: only numbered indices are relevant, so arr_idx fast reject
         * is good (this is valid unless there are more than 4**32-1 arguments).
         */

        DUK_DDD(DUK_DDDPRINT("delete successful, arguments exotic behavior needed"));

        /* Note: we can reuse 'desc' here */
        (void) duk__check_arguments_map_for_delete(thr, obj, key, &desc);
    }

    DUK_DDD(DUK_DDDPRINT("delete successful"));
    return 1;

 fail_virtual:  /* just use the same "not configurable" error message */
 fail_not_configurable:
    DUK_DDD(DUK_DDDPRINT("delete failed: property found, not configurable"));

    if (throw_flag) {
        DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONFIGURABLE);
        DUK_WO_NORETURN(return 0;);
    }
    return 0;
}

/*
 *  DELPROP: ECMAScript property deletion.
 */

DUK_INTERNAL duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, duk_bool_t throw_flag) {
    duk_hstring *key = NULL;
#if defined(DUK_USE_ES6_PROXY)
    duk_propdesc desc;
#endif
    duk_int_t entry_top;
    duk_uint32_t arr_idx = DUK__NO_ARRAY_INDEX;
    duk_bool_t rc;

    DUK_DDD(DUK_DDDPRINT("delprop: thr=%p, obj=%p, key=%p (obj -> %!T, key -> %!T)",
                         (void *) thr, (void *) tv_obj, (void *) tv_key,
                         (duk_tval *) tv_obj, (duk_tval *) tv_key));

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT(tv_obj != NULL);
    DUK_ASSERT(tv_key != NULL);

    DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);

    /* Storing the entry top is cheaper here to ensure stack is correct at exit,
     * as there are several paths out.
     */
    entry_top = duk_get_top(thr);

    if (DUK_TVAL_IS_UNDEFINED(tv_obj) ||
        DUK_TVAL_IS_NULL(tv_obj)) {
        DUK_DDD(DUK_DDDPRINT("base object is undefined or null -> reject"));
        goto fail_invalid_base_uncond;
    }

    duk_push_tval(thr, tv_obj);
    duk_push_tval(thr, tv_key);

    tv_obj = DUK_GET_TVAL_NEGIDX(thr, -2);
    if (DUK_TVAL_IS_OBJECT(tv_obj)) {
        duk_hobject *obj = DUK_TVAL_GET_OBJECT(tv_obj);
        DUK_ASSERT(obj != NULL);

#if defined(DUK_USE_ES6_PROXY)
        if (DUK_UNLIKELY(DUK_HOBJECT_IS_PROXY(obj))) {
            duk_hobject *h_target;
            duk_bool_t tmp_bool;

            /* Note: proxy handling must happen before key is string coerced. */

            if (duk__proxy_check_prop(thr, obj, DUK_STRIDX_DELETE_PROPERTY, tv_key, &h_target)) {
                /* -> [ ... obj key trap handler ] */
                DUK_DDD(DUK_DDDPRINT("-> proxy object 'deleteProperty' for key %!T", (duk_tval *) tv_key));
                duk_push_hobject(thr, h_target);  /* target */
                duk_dup_m4(thr);  /* P */
                duk_call_method(thr, 2 /*nargs*/);
                tmp_bool = duk_to_boolean_top_pop(thr);
                if (!tmp_bool) {
                    goto fail_proxy_rejected;  /* retval indicates delete failed */
                }

                /* Target object must be checked for a conflicting
                 * non-configurable property.
                 */
                tv_key = DUK_GET_TVAL_NEGIDX(thr, -1);
                arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
                DUK_ASSERT(key != NULL);

                if (duk__get_own_propdesc_raw(thr, h_target, key, arr_idx, &desc, 0 /*flags*/)) {  /* don't push value */
                    duk_small_int_t desc_reject;

                    DUK_DDD(DUK_DDDPRINT("proxy 'deleteProperty': target has matching property %!O, check for "
                                         "conflicting property; desc.flags=0x%08lx, "
                                         "desc.get=%p, desc.set=%p",
                                         (duk_heaphdr *) key, (unsigned long) desc.flags,
                                         (void *) desc.get, (void *) desc.set));

                    desc_reject = !(desc.flags & DUK_PROPDESC_FLAG_CONFIGURABLE);
                    if (desc_reject) {
                        /* unconditional */
                        DUK_ERROR_TYPE(thr, DUK_STR_PROXY_REJECTED);
                        DUK_WO_NORETURN(return 0;);
                    }
                }
                rc = 1;  /* success */
                goto done_rc;
            }

            obj = h_target;  /* resume delete to target */
        }
#endif  /* DUK_USE_ES6_PROXY */

        arr_idx = duk__to_property_key(thr, -1, &key);
        DUK_ASSERT(key != NULL);

        rc = duk_hobject_delprop_raw(thr, obj, key, throw_flag ? DUK_DELPROP_FLAG_THROW : 0);
        goto done_rc;
    } else if (DUK_TVAL_IS_STRING(tv_obj)) {
        /* String has .length and array index virtual properties
         * which can't be deleted.  No need for a symbol check;
         * no offending virtual symbols exist.
         */
        /* XXX: unnecessary string coercion for array indices,
         * intentional to keep small.
         */
        duk_hstring *h = DUK_TVAL_GET_STRING(tv_obj);
        DUK_ASSERT(h != NULL);

        arr_idx = duk__to_property_key(thr, -1, &key);
        DUK_ASSERT(key != NULL);

        if (key == DUK_HTHREAD_STRING_LENGTH(thr)) {
            goto fail_not_configurable;
        }

        if (arr_idx != DUK__NO_ARRAY_INDEX &&
            arr_idx < DUK_HSTRING_GET_CHARLEN(h)) {
            goto fail_not_configurable;
        }
    } else if (DUK_TVAL_IS_BUFFER(tv_obj)) {
        /* XXX: unnecessary string coercion for array indices,
         * intentional to keep small; some overlap with string
         * handling.
         */
        duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv_obj);
        DUK_ASSERT(h != NULL);

        arr_idx = duk__to_property_key(thr, -1, &key);
        DUK_ASSERT(key != NULL);

        if (key == DUK_HTHREAD_STRING_LENGTH(thr)) {
            goto fail_not_configurable;
        }

        if (arr_idx != DUK__NO_ARRAY_INDEX &&
            arr_idx < DUK_HBUFFER_GET_SIZE(h)) {
            goto fail_not_configurable;
        }
    } else if (DUK_TVAL_IS_LIGHTFUNC(tv_obj)) {
        /* Lightfunc has no virtual properties since Duktape 2.2
         * so success.  Still must coerce key for side effects.
         */

        arr_idx = duk__to_property_key(thr, -1, &key);
        DUK_ASSERT(key != NULL);
        DUK_UNREF(key);
    }

    /* non-object base, no offending virtual property */
    rc = 1;
    goto done_rc;

 done_rc:
    duk_set_top_unsafe(thr, entry_top);
    return rc;

 fail_invalid_base_uncond:
    /* Note: unconditional throw */
    DUK_ASSERT(duk_get_top(thr) == entry_top);
#if defined(DUK_USE_PARANOID_ERRORS)
    DUK_ERROR_TYPE(thr, DUK_STR_INVALID_BASE);
#else
    DUK_ERROR_FMT2(thr, DUK_ERR_TYPE_ERROR, "cannot delete property %s of %s",
                   duk_push_string_tval_readable(thr, tv_key), duk_push_string_tval_readable(thr, tv_obj));
#endif
    DUK_WO_NORETURN(return 0;);

#if defined(DUK_USE_ES6_PROXY)
 fail_proxy_rejected:
    if (throw_flag) {
        DUK_ERROR_TYPE(thr, DUK_STR_PROXY_REJECTED);
        DUK_WO_NORETURN(return 0;);
    }
    duk_set_top_unsafe(thr, entry_top);
    return 0;
#endif

 fail_not_configurable:
    if (throw_flag) {
        DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONFIGURABLE);
        DUK_WO_NORETURN(return 0;);
    }
    duk_set_top_unsafe(thr, entry_top);
    return 0;
}

/*
 *  Internal helper to define a property with specific flags, ignoring
 *  normal semantics such as extensibility, write protection etc.
 *  Overwrites any existing value and attributes unless caller requests
 *  that value only be updated if it doesn't already exists.
 *
 *  Does not support:
 *    - virtual properties (error if write attempted)
 *    - getter/setter properties (error if write attempted)
 *    - non-default (!= WEC) attributes for array entries (error if attempted)
 *    - array abandoning: if array part exists, it is always extended
 *    - array 'length' updating
 *
 *  Stack: [... in_val] -> []
 *
 *  Used for e.g. built-in initialization and environment record
 *  operations.
 */

DUK_INTERNAL void duk_hobject_define_property_internal(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t flags) {
    duk_propdesc desc;
    duk_uint32_t arr_idx;
    duk_int_t e_idx;
    duk_tval *tv1 = NULL;
    duk_tval *tv2 = NULL;
    duk_small_uint_t propflags = flags & DUK_PROPDESC_FLAGS_MASK;  /* mask out flags not actually stored */

    DUK_DDD(DUK_DDDPRINT("define new property (internal): thr=%p, obj=%!O, key=%!O, flags=0x%02lx, val=%!T",
                         (void *) thr, (duk_heaphdr *) obj, (duk_heaphdr *) key,
                         (unsigned long) flags, (duk_tval *) duk_get_tval(thr, -1)));

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT(obj != NULL);
    DUK_ASSERT(key != NULL);
    DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj));
    DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);
    DUK_ASSERT(duk_is_valid_index(thr, -1));  /* contains value */

    arr_idx = DUK_HSTRING_GET_ARRIDX_SLOW(key);

    if (duk__get_own_propdesc_raw(thr, obj, key, arr_idx, &desc, 0 /*flags*/)) {  /* don't push value */
        if (desc.e_idx >= 0) {
            if (flags & DUK_PROPDESC_FLAG_NO_OVERWRITE) {
                DUK_DDD(DUK_DDDPRINT("property already exists in the entry part -> skip as requested"));
                goto pop_exit;
            }
            DUK_DDD(DUK_DDDPRINT("property already exists in the entry part -> update value and attributes"));
            if (DUK_UNLIKELY(DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, desc.e_idx))) {
                DUK_D(DUK_DPRINT("existing property is an accessor, not supported"));
                goto error_internal;
            }

            DUK_HOBJECT_E_SET_FLAGS(thr->heap, obj, desc.e_idx, propflags);
            tv1 = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, desc.e_idx);
        } else if (desc.a_idx >= 0) {
            if (flags & DUK_PROPDESC_FLAG_NO_OVERWRITE) {
                DUK_DDD(DUK_DDDPRINT("property already exists in the array part -> skip as requested"));
                goto pop_exit;
            }
            DUK_DDD(DUK_DDDPRINT("property already exists in the array part -> update value (assert attributes)"));
            if (propflags != DUK_PROPDESC_FLAGS_WEC) {
                DUK_D(DUK_DPRINT("existing property in array part, but propflags not WEC (0x%02lx)",
                                 (unsigned long) propflags));
                goto error_internal;
            }

            tv1 = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, desc.a_idx);
        } else {
            if (flags & DUK_PROPDESC_FLAG_NO_OVERWRITE) {
                DUK_DDD(DUK_DDDPRINT("property already exists but is virtual -> skip as requested"));
                goto pop_exit;
            }
            if (key == DUK_HTHREAD_STRING_LENGTH(thr) && DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)) {
                duk_uint32_t new_len;
#if defined(DUK_USE_DEBUG)
                duk_uint32_t prev_len;
                prev_len = ((duk_harray *) obj)->length;
#endif
                new_len = duk__to_new_array_length_checked(thr, DUK_GET_TVAL_NEGIDX(thr, -1));
                ((duk_harray *) obj)->length = new_len;
                DUK_DD(DUK_DDPRINT("internal define property for array .length: %ld -> %ld",
                                   (long) prev_len, (long) ((duk_harray *) obj)->length));
                goto pop_exit;
            }
            DUK_DD(DUK_DDPRINT("property already exists but is virtual -> failure"));
            goto error_virtual;
        }

        goto write_value;
    }

    if (DUK_HOBJECT_HAS_ARRAY_PART(obj)) {
        if (arr_idx != DUK__NO_ARRAY_INDEX) {
            DUK_DDD(DUK_DDDPRINT("property does not exist, object has array part -> possibly extend array part and write value (assert attributes)"));
            DUK_ASSERT(propflags == DUK_PROPDESC_FLAGS_WEC);

            tv1 = duk__obtain_arridx_slot(thr, arr_idx, obj);
            if (tv1 == NULL) {
                DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(obj));
                goto write_to_entry_part;
            }

            tv1 = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, arr_idx);
            goto write_value;
        }
    }

 write_to_entry_part:
    DUK_DDD(DUK_DDDPRINT("property does not exist, object belongs in entry part -> allocate new entry and write value and attributes"));
    e_idx = duk__hobject_alloc_entry_checked(thr, obj, key);  /* increases key refcount */
    DUK_ASSERT(e_idx >= 0);
    DUK_HOBJECT_E_SET_FLAGS(thr->heap, obj, e_idx, propflags);
    tv1 = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, e_idx);
    /* new entry: previous value is garbage; set to undefined to share write_value */
    DUK_TVAL_SET_UNDEFINED(tv1);
    goto write_value;

 write_value:
    /* tv1 points to value storage */

    tv2 = duk_require_tval(thr, -1);  /* late lookup, avoid side effects */
    DUK_DDD(DUK_DDDPRINT("writing/updating value: %!T -> %!T",
                         (duk_tval *) tv1, (duk_tval *) tv2));

    DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv2);  /* side effects */
    goto pop_exit;

 pop_exit:
    duk_pop_unsafe(thr);  /* remove in_val */
    return;

 error_virtual:  /* share error message */
 error_internal:
    DUK_ERROR_INTERNAL(thr);
    DUK_WO_NORETURN(return;);
}

/*
 *  Fast path for defining array indexed values without interning the key.
 *  This is used by e.g. code for Array prototype and traceback creation so
 *  must avoid interning.
 */

DUK_INTERNAL void duk_hobject_define_property_internal_arridx(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t arr_idx, duk_small_uint_t flags) {
    duk_hstring *key;
    duk_tval *tv1, *tv2;

    DUK_DDD(DUK_DDDPRINT("define new property (internal) arr_idx fast path: thr=%p, obj=%!O, "
                         "arr_idx=%ld, flags=0x%02lx, val=%!T",
                         (void *) thr, obj, (long) arr_idx, (unsigned long) flags,
                         (duk_tval *) duk_get_tval(thr, -1)));

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT(obj != NULL);
    DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj));

    if (DUK_HOBJECT_HAS_ARRAY_PART(obj) &&
        arr_idx != DUK__NO_ARRAY_INDEX &&
        flags == DUK_PROPDESC_FLAGS_WEC) {
        DUK_ASSERT((flags & DUK_PROPDESC_FLAG_NO_OVERWRITE) == 0);  /* covered by comparison */

        DUK_DDD(DUK_DDDPRINT("define property to array part (property may or may not exist yet)"));

        tv1 = duk__obtain_arridx_slot(thr, arr_idx, obj);
        if (tv1 == NULL) {
            DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(obj));
            goto write_slow;
        }
        tv2 = duk_require_tval(thr, -1);

        DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv2);  /* side effects */

        duk_pop_unsafe(thr);  /* [ ...val ] -> [ ... ] */
        return;
    }

 write_slow:
    DUK_DDD(DUK_DDDPRINT("define property fast path didn't work, use slow path"));

    key = duk_push_uint_to_hstring(thr, (duk_uint_t) arr_idx);
    DUK_ASSERT(key != NULL);
    duk_insert(thr, -2);  /* [ ... val key ] -> [ ... key val ] */

    duk_hobject_define_property_internal(thr, obj, key, flags);

    duk_pop_unsafe(thr);  /* [ ... key ] -> [ ... ] */
}

/*
 *  Internal helpers for managing object 'length'
 */

DUK_INTERNAL duk_size_t duk_hobject_get_length(duk_hthread *thr, duk_hobject *obj) {
    duk_double_t val;

    DUK_CTX_ASSERT_VALID(thr);
    DUK_ASSERT(obj != NULL);

    /* Fast path for Arrays. */
    if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)) {
        return ((duk_harray *) obj)->length;
    }

    /* Slow path, .length can be e.g. accessor, obj can be a Proxy, etc. */
    duk_push_hobject(thr, obj);
    duk_push_hstring_stridx(thr, DUK_STRIDX_LENGTH);
    (void) duk_hobject_getprop(thr,
                               DUK_GET_TVAL_NEGIDX(thr, -2),
                               DUK_GET_TVAL_NEGIDX(thr, -1));
    val = duk_to_number_m1(thr);
    duk_pop_3_unsafe(thr);

    /* This isn't part of ECMAScript semantics; return a value within
     * duk_size_t range, or 0 otherwise.
     */
    if (val >= 0.0 && val <= (duk_double_t) DUK_SIZE_MAX) {
        return (duk_size_t) val;
    }
    return 0;
}

/*
 *  Fast finalizer check for an object.  Walks the prototype chain, checking
 *  for finalizer presence using DUK_HOBJECT_FLAG_HAVE_FINALIZER which is kept
 *  in sync with the actual property when setting/removing the finalizer.
 */

#if defined(DUK_USE_HEAPPTR16)
DUK_INTERNAL duk_bool_t duk_hobject_has_finalizer_fast_raw(duk_heap *heap, duk_hobject *obj) {
#else
DUK_INTERNAL duk_bool_t duk_hobject_has_finalizer_fast_raw(duk_hobject *obj) {
#endif
    duk_uint_t sanity;

    DUK_ASSERT(obj != NULL);

    sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
    do {
        if (DUK_UNLIKELY(DUK_HOBJECT_HAS_HAVE_FINALIZER(obj))) {
            return 1;
        }
        if (DUK_UNLIKELY(sanity-- == 0)) {
            DUK_D(DUK_DPRINT("prototype loop when checking for finalizer existence; returning false"));
            return 0;
        }
#if defined(DUK_USE_HEAPPTR16)
        DUK_ASSERT(heap != NULL);
        obj = DUK_HOBJECT_GET_PROTOTYPE(heap, obj);
#else
        obj = DUK_HOBJECT_GET_PROTOTYPE(NULL, obj);  /* 'heap' arg ignored */
#endif
    } while (obj != NULL);

    return 0;
}

/*
 *  Object.getOwnPropertyDescriptor()  (E5 Sections 15.2.3.3, 8.10.4)
 *
 *  [ ... key ] -> [ ... desc/undefined ]
 */

DUK_INTERNAL void duk_hobject_object_get_own_property_descriptor(duk_hthread *thr, duk_idx_t obj_idx) {
    duk_hobject *obj;
    duk_hstring *key;
    duk_propdesc pd;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);

    obj = duk_require_hobject_promote_mask(thr, obj_idx, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
    key = duk_to_property_key_hstring(thr, -1);
    DUK_ASSERT(key != NULL);

    DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);

    if (!duk_hobject_get_own_propdesc(thr, obj, key, &pd, DUK_GETDESC_FLAG_PUSH_VALUE)) {
        duk_push_undefined(thr);
        duk_remove_m2(thr);
        return;
    }

    duk_push_object(thr);

    /* [ ... key value desc ] */

    if (DUK_PROPDESC_IS_ACCESSOR(&pd)) {
        /* If a setter/getter is missing (undefined), the descriptor must
         * still have the property present with the value 'undefined'.
         */
        if (pd.get) {
            duk_push_hobject(thr, pd.get);
        } else {
            duk_push_undefined(thr);
        }
        duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_GET);
        if (pd.set) {
            duk_push_hobject(thr, pd.set);
        } else {
            duk_push_undefined(thr);
        }
        duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_SET);
    } else {
        duk_dup_m2(thr);
        duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_VALUE);
        duk_push_boolean(thr, DUK_PROPDESC_IS_WRITABLE(&pd));
        duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_WRITABLE);
    }
    duk_push_boolean(thr, DUK_PROPDESC_IS_ENUMERABLE(&pd));
    duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_ENUMERABLE);
    duk_push_boolean(thr, DUK_PROPDESC_IS_CONFIGURABLE(&pd));
    duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_CONFIGURABLE);

    /* [ ... key value desc ] */

    duk_replace(thr, -3);
    duk_pop_unsafe(thr);  /* -> [ ... desc ] */
}

/*
 *  NormalizePropertyDescriptor() related helper.
 *
 *  Internal helper which validates and normalizes a property descriptor
 *  represented as an ECMAScript object (e.g. argument to defineProperty()).
 *  The output of this conversion is a set of defprop_flags and possibly
 *  some values pushed on the value stack to (1) ensure borrowed pointers
 *  remain valid, and (2) avoid unnecessary pops for footprint reasons.
 *  Caller must manage stack top carefully because the number of values
 *  pushed depends on the input property descriptor.
 *
 *  The original descriptor object must not be altered in the process.
 */

/* XXX: very basic optimization -> duk_get_prop_stridx_top */

DUK_INTERNAL
void duk_hobject_prepare_property_descriptor(duk_hthread *thr,
                                             duk_idx_t idx_in,
                                             duk_uint_t *out_defprop_flags,
                                             duk_idx_t *out_idx_value,
                                             duk_hobject **out_getter,
                                             duk_hobject **out_setter) {
    duk_idx_t idx_value = -1;
    duk_hobject *getter = NULL;
    duk_hobject *setter = NULL;
    duk_bool_t is_data_desc = 0;
    duk_bool_t is_acc_desc = 0;
    duk_uint_t defprop_flags = 0;

    DUK_ASSERT(out_defprop_flags != NULL);
    DUK_ASSERT(out_idx_value != NULL);
    DUK_ASSERT(out_getter != NULL);
    DUK_ASSERT(out_setter != NULL);
    DUK_ASSERT(idx_in <= 0x7fffL);  /* short variants would be OK, but not used to avoid shifts */

    /* Must be an object, otherwise TypeError (E5.1 Section 8.10.5, step 1). */
    idx_in = duk_require_normalize_index(thr, idx_in);
    (void) duk_require_hobject(thr, idx_in);

    /* The coercion order must match the ToPropertyDescriptor() algorithm
     * so that side effects in coercion happen in the correct order.
     * (This order also happens to be compatible with duk_def_prop(),
     * although it doesn't matter in practice.)
     */

    if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_VALUE)) {
        is_data_desc = 1;
        defprop_flags |= DUK_DEFPROP_HAVE_VALUE;
        idx_value = duk_get_top_index(thr);
    }

    if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_WRITABLE)) {
        is_data_desc = 1;
        if (duk_to_boolean_top_pop(thr)) {
            defprop_flags |= DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_WRITABLE;
        } else {
            defprop_flags |= DUK_DEFPROP_HAVE_WRITABLE;
        }
    }

    if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_GET)) {
        duk_tval *tv = duk_require_tval(thr, -1);
        duk_hobject *h_get;

        if (DUK_TVAL_IS_UNDEFINED(tv)) {
            /* undefined is accepted */
            DUK_ASSERT(getter == NULL);
        } else {
            /* NOTE: lightfuncs are coerced to full functions because
             * lightfuncs don't fit into a property value slot.  This
             * has some side effects, see test-dev-lightfunc-accessor.js.
             */
            h_get = duk_get_hobject_promote_lfunc(thr, -1);
            if (h_get == NULL || !DUK_HOBJECT_IS_CALLABLE(h_get)) {
                goto type_error;
            }
            getter = h_get;
        }
        is_acc_desc = 1;
        defprop_flags |= DUK_DEFPROP_HAVE_GETTER;
    }

    if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_SET)) {
        duk_tval *tv = duk_require_tval(thr, -1);
        duk_hobject *h_set;

        if (DUK_TVAL_IS_UNDEFINED(tv)) {
            /* undefined is accepted */
            DUK_ASSERT(setter == NULL);
        }  else {
            /* NOTE: lightfuncs are coerced to full functions because
             * lightfuncs don't fit into a property value slot.  This
             * has some side effects, see test-dev-lightfunc-accessor.js.
             */
            h_set = duk_get_hobject_promote_lfunc(thr, -1);
            if (h_set == NULL || !DUK_HOBJECT_IS_CALLABLE(h_set)) {
                goto type_error;
            }
            setter = h_set;
        }
        is_acc_desc = 1;
        defprop_flags |= DUK_DEFPROP_HAVE_SETTER;
    }

    if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_ENUMERABLE)) {
        if (duk_to_boolean_top_pop(thr)) {
            defprop_flags |= DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE;
        } else {
            defprop_flags |= DUK_DEFPROP_HAVE_ENUMERABLE;
        }
    }

    if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_CONFIGURABLE)) {
        if (duk_to_boolean_top_pop(thr)) {
            defprop_flags |= DUK_DEFPROP_HAVE_CONFIGURABLE | DUK_DEFPROP_CONFIGURABLE;
        } else {
            defprop_flags |= DUK_DEFPROP_HAVE_CONFIGURABLE;
        }
    }

    if (is_data_desc && is_acc_desc) {
        goto type_error;
    }

    *out_defprop_flags = defprop_flags;
    *out_idx_value = idx_value;
    *out_getter = getter;
    *out_setter = setter;

    /* [ ... [multiple values] ] */
    return;

 type_error:
    DUK_ERROR_TYPE(thr, DUK_STR_INVALID_DESCRIPTOR);
    DUK_WO_NORETURN(return;);
}

/*
 *  Object.defineProperty() related helper (E5 Section 15.2.3.6).
 *  Also handles ES2015 Reflect.defineProperty().
 *
 *  Inlines all [[DefineOwnProperty]] exotic behaviors.
 *
 *  Note: ECMAScript compliant [[DefineOwnProperty]](P, Desc, Throw) is not
 *  implemented directly, but Object.defineProperty() serves its purpose.
 *  We don't need the [[DefineOwnProperty]] internally and we don't have a
 *  property descriptor with 'missing values' so it's easier to avoid it
 *  entirely.
 *
 *  Note: this is only called for actual objects, not primitive values.
 *  This must support virtual properties for full objects (e.g. Strings)
 *  but not for plain values (e.g. strings).  Lightfuncs, even though
 *  primitive in a sense, are treated like objects and accepted as target
 *  values.
 */

/* XXX: this is a major target for size optimization */
DUK_INTERNAL
duk_bool_t duk_hobject_define_property_helper(duk_hthread *thr,
                                              duk_uint_t defprop_flags,
                                              duk_hobject *obj,
                                              duk_hstring *key,
                                              duk_idx_t idx_value,
                                              duk_hobject *get,
                                              duk_hobject *set,
                                              duk_bool_t throw_flag) {
    duk_uint32_t arr_idx;
    duk_tval tv;
    duk_bool_t has_enumerable;
    duk_bool_t has_configurable;
    duk_bool_t has_writable;
    duk_bool_t has_value;
    duk_bool_t has_get;
    duk_bool_t has_set;
    duk_bool_t is_enumerable;
    duk_bool_t is_configurable;
    duk_bool_t is_writable;
    duk_bool_t force_flag;
    duk_small_uint_t new_flags;
    duk_propdesc curr;
    duk_uint32_t arridx_new_array_length;  /* != 0 => post-update for array 'length' (used when key is an array index) */
    duk_uint32_t arrlen_old_len;
    duk_uint32_t arrlen_new_len;
    duk_bool_t pending_write_protect;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT(obj != NULL);
    DUK_ASSERT(key != NULL);
    /* idx_value may be < 0 (no value), set and get may be NULL */

    DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);

    /* All the flags fit in 16 bits, so will fit into duk_bool_t. */

    has_writable = (defprop_flags & DUK_DEFPROP_HAVE_WRITABLE);
    has_enumerable = (defprop_flags & DUK_DEFPROP_HAVE_ENUMERABLE);
    has_configurable = (defprop_flags & DUK_DEFPROP_HAVE_CONFIGURABLE);
    has_value = (defprop_flags & DUK_DEFPROP_HAVE_VALUE);
    has_get = (defprop_flags & DUK_DEFPROP_HAVE_GETTER);
    has_set = (defprop_flags & DUK_DEFPROP_HAVE_SETTER);
    is_writable = (defprop_flags & DUK_DEFPROP_WRITABLE);
    is_enumerable = (defprop_flags & DUK_DEFPROP_ENUMERABLE);
    is_configurable = (defprop_flags & DUK_DEFPROP_CONFIGURABLE);
    force_flag = (defprop_flags & DUK_DEFPROP_FORCE);

    arr_idx = DUK_HSTRING_GET_ARRIDX_SLOW(key);

    arridx_new_array_length = 0;
    pending_write_protect = 0;
    arrlen_old_len = 0;
    arrlen_new_len = 0;

    DUK_DDD(DUK_DDDPRINT("has_enumerable=%ld is_enumerable=%ld "
                         "has_configurable=%ld is_configurable=%ld "
                         "has_writable=%ld is_writable=%ld "
                         "has_value=%ld value=%!T "
                         "has_get=%ld get=%p=%!O "
                         "has_set=%ld set=%p=%!O "
                         "arr_idx=%ld throw_flag=!%ld",
                         (long) has_enumerable, (long) is_enumerable,
                         (long) has_configurable, (long) is_configurable,
                         (long) has_writable, (long) is_writable,
                         (long) has_value, (duk_tval *) (idx_value >= 0 ? duk_get_tval(thr, idx_value) : NULL),
                         (long) has_get, (void *) get, (duk_heaphdr *) get,
                         (long) has_set, (void *) set, (duk_heaphdr *) set,
                         (long) arr_idx, (long) throw_flag));

    /*
     *  Array exotic behaviors can be implemented at this point.  The local variables
     *  are essentially a 'value copy' of the input descriptor (Desc), which is modified
     *  by the Array [[DefineOwnProperty]] (E5 Section 15.4.5.1).
     */

    if (!DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)) {
        goto skip_array_exotic;
    }

    if (key == DUK_HTHREAD_STRING_LENGTH(thr)) {
        duk_harray *a;

        /* E5 Section 15.4.5.1, step 3, steps a - i are implemented here, j - n at the end */
        if (!has_value) {
            DUK_DDD(DUK_DDDPRINT("exotic array behavior for 'length', but no value in descriptor -> normal behavior"));
            goto skip_array_exotic;
        }

        DUK_DDD(DUK_DDDPRINT("exotic array behavior for 'length', value present in descriptor -> exotic behavior"));

        /*
         *  Get old and new length
         */

        a = (duk_harray *) obj;
        DUK_HARRAY_ASSERT_VALID(a);
        arrlen_old_len = a->length;

        DUK_ASSERT(idx_value >= 0);
        arrlen_new_len = duk__to_new_array_length_checked(thr, DUK_GET_TVAL_POSIDX(thr, idx_value));
        duk_push_u32(thr, arrlen_new_len);
        duk_replace(thr, idx_value);  /* step 3.e: replace 'Desc.[[Value]]' */

        DUK_DDD(DUK_DDDPRINT("old_len=%ld, new_len=%ld", (long) arrlen_old_len, (long) arrlen_new_len));

        if (arrlen_new_len >= arrlen_old_len) {
            /* standard behavior, step 3.f.i */
            DUK_DDD(DUK_DDDPRINT("new length is same or higher as previous => standard behavior"));
            goto skip_array_exotic;
        }
        DUK_DDD(DUK_DDDPRINT("new length is smaller than previous => exotic post behavior"));

        /* XXX: consolidated algorithm step 15.f -> redundant? */
        if (DUK_HARRAY_LENGTH_NONWRITABLE(a) && !force_flag) {
            /* Array .length is always non-configurable; if it's also
             * non-writable, don't allow it to be written.
             */
            goto fail_not_configurable;
        }

        /* steps 3.h and 3.i */
        if (has_writable && !is_writable) {
            DUK_DDD(DUK_DDDPRINT("desc writable is false, force it back to true, and flag pending write protect"));
            is_writable = 1;
            pending_write_protect = 1;
        }

        /* remaining actual steps are carried out if standard DefineOwnProperty succeeds */
    } else if (arr_idx != DUK__NO_ARRAY_INDEX) {
        /* XXX: any chance of unifying this with the 'length' key handling? */

        /* E5 Section 15.4.5.1, step 4 */
        duk_uint32_t old_len;
        duk_harray *a;

        a = (duk_harray *) obj;
        DUK_HARRAY_ASSERT_VALID(a);

        old_len = a->length;

        if (arr_idx >= old_len) {
            DUK_DDD(DUK_DDDPRINT("defineProperty requires array length update "
                                 "(arr_idx=%ld, old_len=%ld)",
                                 (long) arr_idx, (long) old_len));

            if (DUK_HARRAY_LENGTH_NONWRITABLE(a) && !force_flag) {
                /* Array .length is always non-configurable, so
                 * if it's also non-writable, don't allow a value
                 * write.  With force flag allow writing.
                 */
                goto fail_not_configurable;
            }

            /* actual update happens once write has been completed without
             * error below.
             */
            DUK_ASSERT(arr_idx != 0xffffffffUL);
            arridx_new_array_length = arr_idx + 1;
        } else {
            DUK_DDD(DUK_DDDPRINT("defineProperty does not require length update "
                                 "(arr_idx=%ld, old_len=%ld) -> standard behavior",
                                 (long) arr_idx, (long) old_len));
        }
    }
 skip_array_exotic:

    /* XXX: There is currently no support for writing buffer object
     * indexed elements here.  Attempt to do so will succeed and
     * write a concrete property into the buffer object.  This should
     * be fixed at some point but because buffers are a custom feature
     * anyway, this is relatively unimportant.
     */

    /*
     *  Actual Object.defineProperty() default algorithm.
     */

    /*
     *  First check whether property exists; if not, simple case.  This covers
     *  steps 1-4.
     */

    if (!duk__get_own_propdesc_raw(thr, obj, key, arr_idx, &curr, DUK_GETDESC_FLAG_PUSH_VALUE)) {
        DUK_DDD(DUK_DDDPRINT("property does not exist"));

        if (!DUK_HOBJECT_HAS_EXTENSIBLE(obj) && !force_flag) {
            goto fail_not_extensible;
        }

#if defined(DUK_USE_ROM_OBJECTS)
        /* ROM objects are never extensible but force flag may
         * allow us to come here anyway.
         */
        DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj) || !DUK_HOBJECT_HAS_EXTENSIBLE(obj));
        if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)) {
            DUK_D(DUK_DPRINT("attempt to define property on a read-only target object"));
            goto fail_not_configurable;
        }
#endif

        /* XXX: share final setting code for value and flags?  difficult because
         * refcount code is different.  Share entry allocation?  But can't allocate
         * until array index checked.
         */

        /* steps 4.a and 4.b are tricky */
        if (has_set || has_get) {
            duk_int_t e_idx;

            DUK_DDD(DUK_DDDPRINT("create new accessor property"));

            DUK_ASSERT(has_set || set == NULL);
            DUK_ASSERT(has_get || get == NULL);
            DUK_ASSERT(!has_value);
            DUK_ASSERT(!has_writable);

            new_flags = DUK_PROPDESC_FLAG_ACCESSOR;  /* defaults, E5 Section 8.6.1, Table 7 */
            if (has_enumerable && is_enumerable) {
                new_flags |= DUK_PROPDESC_FLAG_ENUMERABLE;
            }
            if (has_configurable && is_configurable) {
                new_flags |= DUK_PROPDESC_FLAG_CONFIGURABLE;
            }

            if (arr_idx != DUK__NO_ARRAY_INDEX && DUK_HOBJECT_HAS_ARRAY_PART(obj)) {
                DUK_DDD(DUK_DDDPRINT("accessor cannot go to array part, abandon array"));
                duk__abandon_array_part(thr, obj);
            }

            /* write to entry part */
            e_idx = duk__hobject_alloc_entry_checked(thr, obj, key);
            DUK_ASSERT(e_idx >= 0);

            DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, obj, e_idx, get);
            DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, obj, e_idx, set);
            DUK_HOBJECT_INCREF_ALLOWNULL(thr, get);
            DUK_HOBJECT_INCREF_ALLOWNULL(thr, set);

            DUK_HOBJECT_E_SET_FLAGS(thr->heap, obj, e_idx, new_flags);
            goto success_exotics;
        } else {
            duk_int_t e_idx;
            duk_tval *tv2;

            DUK_DDD(DUK_DDDPRINT("create new data property"));

            DUK_ASSERT(!has_set);
            DUK_ASSERT(!has_get);

            new_flags = 0;  /* defaults, E5 Section 8.6.1, Table 7 */
            if (has_writable && is_writable) {
                new_flags |= DUK_PROPDESC_FLAG_WRITABLE;
            }
            if (has_enumerable && is_enumerable) {
                new_flags |= DUK_PROPDESC_FLAG_ENUMERABLE;
            }
            if (has_configurable && is_configurable) {
                new_flags |= DUK_PROPDESC_FLAG_CONFIGURABLE;
            }
            if (has_value) {
                duk_tval *tv_tmp = duk_require_tval(thr, idx_value);
                DUK_TVAL_SET_TVAL(&tv, tv_tmp);
            } else {
                DUK_TVAL_SET_UNDEFINED(&tv);  /* default value */
            }

            if (arr_idx != DUK__NO_ARRAY_INDEX && DUK_HOBJECT_HAS_ARRAY_PART(obj)) {
                if (new_flags == DUK_PROPDESC_FLAGS_WEC) {
                    DUK_DDD(DUK_DDDPRINT("new data property attributes match array defaults, attempt to write to array part"));
                    tv2 = duk__obtain_arridx_slot(thr, arr_idx, obj);
                    if (tv2 == NULL) {
                        DUK_DDD(DUK_DDDPRINT("failed writing to array part, abandoned array"));
                    } else {
                        DUK_DDD(DUK_DDDPRINT("success in writing to array part"));
                        DUK_ASSERT(DUK_HOBJECT_HAS_ARRAY_PART(obj));
                        DUK_ASSERT(DUK_TVAL_IS_UNUSED(tv2));
                        DUK_TVAL_SET_TVAL(tv2, &tv);
                        DUK_TVAL_INCREF(thr, tv2);
                        goto success_exotics;
                    }
                } else {
                    DUK_DDD(DUK_DDDPRINT("new data property cannot go to array part, abandon array"));
                    duk__abandon_array_part(thr, obj);
                }
                /* fall through */
            }

            /* write to entry part */
            e_idx = duk__hobject_alloc_entry_checked(thr, obj, key);
            DUK_ASSERT(e_idx >= 0);
            tv2 = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, e_idx);
            DUK_TVAL_SET_TVAL(tv2, &tv);
            DUK_TVAL_INCREF(thr, tv2);

            DUK_HOBJECT_E_SET_FLAGS(thr->heap, obj, e_idx, new_flags);
            goto success_exotics;
        }
        DUK_UNREACHABLE();
    }

    /* we currently assume virtual properties are not configurable (as none of them are) */
    DUK_ASSERT((curr.e_idx >= 0 || curr.a_idx >= 0) || !(curr.flags & DUK_PROPDESC_FLAG_CONFIGURABLE));

    /* [obj key desc value get set curr_value] */

    /*
     *  Property already exists.  Steps 5-6 detect whether any changes need
     *  to be made.
     */

    if (has_enumerable) {
        if (is_enumerable) {
            if (!(curr.flags & DUK_PROPDESC_FLAG_ENUMERABLE)) {
                goto need_check;
            }
        } else {
            if (curr.flags & DUK_PROPDESC_FLAG_ENUMERABLE) {
                goto need_check;
            }
        }
    }
    if (has_configurable) {
        if (is_configurable) {
            if (!(curr.flags & DUK_PROPDESC_FLAG_CONFIGURABLE)) {
                goto need_check;
            }
        } else {
            if (curr.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) {
                goto need_check;
            }
        }
    }
    if (has_value) {
        duk_tval *tmp1;
        duk_tval *tmp2;

        /* attempt to change from accessor to data property */
        if (curr.flags & DUK_PROPDESC_FLAG_ACCESSOR) {
            goto need_check;
        }

        tmp1 = duk_require_tval(thr, -1);         /* curr value */
        tmp2 = duk_require_tval(thr, idx_value);  /* new value */
        if (!duk_js_samevalue(tmp1, tmp2)) {
            goto need_check;
        }
    }
    if (has_writable) {
        /* attempt to change from accessor to data property */
        if (curr.flags & DUK_PROPDESC_FLAG_ACCESSOR) {
            goto need_check;
        }

        if (is_writable) {
            if (!(curr.flags & DUK_PROPDESC_FLAG_WRITABLE)) {
                goto need_check;
            }
        } else {
            if (curr.flags & DUK_PROPDESC_FLAG_WRITABLE) {
                goto need_check;
            }
        }
    }
    if (has_set) {
        if (curr.flags & DUK_PROPDESC_FLAG_ACCESSOR) {
            if (set != curr.set) {
                goto need_check;
            }
        } else {
            goto need_check;
        }
    }
    if (has_get) {
        if (curr.flags & DUK_PROPDESC_FLAG_ACCESSOR) {
            if (get != curr.get) {
                goto need_check;
            }
        } else {
            goto need_check;
        }
    }

    /* property exists, either 'desc' is empty, or all values
     * match (SameValue)
     */
    goto success_no_exotics;

 need_check:

    /*
     *  Some change(s) need to be made.  Steps 7-11.
     */

    /* shared checks for all descriptor types */
    if (!(curr.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) && !force_flag) {
        if (has_configurable && is_configurable) {
            goto fail_not_configurable;
        }
        if (has_enumerable) {
            if (curr.flags & DUK_PROPDESC_FLAG_ENUMERABLE) {
                if (!is_enumerable) {
                    goto fail_not_configurable;
                }
            } else {
                if (is_enumerable) {
                    goto fail_not_configurable;
                }
            }
        }
    }

    /* Virtual properties don't have backing so they can't mostly be
     * edited.  Some virtual properties are, however, writable: for
     * example, virtual index properties of buffer objects and Array
     * instance .length.  These are not configurable so the checks
     * above mostly cover attempts to change them, except when the
     * duk_def_prop() call is used with DUK_DEFPROP_FORCE; even in
     * that case we can't forcibly change the property attributes
     * because they don't have concrete backing.
     */

    /* XXX: for ROM objects too it'd be best if value modify was
     * allowed if the value matches SameValue.
     */
    /* Reject attempt to change a read-only object. */
#if defined(DUK_USE_ROM_OBJECTS)
    if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)) {
        DUK_DD(DUK_DDPRINT("attempt to define property on read-only target object"));
        goto fail_not_configurable;
    }
#endif

    /* descriptor type specific checks */
    if (has_set || has_get) {
        /* IsAccessorDescriptor(desc) == true */
        DUK_ASSERT(!has_writable);
        DUK_ASSERT(!has_value);

        if (curr.flags & DUK_PROPDESC_FLAG_ACCESSOR) {
            /* curr and desc are accessors */
            if (!(curr.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) && !force_flag) {
                if (has_set && set != curr.set) {
                    goto fail_not_configurable;
                }
                if (has_get && get != curr.get) {
                    goto fail_not_configurable;
                }
            }
        } else {
            duk_bool_t rc;
            duk_tval *tv1;

            /* curr is data, desc is accessor */
            if (!(curr.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) && !force_flag) {
                goto fail_not_configurable;
            }

            DUK_DDD(DUK_DDDPRINT("convert property to accessor property"));
            if (curr.a_idx >= 0) {
                DUK_DDD(DUK_DDDPRINT("property to convert is stored in an array entry, abandon array and re-lookup"));
                duk__abandon_array_part(thr, obj);
                duk_pop_unsafe(thr);  /* remove old value */
                rc = duk__get_own_propdesc_raw(thr, obj, key, arr_idx, &curr, DUK_GETDESC_FLAG_PUSH_VALUE);
                DUK_UNREF(rc);
                DUK_ASSERT(rc != 0);
                DUK_ASSERT(curr.e_idx >= 0 && curr.a_idx < 0);
            }
            if (curr.e_idx < 0) {
                DUK_ASSERT(curr.a_idx < 0 && curr.e_idx < 0);
                goto fail_virtual;  /* safeguard for virtual property */
            }

            DUK_ASSERT(curr.e_idx >= 0);
            DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, curr.e_idx));

            tv1 = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, curr.e_idx);
            DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ(thr, tv1);  /* XXX: just decref */

            DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, obj, curr.e_idx, NULL);
            DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, obj, curr.e_idx, NULL);
            DUK_HOBJECT_E_SLOT_CLEAR_WRITABLE(thr->heap, obj, curr.e_idx);
            DUK_HOBJECT_E_SLOT_SET_ACCESSOR(thr->heap, obj, curr.e_idx);

            DUK_DDD(DUK_DDDPRINT("flags after data->accessor conversion: 0x%02lx",
                                 (unsigned long) DUK_HOBJECT_E_GET_FLAGS(thr->heap, obj, curr.e_idx)));
            /* Update curr.flags; faster than a re-lookup. */
            curr.flags &= ~DUK_PROPDESC_FLAG_WRITABLE;
            curr.flags |= DUK_PROPDESC_FLAG_ACCESSOR;
        }
    } else if (has_value || has_writable) {
        /* IsDataDescriptor(desc) == true */
        DUK_ASSERT(!has_set);
        DUK_ASSERT(!has_get);

        if (curr.flags & DUK_PROPDESC_FLAG_ACCESSOR) {
            duk_hobject *tmp;

            /* curr is accessor, desc is data */
            if (!(curr.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) && !force_flag) {
                goto fail_not_configurable;
            }

            /* curr is accessor -> cannot be in array part. */
            DUK_ASSERT(curr.a_idx < 0);
            if (curr.e_idx < 0) {
                goto fail_virtual;  /* safeguard; no virtual accessors now */
            }

            DUK_DDD(DUK_DDDPRINT("convert property to data property"));

            DUK_ASSERT(DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, curr.e_idx));
            tmp = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, obj, curr.e_idx);
            DUK_UNREF(tmp);
            DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, obj, curr.e_idx, NULL);
            DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, tmp);
            tmp = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, obj, curr.e_idx);
            DUK_UNREF(tmp);
            DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, obj, curr.e_idx, NULL);
            DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, tmp);

            DUK_TVAL_SET_UNDEFINED(DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, curr.e_idx));
            DUK_HOBJECT_E_SLOT_CLEAR_WRITABLE(thr->heap, obj, curr.e_idx);
            DUK_HOBJECT_E_SLOT_CLEAR_ACCESSOR(thr->heap, obj, curr.e_idx);

            DUK_DDD(DUK_DDDPRINT("flags after accessor->data conversion: 0x%02lx",
                                 (unsigned long) DUK_HOBJECT_E_GET_FLAGS(thr->heap, obj, curr.e_idx)));

            /* Update curr.flags; faster than a re-lookup. */
            curr.flags &= ~(DUK_PROPDESC_FLAG_WRITABLE | DUK_PROPDESC_FLAG_ACCESSOR);
        } else {
            /* curr and desc are data */
            if (!(curr.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) && !force_flag) {
                if (!(curr.flags & DUK_PROPDESC_FLAG_WRITABLE) && has_writable && is_writable) {
                    goto fail_not_configurable;
                }
                /* Note: changing from writable to non-writable is OK */
                if (!(curr.flags & DUK_PROPDESC_FLAG_WRITABLE) && has_value) {
                    duk_tval *tmp1 = duk_require_tval(thr, -1);         /* curr value */
                    duk_tval *tmp2 = duk_require_tval(thr, idx_value);  /* new value */
                    if (!duk_js_samevalue(tmp1, tmp2)) {
                        goto fail_not_configurable;
                    }
                }
            }
        }
    } else {
        /* IsGenericDescriptor(desc) == true; this means in practice that 'desc'
         * only has [[Enumerable]] or [[Configurable]] flag updates, which are
         * allowed at this point.
         */

        DUK_ASSERT(!has_value && !has_writable && !has_get && !has_set);
    }

    /*
     *  Start doing property attributes updates.  Steps 12-13.
     *
     *  Start by computing new attribute flags without writing yet.
     *  Property type conversion is done above if necessary.
     */

    new_flags = curr.flags;

    if (has_enumerable) {
        if (is_enumerable) {
            new_flags |= DUK_PROPDESC_FLAG_ENUMERABLE;
        } else {
            new_flags &= ~DUK_PROPDESC_FLAG_ENUMERABLE;
        }
    }
    if (has_configurable) {
        if (is_configurable) {
            new_flags |= DUK_PROPDESC_FLAG_CONFIGURABLE;
        } else {
            new_flags &= ~DUK_PROPDESC_FLAG_CONFIGURABLE;
        }
    }
    if (has_writable) {
        if (is_writable) {
            new_flags |= DUK_PROPDESC_FLAG_WRITABLE;
        } else {
            new_flags &= ~DUK_PROPDESC_FLAG_WRITABLE;
        }
    }

    /* XXX: write protect after flag? -> any chance of handling it here? */

    DUK_DDD(DUK_DDDPRINT("new flags that we want to write: 0x%02lx",
                         (unsigned long) new_flags));

    /*
     *  Check whether we need to abandon an array part (if it exists)
     */

    if (curr.a_idx >= 0) {
        duk_bool_t rc;

        DUK_ASSERT(curr.e_idx < 0);

        if (new_flags == DUK_PROPDESC_FLAGS_WEC) {
            duk_tval *tv1, *tv2;

            DUK_DDD(DUK_DDDPRINT("array index, new property attributes match array defaults, update in-place"));

            DUK_ASSERT(curr.flags == DUK_PROPDESC_FLAGS_WEC);  /* must have been, since in array part */
            DUK_ASSERT(!has_set);
            DUK_ASSERT(!has_get);
            DUK_ASSERT(idx_value >= 0);  /* must be: if attributes match and we get here the value must differ (otherwise no change) */

            tv2 = duk_require_tval(thr, idx_value);
            tv1 = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, curr.a_idx);
            DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv2);  /* side effects; may invalidate a_idx */
            goto success_exotics;
        }

        DUK_DDD(DUK_DDDPRINT("array index, new property attributes do not match array defaults, abandon array and re-lookup"));
        duk__abandon_array_part(thr, obj);
        duk_pop_unsafe(thr);  /* remove old value */
        rc = duk__get_own_propdesc_raw(thr, obj, key, arr_idx, &curr, DUK_GETDESC_FLAG_PUSH_VALUE);
        DUK_UNREF(rc);
        DUK_ASSERT(rc != 0);
        DUK_ASSERT(curr.e_idx >= 0 && curr.a_idx < 0);
    }

    DUK_DDD(DUK_DDDPRINT("updating existing property in entry part"));

    /* Array case is handled comprehensively above: either in entry
     * part or a virtual property.
     */
    DUK_ASSERT(curr.a_idx < 0);

    DUK_DDD(DUK_DDDPRINT("update existing property attributes"));
    if (curr.e_idx >= 0) {
        DUK_HOBJECT_E_SET_FLAGS(thr->heap, obj, curr.e_idx, new_flags);
    } else {
        /* For Array .length the only allowed transition is for .length
         * to become non-writable.
         */
        if (key == DUK_HTHREAD_STRING_LENGTH(thr) && DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)) {
            duk_harray *a;
            a = (duk_harray *) obj;
            DUK_DD(DUK_DDPRINT("Object.defineProperty() attribute update for duk_harray .length -> %02lx", (unsigned long) new_flags));
            DUK_HARRAY_ASSERT_VALID(a);
            if ((new_flags & DUK_PROPDESC_FLAGS_EC) != (curr.flags & DUK_PROPDESC_FLAGS_EC)) {
                DUK_D(DUK_DPRINT("Object.defineProperty() attempt to change virtual array .length enumerable or configurable attribute, fail"));
                goto fail_virtual;
            }
            if (new_flags & DUK_PROPDESC_FLAG_WRITABLE) {
                DUK_HARRAY_SET_LENGTH_WRITABLE(a);
            } else {
                DUK_HARRAY_SET_LENGTH_NONWRITABLE(a);
            }
        }
    }

    if (has_set) {
        duk_hobject *tmp;

        /* Virtual properties are non-configurable but with a 'force'
         * flag we might come here so check explicitly for virtual.
         */
        if (curr.e_idx < 0) {
            goto fail_virtual;
        }

        DUK_DDD(DUK_DDDPRINT("update existing property setter"));
        DUK_ASSERT(DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, curr.e_idx));

        tmp = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, obj, curr.e_idx);
        DUK_UNREF(tmp);
        DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, obj, curr.e_idx, set);
        DUK_HOBJECT_INCREF_ALLOWNULL(thr, set);
        DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp);  /* side effects; may invalidate e_idx */
    }
    if (has_get) {
        duk_hobject *tmp;

        if (curr.e_idx < 0) {
            goto fail_virtual;
        }

        DUK_DDD(DUK_DDDPRINT("update existing property getter"));
        DUK_ASSERT(DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, curr.e_idx));

        tmp = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, obj, curr.e_idx);
        DUK_UNREF(tmp);
        DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, obj, curr.e_idx, get);
        DUK_HOBJECT_INCREF_ALLOWNULL(thr, get);
        DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp);  /* side effects; may invalidate e_idx */
    }
    if (has_value) {
        duk_tval *tv1, *tv2;

        DUK_DDD(DUK_DDDPRINT("update existing property value"));

        if (curr.e_idx >= 0) {
            DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, curr.e_idx));
            tv2 = duk_require_tval(thr, idx_value);
            tv1 = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, curr.e_idx);
            DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv2);  /* side effects; may invalidate e_idx */
        } else {
            DUK_ASSERT(curr.a_idx < 0);  /* array part case handled comprehensively previously */

            DUK_DD(DUK_DDPRINT("Object.defineProperty(), value update for virtual property"));
            /* XXX: Uint8Array and other typed array virtual writes not currently
             * handled.
             */
            if (key == DUK_HTHREAD_STRING_LENGTH(thr) && DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)) {
                duk_harray *a;
                a = (duk_harray *) obj;
                DUK_DD(DUK_DDPRINT("Object.defineProperty() value update for duk_harray .length -> %ld", (long) arrlen_new_len));
                DUK_HARRAY_ASSERT_VALID(a);
                a->length = arrlen_new_len;
            } else {
                goto fail_virtual;  /* should not happen */
            }
        }
    }

    /*
     *  Standard algorithm succeeded without errors, check for exotic post-behaviors.
     *
     *  Arguments exotic behavior in E5 Section 10.6 occurs after the standard
     *  [[DefineOwnProperty]] has completed successfully.
     *
     *  Array exotic behavior in E5 Section 15.4.5.1 is implemented partly
     *  prior to the default [[DefineOwnProperty]], but:
     *    - for an array index key (e.g. "10") the final 'length' update occurs here
     *    - for 'length' key the element deletion and 'length' update occurs here
     */

 success_exotics:

    /* curr.a_idx or curr.e_idx may have been invalidated by side effects
     * above.
     */

    /* [obj key desc value get set curr_value] */

    if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)) {
        duk_harray *a;

        a = (duk_harray *) obj;
        DUK_HARRAY_ASSERT_VALID(a);

        if (arridx_new_array_length > 0) {
            /*
             *  Note: zero works as a "no update" marker because the new length
             *  can never be zero after a new property is written.
             */

            /* E5 Section 15.4.5.1, steps 4.e.i - 4.e.ii */

            DUK_DDD(DUK_DDDPRINT("defineProperty successful, pending array length update to: %ld",
                                 (long) arridx_new_array_length));

            a->length = arridx_new_array_length;
        }

        if (key == DUK_HTHREAD_STRING_LENGTH(thr) && arrlen_new_len < arrlen_old_len) {
            /*
             *  E5 Section 15.4.5.1, steps 3.k - 3.n.  The order at the end combines
             *  the error case 3.l.iii and the success case 3.m-3.n.
             */

            /* XXX: investigate whether write protect can be handled above, if we
             * just update length here while ignoring its protected status
             */

            duk_uint32_t result_len;
            duk_bool_t rc;

            DUK_DDD(DUK_DDDPRINT("defineProperty successful, key is 'length', exotic array behavior, "
                                 "doing array element deletion and length update"));

            rc = duk__handle_put_array_length_smaller(thr, obj, arrlen_old_len, arrlen_new_len, force_flag, &result_len);

            /* update length (curr points to length, and we assume it's still valid) */
            DUK_ASSERT(result_len >= arrlen_new_len && result_len <= arrlen_old_len);

            a->length = result_len;

            if (pending_write_protect) {
                DUK_DDD(DUK_DDDPRINT("setting array length non-writable (pending writability update)"));
                DUK_HARRAY_SET_LENGTH_NONWRITABLE(a);
            }

            /* XXX: shrink array allocation or entries compaction here? */
            if (!rc) {
                DUK_DD(DUK_DDPRINT("array length write only partially successful"));
                goto fail_not_configurable;
            }
        }
    } else if (arr_idx != DUK__NO_ARRAY_INDEX && DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj)) {
        duk_hobject *map;
        duk_hobject *varenv;

        DUK_ASSERT(arridx_new_array_length == 0);
        DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj));  /* traits are separate; in particular, arguments not an array */

        map = NULL;
        varenv = NULL;
        if (!duk__lookup_arguments_map(thr, obj, key, &curr, &map, &varenv)) {
            goto success_no_exotics;
        }
        DUK_ASSERT(map != NULL);
        DUK_ASSERT(varenv != NULL);

        /* [obj key desc value get set curr_value varname] */

        if (has_set || has_get) {
            /* = IsAccessorDescriptor(Desc) */
            DUK_DDD(DUK_DDDPRINT("defineProperty successful, key mapped to arguments 'map' "
                                 "changed to an accessor, delete arguments binding"));

            (void) duk_hobject_delprop_raw(thr, map, key, 0);  /* ignore result */
        } else {
            /* Note: this order matters (final value before deleting map entry must be done) */
            DUK_DDD(DUK_DDDPRINT("defineProperty successful, key mapped to arguments 'map', "
                                 "check for value update / binding deletion"));

            if (has_value) {
                duk_hstring *varname;

                DUK_DDD(DUK_DDDPRINT("defineProperty successful, key mapped to arguments 'map', "
                                     "update bound value (variable/argument)"));

                varname = duk_require_hstring(thr, -1);
                DUK_ASSERT(varname != NULL);

                DUK_DDD(DUK_DDDPRINT("arguments object automatic putvar for a bound variable; "
                                     "key=%!O, varname=%!O, value=%!T",
                                     (duk_heaphdr *) key,
                                     (duk_heaphdr *) varname,
                                     (duk_tval *) duk_require_tval(thr, idx_value)));

                /* strict flag for putvar comes from our caller (currently: fixed) */
                duk_js_putvar_envrec(thr, varenv, varname, duk_require_tval(thr, idx_value), 1 /*throw_flag*/);
            }
            if (has_writable && !is_writable) {
                DUK_DDD(DUK_DDDPRINT("defineProperty successful, key mapped to arguments 'map', "
                                     "changed to non-writable, delete arguments binding"));

                (void) duk_hobject_delprop_raw(thr, map, key, 0);  /* ignore result */
            }
        }

        /* 'varname' is in stack in this else branch, leaving an unbalanced stack below,
         * but this doesn't matter now.
         */
    }

 success_no_exotics:
    /* Some code paths use NORZ macros for simplicity, ensure refzero
     * handling is completed.
     */
    DUK_REFZERO_CHECK_SLOW(thr);
    return 1;

 fail_not_extensible:
    if (throw_flag) {
        DUK_ERROR_TYPE(thr, DUK_STR_NOT_EXTENSIBLE);
        DUK_WO_NORETURN(return 0;);
    }
    return 0;

 fail_virtual:  /* just use the same "not configurable" error message" */
 fail_not_configurable:
    if (throw_flag) {
        DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONFIGURABLE);
        DUK_WO_NORETURN(return 0;);
    }
    return 0;
}

/*
 *  Object.prototype.hasOwnProperty() and Object.prototype.propertyIsEnumerable().
 */

DUK_INTERNAL duk_bool_t duk_hobject_object_ownprop_helper(duk_hthread *thr, duk_small_uint_t required_desc_flags) {
    duk_hstring *h_v;
    duk_hobject *h_obj;
    duk_propdesc desc;
    duk_bool_t ret;

    /* coercion order matters */
    h_v = duk_to_hstring_acceptsymbol(thr, 0);
    DUK_ASSERT(h_v != NULL);

    h_obj = duk_push_this_coercible_to_object(thr);
    DUK_ASSERT(h_obj != NULL);

    ret = duk_hobject_get_own_propdesc(thr, h_obj, h_v, &desc, 0 /*flags*/);  /* don't push value */

    duk_push_boolean(thr, ret && ((desc.flags & required_desc_flags) == required_desc_flags));
    return 1;
}

/*
 *  Object.seal() and Object.freeze()  (E5 Sections 15.2.3.8 and 15.2.3.9)
 *
 *  Since the algorithms are similar, a helper provides both functions.
 *  Freezing is essentially sealing + making plain properties non-writable.
 *
 *  Note: virtual (non-concrete) properties which are non-configurable but
 *  writable would pose some problems, but such properties do not currently
 *  exist (all virtual properties are non-configurable and non-writable).
 *  If they did exist, the non-configurability does NOT prevent them from
 *  becoming non-writable.  However, this change should be recorded somehow
 *  so that it would turn up (e.g. when getting the property descriptor),
 *  requiring some additional flags in the object.
 */

DUK_INTERNAL void duk_hobject_object_seal_freeze_helper(duk_hthread *thr, duk_hobject *obj, duk_bool_t is_freeze) {
    duk_uint_fast32_t i;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT(obj != NULL);

    DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);

#if defined(DUK_USE_ROM_OBJECTS)
    if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)) {
        DUK_DD(DUK_DDPRINT("attempt to seal/freeze a readonly object, reject"));
        DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONFIGURABLE);
        DUK_WO_NORETURN(return;);
    }
#endif

    /*
     *  Abandon array part because all properties must become non-configurable.
     *  Note that this is now done regardless of whether this is always the case
     *  (skips check, but performance problem if caller would do this many times
     *  for the same object; not likely).
     */

    duk__abandon_array_part(thr, obj);
    DUK_ASSERT(DUK_HOBJECT_GET_ASIZE(obj) == 0);

    for (i = 0; i < DUK_HOBJECT_GET_ENEXT(obj); i++) {
        duk_uint8_t *fp;

        /* since duk__abandon_array_part() causes a resize, there should be no gaps in keys */
        DUK_ASSERT(DUK_HOBJECT_E_GET_KEY(thr->heap, obj, i) != NULL);

        /* avoid multiple computations of flags address; bypasses macros */
        fp = DUK_HOBJECT_E_GET_FLAGS_PTR(thr->heap, obj, i);
        if (is_freeze && !((*fp) & DUK_PROPDESC_FLAG_ACCESSOR)) {
            *fp &= ~(DUK_PROPDESC_FLAG_WRITABLE | DUK_PROPDESC_FLAG_CONFIGURABLE);
        } else {
            *fp &= ~DUK_PROPDESC_FLAG_CONFIGURABLE;
        }
    }

    DUK_HOBJECT_CLEAR_EXTENSIBLE(obj);

    /* no need to compact since we already did that in duk__abandon_array_part()
     * (regardless of whether an array part existed or not.
     */

    return;
}

/*
 *  Object.isSealed() and Object.isFrozen()  (E5 Sections 15.2.3.11, 15.2.3.13)
 *
 *  Since the algorithms are similar, a helper provides both functions.
 *  Freezing is essentially sealing + making plain properties non-writable.
 *
 *  Note: all virtual (non-concrete) properties are currently non-configurable
 *  and non-writable (and there are no accessor virtual properties), so they don't
 *  need to be considered here now.
 */

DUK_INTERNAL duk_bool_t duk_hobject_object_is_sealed_frozen_helper(duk_hthread *thr, duk_hobject *obj, duk_bool_t is_frozen) {
    duk_uint_fast32_t i;

    DUK_ASSERT(obj != NULL);
    DUK_UNREF(thr);

    /* Note: no allocation pressure, no need to check refcounts etc */

    /* must not be extensible */
    if (DUK_HOBJECT_HAS_EXTENSIBLE(obj)) {
        return 0;
    }

    /* all virtual properties are non-configurable and non-writable */

    /* entry part must not contain any configurable properties, or
     * writable properties (if is_frozen).
     */
    for (i = 0; i < DUK_HOBJECT_GET_ENEXT(obj); i++) {
        duk_small_uint_t flags;

        if (!DUK_HOBJECT_E_GET_KEY(thr->heap, obj, i)) {
            continue;
        }

        /* avoid multiple computations of flags address; bypasses macros */
        flags = (duk_small_uint_t) DUK_HOBJECT_E_GET_FLAGS(thr->heap, obj, i);

        if (flags & DUK_PROPDESC_FLAG_CONFIGURABLE) {
            return 0;
        }
        if (is_frozen &&
            !(flags & DUK_PROPDESC_FLAG_ACCESSOR) &&
            (flags & DUK_PROPDESC_FLAG_WRITABLE)) {
            return 0;
        }
    }

    /* array part must not contain any non-unused properties, as they would
     * be configurable and writable.
     */
    for (i = 0; i < DUK_HOBJECT_GET_ASIZE(obj); i++) {
        duk_tval *tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, i);
        if (!DUK_TVAL_IS_UNUSED(tv)) {
            return 0;
        }
    }

    return 1;
}

/*
 *  Object.preventExtensions() and Object.isExtensible()  (E5 Sections 15.2.3.10, 15.2.3.13)
 *
 *  Not needed, implemented by macros DUK_HOBJECT_{HAS,CLEAR,SET}_EXTENSIBLE
 *  and the Object built-in bindings.
 */

/* automatic undefs */
#undef DUK__HASH_DELETED
#undef DUK__HASH_UNUSED
#undef DUK__NO_ARRAY_INDEX
#undef DUK__VALSTACK_PROXY_LOOKUP
#undef DUK__VALSTACK_SPACE
//#line 1 "duk_hstring_assert.c"
/*
 *  duk_hstring assertion helpers.
 */

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

#if defined(DUK_USE_ASSERTIONS)

DUK_INTERNAL void duk_hstring_assert_valid(duk_hstring *h) {
    DUK_ASSERT(h != NULL);
}

#endif  /* DUK_USE_ASSERTIONS */
//#line 1 "duk_hstring_misc.c"
/*
 *  Misc support functions
 */

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

/*
 *  duk_hstring charCodeAt, with and without surrogate awareness
 */

DUK_INTERNAL duk_ucodepoint_t duk_hstring_char_code_at_raw(duk_hthread *thr, duk_hstring *h, duk_uint_t pos, duk_bool_t surrogate_aware) {
    duk_uint32_t boff;
    const duk_uint8_t *p, *p_start, *p_end;
    duk_ucodepoint_t cp1;
    duk_ucodepoint_t cp2;

    /* Caller must check character offset to be inside the string. */
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(h != NULL);
    DUK_ASSERT_DISABLE(pos >= 0);  /* unsigned */
    DUK_ASSERT(pos < (duk_uint_t) DUK_HSTRING_GET_CHARLEN(h));

    boff = (duk_uint32_t) duk_heap_strcache_offset_char2byte(thr, h, (duk_uint32_t) pos);
    DUK_DDD(DUK_DDDPRINT("charCodeAt: pos=%ld -> boff=%ld, str=%!O",
                         (long) pos, (long) boff, (duk_heaphdr *) h));
    DUK_ASSERT_DISABLE(boff >= 0);
    DUK_ASSERT(boff < DUK_HSTRING_GET_BYTELEN(h));

    p_start = DUK_HSTRING_GET_DATA(h);
    p_end = p_start + DUK_HSTRING_GET_BYTELEN(h);
    p = p_start + boff;
    DUK_DDD(DUK_DDDPRINT("p_start=%p, p_end=%p, p=%p",
                         (const void *) p_start, (const void *) p_end,
                         (const void *) p));

    /* For invalid UTF-8 (never happens for standard ECMAScript strings)
     * return U+FFFD replacement character.
     */
    if (duk_unicode_decode_xutf8(thr, &p, p_start, p_end, &cp1)) {
        if (surrogate_aware && cp1 >= 0xd800UL && cp1 <= 0xdbffUL) {
            /* The decode helper is memory safe even if 'cp1' was
             * decoded at the end of the string and 'p' is no longer
             * within string memory range.
             */
            cp2 = 0;  /* If call fails, this is left untouched and won't match cp2 check. */
            (void) duk_unicode_decode_xutf8(thr, &p, p_start, p_end, &cp2);
            if (cp2 >= 0xdc00UL && cp2 <= 0xdfffUL) {
                cp1 = (duk_ucodepoint_t) (((cp1 - 0xd800UL) << 10) + (cp2 - 0xdc00UL) + 0x10000UL);
            }
        }
    } else {
        cp1 = DUK_UNICODE_CP_REPLACEMENT_CHARACTER;
    }

    return cp1;
}

/*
 *  duk_hstring charlen, when lazy charlen disabled
 */

#if !defined(DUK_USE_HSTRING_LAZY_CLEN)
#if !defined(DUK_USE_HSTRING_CLEN)
#error non-lazy duk_hstring charlen but DUK_USE_HSTRING_CLEN not set
#endif
DUK_INTERNAL void duk_hstring_init_charlen(duk_hstring *h) {
    duk_uint32_t clen;

    DUK_ASSERT(h != NULL);
    DUK_ASSERT(!DUK_HSTRING_HAS_ASCII(h));
    DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) h));

    clen = duk_unicode_unvalidated_utf8_length(DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h));
#if defined(DUK_USE_STRLEN16)
    DUK_ASSERT(clen <= 0xffffUL);  /* Bytelength checked during interning. */
    h->clen16 = (duk_uint16_t) clen;
#else
    h->clen = (duk_uint32_t) clen;
#endif
    if (DUK_LIKELY(clen == DUK_HSTRING_GET_BYTELEN(h))) {
        DUK_HSTRING_SET_ASCII(h);
    }
}

DUK_INTERNAL DUK_HOT duk_size_t duk_hstring_get_charlen(duk_hstring *h) {
#if defined(DUK_USE_STRLEN16)
    return h->clen16;
#else
    return h->clen;
#endif
}
#endif  /* !DUK_USE_HSTRING_LAZY_CLEN */

/*
 *  duk_hstring charlen, when lazy charlen enabled
 */

#if defined(DUK_USE_HSTRING_LAZY_CLEN)
#if defined(DUK_USE_HSTRING_CLEN)
DUK_LOCAL DUK_COLD duk_size_t duk__hstring_get_charlen_slowpath(duk_hstring *h) {
    duk_size_t res;

    DUK_ASSERT(h->clen == 0);  /* Checked by caller. */

#if defined(DUK_USE_ROM_STRINGS)
    /* ROM strings have precomputed clen, but if the computed clen is zero
     * we can still come here and can't write anything.
     */
    if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) h)) {
        return 0;
    }
#endif

    res = duk_unicode_unvalidated_utf8_length(DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h));
#if defined(DUK_USE_STRLEN16)
    DUK_ASSERT(res <= 0xffffUL);  /* Bytelength checked during interning. */
    h->clen16 = (duk_uint16_t) res;
#else
    h->clen = (duk_uint32_t) res;
#endif
    if (DUK_LIKELY(res == DUK_HSTRING_GET_BYTELEN(h))) {
        DUK_HSTRING_SET_ASCII(h);
    }
    return res;
}
#else  /* DUK_USE_HSTRING_CLEN */
DUK_LOCAL duk_size_t duk__hstring_get_charlen_slowpath(duk_hstring *h) {
    if (DUK_LIKELY(DUK_HSTRING_HAS_ASCII(h))) {
        /* Most practical strings will go here. */
        return DUK_HSTRING_GET_BYTELEN(h);
    } else {
        /* ASCII flag is lazy, so set it here. */
        duk_size_t res;

        /* XXX: here we could use the strcache to speed up the
         * computation (matters for 'i < str.length' loops).
         */

        res = duk_unicode_unvalidated_utf8_length(DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h));

#if defined(DUK_USE_ROM_STRINGS)
        if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) h)) {
            /* For ROM strings, can't write anything; ASCII flag
             * is preset so we don't need to update it.
             */
            return res;
        }
#endif
        if (DUK_LIKELY(res == DUK_HSTRING_GET_BYTELEN(h))) {
            DUK_HSTRING_SET_ASCII(h);
        }
        return res;
    }
}
#endif  /* DUK_USE_HSTRING_CLEN */

#if defined(DUK_USE_HSTRING_CLEN)
DUK_INTERNAL DUK_HOT duk_size_t duk_hstring_get_charlen(duk_hstring *h) {
#if defined(DUK_USE_STRLEN16)
    if (DUK_LIKELY(h->clen16 != 0)) {
        return h->clen16;
    }
#else
    if (DUK_LIKELY(h->clen != 0)) {
        return h->clen;
    }
#endif
    return duk__hstring_get_charlen_slowpath(h);
}
#else  /* DUK_USE_HSTRING_CLEN */
DUK_INTERNAL DUK_HOT duk_size_t duk_hstring_get_charlen(duk_hstring *h) {
    /* Always use slow path. */
    return duk__hstring_get_charlen_slowpath(h);
}
#endif  /* DUK_USE_HSTRING_CLEN */
#endif  /* DUK_USE_HSTRING_LAZY_CLEN */

/*
 *  Compare duk_hstring to an ASCII cstring.
 */

DUK_INTERNAL duk_bool_t duk_hstring_equals_ascii_cstring(duk_hstring *h, const char *cstr) {
    duk_size_t len;

    DUK_ASSERT(h != NULL);
    DUK_ASSERT(cstr != NULL);

    len = DUK_STRLEN(cstr);
    if (len != DUK_HSTRING_GET_BYTELEN(h)) {
        return 0;
    }
    if (duk_memcmp((const void *) cstr, (const void *) DUK_HSTRING_GET_DATA(h), len) == 0) {
        return 1;
    }
    return 0;
}
//#line 1 "duk_hthread_alloc.c"
/*
 *  duk_hthread allocation and freeing.
 */

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

/*
 *  Allocate initial stacks for a thread.  Note that 'thr' must be reachable
 *  as a garbage collection may be triggered by the allocation attempts.
 *  Returns zero (without leaking memory) if init fails.
 */

DUK_INTERNAL duk_bool_t duk_hthread_init_stacks(duk_heap *heap, duk_hthread *thr) {
    duk_size_t alloc_size;
    duk_size_t i;

    DUK_ASSERT(heap != NULL);
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->valstack == NULL);
    DUK_ASSERT(thr->valstack_end == NULL);
    DUK_ASSERT(thr->valstack_alloc_end == NULL);
    DUK_ASSERT(thr->valstack_bottom == NULL);
    DUK_ASSERT(thr->valstack_top == NULL);
    DUK_ASSERT(thr->callstack_curr == NULL);

    /* valstack */
    DUK_ASSERT(DUK_VALSTACK_API_ENTRY_MINIMUM <= DUK_VALSTACK_INITIAL_SIZE);
    alloc_size = sizeof(duk_tval) * DUK_VALSTACK_INITIAL_SIZE;
    thr->valstack = (duk_tval *) DUK_ALLOC(heap, alloc_size);
    if (!thr->valstack) {
        goto fail;
    }
    duk_memzero(thr->valstack, alloc_size);
    thr->valstack_end = thr->valstack + DUK_VALSTACK_API_ENTRY_MINIMUM;
    thr->valstack_alloc_end = thr->valstack + DUK_VALSTACK_INITIAL_SIZE;
    thr->valstack_bottom = thr->valstack;
    thr->valstack_top = thr->valstack;

    for (i = 0; i < DUK_VALSTACK_INITIAL_SIZE; i++) {
        DUK_TVAL_SET_UNDEFINED(&thr->valstack[i]);
    }

    return 1;

 fail:
    DUK_FREE(heap, thr->valstack);
    DUK_ASSERT(thr->callstack_curr == NULL);

    thr->valstack = NULL;
    return 0;
}

/* For indirect allocs. */

DUK_INTERNAL void *duk_hthread_get_valstack_ptr(duk_heap *heap, void *ud) {
    duk_hthread *thr = (duk_hthread *) ud;
    DUK_UNREF(heap);
    return (void *) thr->valstack;
}
//#line 1 "duk_hthread_builtins.c"
/*
 *  Initialize built-in objects.  Current thread must have a valstack
 *  and initialization errors may longjmp, so a setjmp() catch point
 *  must exist.
 */

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

/*
 *  Encoding constants, must match genbuiltins.py
 */

#define DUK__PROP_FLAGS_BITS             3
#define DUK__LENGTH_PROP_BITS            3
#define DUK__NARGS_BITS                  3
#define DUK__PROP_TYPE_BITS              3

#define DUK__NARGS_VARARGS_MARKER        0x07

#define DUK__PROP_TYPE_DOUBLE            0
#define DUK__PROP_TYPE_STRING            1
#define DUK__PROP_TYPE_STRIDX            2
#define DUK__PROP_TYPE_BUILTIN           3
#define DUK__PROP_TYPE_UNDEFINED         4
#define DUK__PROP_TYPE_BOOLEAN_TRUE      5
#define DUK__PROP_TYPE_BOOLEAN_FALSE     6
#define DUK__PROP_TYPE_ACCESSOR          7

/*
 *  Create built-in objects by parsing an init bitstream generated
 *  by genbuiltins.py.
 */

#if defined(DUK_USE_ROM_OBJECTS)
#if defined(DUK_USE_ROM_GLOBAL_CLONE) || defined(DUK_USE_ROM_GLOBAL_INHERIT)
DUK_LOCAL void duk__duplicate_ram_global_object(duk_hthread *thr) {
    duk_hobject *h_global;
#if defined(DUK_USE_ROM_GLOBAL_CLONE)
    duk_hobject *h_oldglobal;
    duk_uint8_t *props;
    duk_size_t alloc_size;
#endif
    duk_hobject *h_objenv;

    /* XXX: refactor into internal helper, duk_clone_hobject() */

#if defined(DUK_USE_ROM_GLOBAL_INHERIT)
    /* Inherit from ROM-based global object: less RAM usage, less transparent. */
    h_global = duk_push_object_helper(thr,
                                      DUK_HOBJECT_FLAG_EXTENSIBLE |
                                      DUK_HOBJECT_FLAG_FASTREFS |
                                      DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_GLOBAL),
                                      DUK_BIDX_GLOBAL);
    DUK_ASSERT(h_global != NULL);
#elif defined(DUK_USE_ROM_GLOBAL_CLONE)
    /* Clone the properties of the ROM-based global object to create a
     * fully RAM-based global object.  Uses more memory than the inherit
     * model but more compliant.
     */
    h_global = duk_push_object_helper(thr,
                                      DUK_HOBJECT_FLAG_EXTENSIBLE |
                                      DUK_HOBJECT_FLAG_FASTREFS |
                                      DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_GLOBAL),
                                      DUK_BIDX_OBJECT_PROTOTYPE);
    DUK_ASSERT(h_global != NULL);
    h_oldglobal = thr->builtins[DUK_BIDX_GLOBAL];
    DUK_ASSERT(h_oldglobal != NULL);

    /* Copy the property table verbatim; this handles attributes etc.
     * For ROM objects it's not necessary (or possible) to update
     * refcounts so leave them as is.
     */
    alloc_size = DUK_HOBJECT_P_ALLOC_SIZE(h_oldglobal);
    DUK_ASSERT(alloc_size > 0);
    props = DUK_ALLOC_CHECKED(thr, alloc_size);
    DUK_ASSERT(props != NULL);
    DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, h_oldglobal) != NULL);
    duk_memcpy((void *) props, (const void *) DUK_HOBJECT_GET_PROPS(thr->heap, h_oldglobal), alloc_size);

    /* XXX: keep property attributes or tweak them here?
     * Properties will now be non-configurable even when they're
     * normally configurable for the global object.
     */

    DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, h_global) == NULL);
    DUK_HOBJECT_SET_PROPS(thr->heap, h_global, props);
    DUK_HOBJECT_SET_ESIZE(h_global, DUK_HOBJECT_GET_ESIZE(h_oldglobal));
    DUK_HOBJECT_SET_ENEXT(h_global, DUK_HOBJECT_GET_ENEXT(h_oldglobal));
    DUK_HOBJECT_SET_ASIZE(h_global, DUK_HOBJECT_GET_ASIZE(h_oldglobal));
    DUK_HOBJECT_SET_HSIZE(h_global, DUK_HOBJECT_GET_HSIZE(h_oldglobal));
#else
#error internal error in config defines
#endif

    duk_hobject_compact_props(thr, h_global);
    DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL);
    DUK_ASSERT(!DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE((duk_heaphdr *) thr->builtins[DUK_BIDX_GLOBAL]));  /* no need to decref: ROM object */
    thr->builtins[DUK_BIDX_GLOBAL] = h_global;
    DUK_HOBJECT_INCREF(thr, h_global);
    DUK_D(DUK_DPRINT("duplicated global object: %!O", h_global));

    /* Create a fresh object environment for the global scope.  This is
     * needed so that the global scope points to the newly created RAM-based
     * global object.
     */
    h_objenv = (duk_hobject *) duk_hobjenv_alloc(thr,
                                                 DUK_HOBJECT_FLAG_EXTENSIBLE |
                                                 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV));
    DUK_ASSERT(h_objenv != NULL);
    DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_objenv) == NULL);
    duk_push_hobject(thr, h_objenv);

    DUK_ASSERT(h_global != NULL);
    ((duk_hobjenv *) h_objenv)->target = h_global;
    DUK_HOBJECT_INCREF(thr, h_global);
    DUK_ASSERT(((duk_hobjenv *) h_objenv)->has_this == 0);

    DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL_ENV] != NULL);
    DUK_ASSERT(!DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE((duk_heaphdr *) thr->builtins[DUK_BIDX_GLOBAL_ENV]));  /* no need to decref: ROM object */
    thr->builtins[DUK_BIDX_GLOBAL_ENV] = h_objenv;
    DUK_HOBJECT_INCREF(thr, h_objenv);
    DUK_D(DUK_DPRINT("duplicated global env: %!O", h_objenv));

    DUK_HOBJENV_ASSERT_VALID((duk_hobjenv *) h_objenv);

    duk_pop_2(thr);  /* Pop global object and global env. */
}
#endif  /* DUK_USE_ROM_GLOBAL_CLONE || DUK_USE_ROM_GLOBAL_INHERIT */

DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) {
    /* Setup builtins from ROM objects.  All heaps/threads will share
     * the same readonly objects.
     */
    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) */
            (void) duk_push_c_function_builtin(thr, c_func, c_nargs);
            h = duk_known_hobject(thr, -1);

            /* Currently all built-in native functions are strict.
             * duk_push_c_function() now sets strict flag, so
             * assert for it.
             */
            DUK_ASSERT(DUK_HOBJECT_HAS_STRICT(h));

            /* XXX: function properties */

            duk__push_stridx_or_string(thr, bd);
#if defined(DUK_USE_FUNC_NAME_PROPERTY)
            duk_xdef_prop_stridx_short(thr,
                                       -2,
                                       DUK_STRIDX_NAME,
                                       DUK_PROPDESC_FLAGS_C);
#else
            duk_pop(thr);  /* Not very ideal but good enough for now. */
#endif

            /* Almost all global level Function objects are constructable
             * but not all: Function.prototype is a non-constructable,
             * callable Function.
             */
            if (duk_bd_decode_flag(bd)) {
                DUK_ASSERT(DUK_HOBJECT_HAS_CONSTRUCTABLE(h));
            } else {
                DUK_HOBJECT_CLEAR_CONSTRUCTABLE(h);
            }

            /* Cast converts magic to 16-bit signed value */
            magic = (duk_int16_t) duk_bd_decode_varuint(bd);
            ((duk_hnatfunc *) h)->magic = magic;
        } else if (class_num == DUK_HOBJECT_CLASS_ARRAY) {
            duk_push_array(thr);
        } else if (class_num == DUK_HOBJECT_CLASS_OBJENV) {
            duk_hobjenv *env;
            duk_hobject *global;

            DUK_ASSERT(i == DUK_BIDX_GLOBAL_ENV);
            DUK_ASSERT(DUK_BIDX_GLOBAL_ENV > DUK_BIDX_GLOBAL);

            env = duk_hobjenv_alloc(thr,
                                            DUK_HOBJECT_FLAG_EXTENSIBLE |
                                            DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV));
            DUK_ASSERT(env->target == NULL);
            duk_push_hobject(thr, (duk_hobject *) env);

            global = duk_known_hobject(thr, DUK_BIDX_GLOBAL);
            DUK_ASSERT(global != NULL);
            env->target = global;
            DUK_HOBJECT_INCREF(thr, global);
            DUK_ASSERT(env->has_this == 0);

            DUK_HOBJENV_ASSERT_VALID(env);
        } else {
            DUK_ASSERT(class_num != DUK_HOBJECT_CLASS_DECENV);

            (void) duk_push_object_helper(thr,
                                          DUK_HOBJECT_FLAG_FASTREFS |
                                          DUK_HOBJECT_FLAG_EXTENSIBLE,
                                          -1);  /* no prototype or class yet */

        }

        h = duk_known_hobject(thr, -1);
        DUK_HOBJECT_SET_CLASS_NUMBER(h, class_num);

        if (i < DUK_NUM_BUILTINS) {
            thr->builtins[i] = h;
            DUK_HOBJECT_INCREF(thr, &h->hdr);
        }

        if (len >= 0) {
            /* In ES2015+ built-in function object .length property
             * has property attributes C (configurable only):
             * http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-standard-built-in-objects
             *
             * Array.prototype remains an Array instance in ES2015+
             * and its length has attributes W (writable only).
             * Because .length is now virtual for duk_harray, it is
             * not encoded explicitly in init data.
             */

            DUK_ASSERT(class_num != DUK_HOBJECT_CLASS_ARRAY);  /* .length is virtual */
            duk_push_int(thr, len);
            duk_xdef_prop_stridx_short(thr,
                                       -2,
                                       DUK_STRIDX_LENGTH,
                                       DUK_PROPDESC_FLAGS_C);
        }

        /* enable exotic behaviors last */

        if (class_num == DUK_HOBJECT_CLASS_ARRAY) {
            DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_ARRAY(h));  /* set by duk_push_array() */
        }
        if (class_num == DUK_HOBJECT_CLASS_STRING) {
            DUK_HOBJECT_SET_EXOTIC_STRINGOBJ(h);
        }

        /* some assertions */

        DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(h));
        /* DUK_HOBJECT_FLAG_CONSTRUCTABLE varies */
        DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(h));
        DUK_ASSERT(!DUK_HOBJECT_HAS_COMPFUNC(h));
        /* DUK_HOBJECT_FLAG_NATFUNC varies */
        DUK_ASSERT(!DUK_HOBJECT_IS_THREAD(h));
        DUK_ASSERT(!DUK_HOBJECT_IS_PROXY(h));
        DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(h) || class_num == DUK_HOBJECT_CLASS_ARRAY);
        /* DUK_HOBJECT_FLAG_STRICT varies */
        DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(h) ||  /* all native functions have NEWENV */
                   DUK_HOBJECT_HAS_NEWENV(h));
        DUK_ASSERT(!DUK_HOBJECT_HAS_NAMEBINDING(h));
        DUK_ASSERT(!DUK_HOBJECT_HAS_CREATEARGS(h));
        /* DUK_HOBJECT_FLAG_EXOTIC_ARRAY varies */
        /* DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ varies */
        DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(h));

        DUK_DDD(DUK_DDDPRINT("created built-in %ld, class=%ld, length=%ld", (long) i, (long) class_num, (long) len));
    }

    /*
     *  Then decode the builtins init data (see genbuiltins.py) to
     *  init objects.  Internal prototypes are set at this stage,
     *  with thr->builtins[] populated.
     */

    DUK_DD(DUK_DDPRINT("initialize built-in object properties"));
    for (i = 0; i < DUK_NUM_ALL_BUILTINS; i++) {
        duk_small_uint_t t;
        duk_small_uint_t num;

        DUK_DDD(DUK_DDDPRINT("initializing built-in object at index %ld", (long) i));
        h = duk_known_hobject(thr, (duk_idx_t) i);

        t = (duk_small_uint_t) duk_bd_decode_varuint(bd);
        if (t > 0) {
            t--;
            DUK_DDD(DUK_DDDPRINT("set internal prototype: built-in %ld", (long) t));
            DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, duk_known_hobject(thr, (duk_idx_t) t));
        } else if (DUK_HOBJECT_IS_NATFUNC(h)) {
            /* Standard native built-ins cannot inherit from
             * %NativeFunctionPrototype%, they are required to
             * inherit from Function.prototype directly.
             */
            DUK_ASSERT(thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE] != NULL);
            DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
        }

        t = (duk_small_uint_t) duk_bd_decode_varuint(bd);
        if (t > 0) {
            /* 'prototype' property for all built-in objects (which have it) has attributes:
             *  [[Writable]] = false,
             *  [[Enumerable]] = false,
             *  [[Configurable]] = false
             */
            t--;
            DUK_DDD(DUK_DDDPRINT("set external prototype: built-in %ld", (long) t));
            duk_dup(thr, (duk_idx_t) t);
            duk_xdef_prop_stridx(thr, (duk_idx_t) i, DUK_STRIDX_PROTOTYPE, DUK_PROPDESC_FLAGS_NONE);
        }

        t = (duk_small_uint_t) duk_bd_decode_varuint(bd);
        if (t > 0) {
            /* 'constructor' property for all built-in objects (which have it) has attributes:
             *  [[Writable]] = true,
             *  [[Enumerable]] = false,
             *  [[Configurable]] = true
             */
            t--;
            DUK_DDD(DUK_DDDPRINT("set external constructor: built-in %ld", (long) t));
            duk_dup(thr, (duk_idx_t) t);
            duk_xdef_prop_stridx(thr, (duk_idx_t) i, DUK_STRIDX_CONSTRUCTOR, DUK_PROPDESC_FLAGS_WC);
        }

        /* normal valued properties */
        num = (duk_small_uint_t) duk_bd_decode_varuint(bd);
        DUK_DDD(DUK_DDDPRINT("built-in object %ld, %ld normal valued properties", (long) i, (long) num));
        for (j = 0; j < num; j++) {
            duk_small_uint_t defprop_flags;

            duk__push_stridx_or_string(thr, bd);

            /*
             *  Property attribute defaults are defined in E5 Section 15 (first
             *  few pages); there is a default for all properties and a special
             *  default for 'length' properties.  Variation from the defaults is
             *  signaled using a single flag bit in the bitstream.
             */

            defprop_flags = (duk_small_uint_t) duk_bd_decode_flagged(bd,
                                                                     DUK__PROP_FLAGS_BITS,
                                                                     (duk_uint32_t) DUK_PROPDESC_FLAGS_WC);
            defprop_flags |= DUK_DEFPROP_FORCE |
                             DUK_DEFPROP_HAVE_VALUE |
                             DUK_DEFPROP_HAVE_WRITABLE |
                             DUK_DEFPROP_HAVE_ENUMERABLE |
                             DUK_DEFPROP_HAVE_CONFIGURABLE;  /* Defaults for data properties. */

            /* The writable, enumerable, configurable flags in prop_flags
             * match both duk_def_prop() and internal property flags.
             */
            DUK_ASSERT(DUK_PROPDESC_FLAG_WRITABLE == DUK_DEFPROP_WRITABLE);
            DUK_ASSERT(DUK_PROPDESC_FLAG_ENUMERABLE == DUK_DEFPROP_ENUMERABLE);
            DUK_ASSERT(DUK_PROPDESC_FLAG_CONFIGURABLE == DUK_DEFPROP_CONFIGURABLE);

            t = (duk_small_uint_t) duk_bd_decode(bd, DUK__PROP_TYPE_BITS);

            DUK_DDD(DUK_DDDPRINT("built-in %ld, normal-valued property %ld, key %!T, flags 0x%02lx, type %ld",
                                 (long) i, (long) j, duk_get_tval(thr, -1), (unsigned long) defprop_flags, (long) t));

            switch (t) {
            case DUK__PROP_TYPE_DOUBLE: {
                duk__push_double(thr, bd);
                break;
            }
            case DUK__PROP_TYPE_STRING: {
                duk__push_string(thr, bd);
                break;
            }
            case DUK__PROP_TYPE_STRIDX: {
                duk__push_stridx(thr, bd);
                break;
            }
            case DUK__PROP_TYPE_BUILTIN: {
                duk_small_uint_t bidx;

                bidx = (duk_small_uint_t) duk_bd_decode_varuint(bd);
                duk_dup(thr, (duk_idx_t) bidx);
                break;
            }
            case DUK__PROP_TYPE_UNDEFINED: {
                duk_push_undefined(thr);
                break;
            }
            case DUK__PROP_TYPE_BOOLEAN_TRUE: {
                duk_push_true(thr);
                break;
            }
            case DUK__PROP_TYPE_BOOLEAN_FALSE: {
                duk_push_false(thr);
                break;
            }
            case DUK__PROP_TYPE_ACCESSOR: {
                duk_small_uint_t natidx_getter = (duk_small_uint_t) duk_bd_decode_varuint(bd);
                duk_small_uint_t natidx_setter = (duk_small_uint_t) duk_bd_decode_varuint(bd);
                duk_small_uint_t accessor_magic = (duk_small_uint_t) duk_bd_decode_varuint(bd);
                duk_c_function c_func_getter;
                duk_c_function c_func_setter;

                DUK_DDD(DUK_DDDPRINT("built-in accessor property: objidx=%ld, key=%!T, getteridx=%ld, setteridx=%ld, flags=0x%04lx",
                                     (long) i, duk_get_tval(thr, -1), (long) natidx_getter, (long) natidx_setter, (unsigned long) defprop_flags));

                c_func_getter = duk_bi_native_functions[natidx_getter];
                if (c_func_getter != NULL) {
                    duk_push_c_function_builtin_noconstruct(thr, c_func_getter, 0);  /* always 0 args */
                    duk_set_magic(thr, -1, (duk_int_t) accessor_magic);
                    defprop_flags |= DUK_DEFPROP_HAVE_GETTER;
                }
                c_func_setter = duk_bi_native_functions[natidx_setter];
                if (c_func_setter != NULL) {
                    duk_push_c_function_builtin_noconstruct(thr, c_func_setter, 1);  /* always 1 arg */
                    duk_set_magic(thr, -1, (duk_int_t) accessor_magic);
                    defprop_flags |= DUK_DEFPROP_HAVE_SETTER;
                }

                /* Writable flag doesn't make sense for an accessor. */
                DUK_ASSERT((defprop_flags & DUK_PROPDESC_FLAG_WRITABLE) == 0);  /* genbuiltins.py ensures */

                defprop_flags &= ~(DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_HAVE_WRITABLE);
                defprop_flags |= DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_HAVE_CONFIGURABLE;
                break;
            }
            default: {
                /* exhaustive */
                DUK_UNREACHABLE();
            }
            }

            duk_def_prop(thr, (duk_idx_t) i, defprop_flags);
            DUK_ASSERT_TOP(thr, DUK_NUM_ALL_BUILTINS);
        }

        /* native function properties */
        num = (duk_small_uint_t) duk_bd_decode_varuint(bd);
        DUK_DDD(DUK_DDDPRINT("built-in object %ld, %ld function valued properties", (long) i, (long) num));
        for (j = 0; j < num; j++) {
            duk_hstring *h_key;
            duk_small_uint_t natidx;
            duk_int_t c_nargs;  /* must hold DUK_VARARGS */
            duk_small_uint_t c_length;
            duk_int16_t magic;
            duk_c_function c_func;
            duk_hnatfunc *h_func;
#if defined(DUK_USE_LIGHTFUNC_BUILTINS)
            duk_small_int_t lightfunc_eligible;
#endif
            duk_small_uint_t defprop_flags;

            duk__push_stridx_or_string(thr, bd);
            h_key = duk_known_hstring(thr, -1);
            DUK_UNREF(h_key);
            natidx = (duk_small_uint_t) duk_bd_decode_varuint(bd);

            c_length = (duk_small_uint_t) duk_bd_decode(bd, DUK__LENGTH_PROP_BITS);
            c_nargs = (duk_int_t) duk_bd_decode_flagged(bd, DUK__NARGS_BITS, (duk_uint32_t) c_length /*def_value*/);
            if (c_nargs == DUK__NARGS_VARARGS_MARKER) {
                c_nargs = DUK_VARARGS;
            }

            c_func = duk_bi_native_functions[natidx];

            DUK_DDD(DUK_DDDPRINT("built-in %ld, function-valued property %ld, key %!O, natidx %ld, length %ld, nargs %ld",
                                 (long) i, (long) j, (duk_heaphdr *) h_key, (long) natidx, (long) c_length,
                                 (c_nargs == DUK_VARARGS ? (long) -1 : (long) c_nargs)));

            /* Cast converts magic to 16-bit signed value */
            magic = (duk_int16_t) duk_bd_decode_varuint(bd);

#if defined(DUK_USE_LIGHTFUNC_BUILTINS)
            lightfunc_eligible =
                ((c_nargs >= DUK_LFUNC_NARGS_MIN && c_nargs <= DUK_LFUNC_NARGS_MAX) || (c_nargs == DUK_VARARGS)) &&
                (c_length <= DUK_LFUNC_LENGTH_MAX) &&
                (magic >= DUK_LFUNC_MAGIC_MIN && magic <= DUK_LFUNC_MAGIC_MAX);

            /* These functions have trouble working as lightfuncs.
             * Some of them have specific asserts and some may have
                 * additional properties (e.g. 'require.id' may be written).
             */
            if (c_func == duk_bi_global_object_eval) {
                lightfunc_eligible = 0;
            }
#if defined(DUK_USE_COROUTINE_SUPPORT)
            if (c_func == duk_bi_thread_yield ||
                c_func == duk_bi_thread_resume) {
                lightfunc_eligible = 0;
            }
#endif
            if (c_func == duk_bi_function_prototype_call ||
                c_func == duk_bi_function_prototype_apply ||
                c_func == duk_bi_reflect_apply ||
                c_func == duk_bi_reflect_construct) {
                lightfunc_eligible = 0;
            }

            if (lightfunc_eligible) {
                duk_tval tv_lfunc;
                duk_small_uint_t lf_nargs = (duk_small_uint_t) (c_nargs == DUK_VARARGS ? DUK_LFUNC_NARGS_VARARGS : c_nargs);
                duk_small_uint_t lf_flags = DUK_LFUNC_FLAGS_PACK(magic, c_length, lf_nargs);
                DUK_TVAL_SET_LIGHTFUNC(&tv_lfunc, c_func, lf_flags);
                duk_push_tval(thr, &tv_lfunc);
                DUK_D(DUK_DPRINT("built-in function eligible as light function: i=%d, j=%d c_length=%ld, c_nargs=%ld, magic=%ld -> %!iT", (int) i, (int) j, (long) c_length, (long) c_nargs, (long) magic, duk_get_tval(thr, -1)));
                goto lightfunc_skip;
            }

            DUK_D(DUK_DPRINT("built-in function NOT ELIGIBLE as light function: i=%d, j=%d c_length=%ld, c_nargs=%ld, magic=%ld", (int) i, (int) j, (long) c_length, (long) c_nargs, (long) magic));
#endif  /* DUK_USE_LIGHTFUNC_BUILTINS */

            /* [ (builtin objects) name ] */

            duk_push_c_function_builtin_noconstruct(thr, c_func, c_nargs);
            h_func = duk_known_hnatfunc(thr, -1);
            DUK_UNREF(h_func);

            /* XXX: add into init data? */

            /* Special call handling, not described in init data. */
            if (c_func == duk_bi_global_object_eval ||
                c_func == duk_bi_function_prototype_call ||
                c_func == duk_bi_function_prototype_apply ||
                c_func == duk_bi_reflect_apply ||
                c_func == duk_bi_reflect_construct) {
                DUK_HOBJECT_SET_SPECIAL_CALL((duk_hobject *) h_func);
            }

            /* Currently all built-in native functions are strict.
             * This doesn't matter for many functions, but e.g.
             * String.prototype.charAt (and other string functions)
             * rely on being strict so that their 'this' binding is
             * not automatically coerced.
             */
            DUK_HOBJECT_SET_STRICT((duk_hobject *) h_func);

            /* No built-in functions are constructable except the top
             * level ones (Number, etc).
             */
            DUK_ASSERT(!DUK_HOBJECT_HAS_CONSTRUCTABLE((duk_hobject *) h_func));

            /* XXX: any way to avoid decoding magic bit; there are quite
             * many function properties and relatively few with magic values.
             */
            h_func->magic = magic;

            /* [ (builtin objects) name func ] */

            duk_push_uint(thr, c_length);
            duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_C);

            duk_dup_m2(thr);
            duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C);

            /* XXX: other properties of function instances; 'arguments', 'caller'. */

            DUK_DD(DUK_DDPRINT("built-in object %ld, function property %ld -> %!T",
                               (long) i, (long) j, (duk_tval *) duk_get_tval(thr, -1)));

            /* [ (builtin objects) name func ] */

            /*
             *  The default property attributes are correct for all
             *  function valued properties of built-in objects now.
             */

#if defined(DUK_USE_LIGHTFUNC_BUILTINS)
         lightfunc_skip:
#endif

            defprop_flags = (duk_small_uint_t) duk_bd_decode_flagged(bd,
                                                                     DUK__PROP_FLAGS_BITS,
                                                                     (duk_uint32_t) DUK_PROPDESC_FLAGS_WC);
            defprop_flags |= DUK_DEFPROP_FORCE |
                             DUK_DEFPROP_HAVE_VALUE |
                             DUK_DEFPROP_HAVE_WRITABLE |
                             DUK_DEFPROP_HAVE_ENUMERABLE |
                             DUK_DEFPROP_HAVE_CONFIGURABLE;
            DUK_ASSERT(DUK_PROPDESC_FLAG_WRITABLE == DUK_DEFPROP_WRITABLE);
            DUK_ASSERT(DUK_PROPDESC_FLAG_ENUMERABLE == DUK_DEFPROP_ENUMERABLE);
            DUK_ASSERT(DUK_PROPDESC_FLAG_CONFIGURABLE == DUK_DEFPROP_CONFIGURABLE);

            duk_def_prop(thr, (duk_idx_t) i, defprop_flags);

            /* [ (builtin objects) ] */
        }
    }

    /*
     *  Special post-tweaks, for cases not covered by the init data format.
     *
     *  - Set Date.prototype.toGMTString to Date.prototype.toUTCString.
     *    toGMTString is required to have the same Function object as
     *    toUTCString in E5 Section B.2.6.  Note that while Smjs respects
     *    this, V8 does not (the Function objects are distinct).
     *
     *  - Make DoubleError non-extensible.
     *
     *  - Add info about most important effective compile options to Duktape.
     *
     *  - Possibly remove some properties (values or methods) which are not
     *    desirable with current feature options but are not currently
     *    conditional in init data.
     */

#if defined(DUK_USE_DATE_BUILTIN)
    duk_get_prop_stridx_short(thr, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_UTC_STRING);
    duk_xdef_prop_stridx_short(thr, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_GMT_STRING, DUK_PROPDESC_FLAGS_WC);
#endif

    h = duk_known_hobject(thr, DUK_BIDX_DOUBLE_ERROR);
    DUK_HOBJECT_CLEAR_EXTENSIBLE(h);

#if !defined(DUK_USE_ES6_OBJECT_PROTO_PROPERTY)
    DUK_DD(DUK_DDPRINT("delete Object.prototype.__proto__ built-in which is not enabled in features"));
    (void) duk_hobject_delprop_raw(thr, thr->builtins[DUK_BIDX_OBJECT_PROTOTYPE], DUK_HTHREAD_STRING___PROTO__(thr), DUK_DELPROP_FLAG_THROW);
#endif

#if !defined(DUK_USE_ES6_OBJECT_SETPROTOTYPEOF)
    DUK_DD(DUK_DDPRINT("delete Object.setPrototypeOf built-in which is not enabled in features"));
    (void) duk_hobject_delprop_raw(thr, thr->builtins[DUK_BIDX_OBJECT_CONSTRUCTOR], DUK_HTHREAD_STRING_SET_PROTOTYPE_OF(thr), DUK_DELPROP_FLAG_THROW);
#endif

    /* XXX: relocate */
    duk_push_string(thr,
            /* Endianness indicator */
#if defined(DUK_USE_INTEGER_LE)
                    "l"
#elif defined(DUK_USE_INTEGER_BE)
                    "b"
#elif defined(DUK_USE_INTEGER_ME)  /* integer mixed endian not really used now */
                    "m"
#else
                    "?"
#endif
#if defined(DUK_USE_DOUBLE_LE)
                    "l"
#elif defined(DUK_USE_DOUBLE_BE)
                    "b"
#elif defined(DUK_USE_DOUBLE_ME)
                    "m"
#else
                    "?"
#endif
                    " "
            /* Packed or unpacked tval */
#if defined(DUK_USE_PACKED_TVAL)
                    "p"
#else
                    "u"
#endif
#if defined(DUK_USE_FASTINT)
            "f"
#endif
            " "
            /* Low memory/performance options */
#if defined(DUK_USE_STRTAB_PTRCOMP)
            "s"
#endif
#if !defined(DUK_USE_HEAPPTR16) && !defined(DUK_DATAPTR16) && !defined(DUK_FUNCPTR16)
            "n"
#endif
#if defined(DUK_USE_HEAPPTR16)
            "h"
#endif
#if defined(DUK_USE_DATAPTR16)
            "d"
#endif
#if defined(DUK_USE_FUNCPTR16)
            "f"
#endif
#if defined(DUK_USE_REFCOUNT16)
            "R"
#endif
#if defined(DUK_USE_STRHASH16)
            "H"
#endif
#if defined(DUK_USE_STRLEN16)
            "S"
#endif
#if defined(DUK_USE_BUFLEN16)
            "B"
#endif
#if defined(DUK_USE_OBJSIZES16)
            "O"
#endif
#if defined(DUK_USE_LIGHTFUNC_BUILTINS)
            "L"
#endif
#if defined(DUK_USE_ROM_STRINGS) || defined(DUK_USE_ROM_OBJECTS)
            /* XXX: This won't be shown in practice now
             * because this code is not run when builtins
             * are in ROM.
             */
            "Z"
#endif
#if defined(DUK_USE_LITCACHE_SIZE)
            "l"
#endif
                    " "
            /* Object property allocation layout */
#if defined(DUK_USE_HOBJECT_LAYOUT_1)
            "p1"
#elif defined(DUK_USE_HOBJECT_LAYOUT_2)
            "p2"
#elif defined(DUK_USE_HOBJECT_LAYOUT_3)
            "p3"
#else
            "p?"
#endif
            " "
            /* Alignment guarantee */
#if (DUK_USE_ALIGN_BY == 4)
            "a4"
#elif (DUK_USE_ALIGN_BY == 8)
            "a8"
#elif (DUK_USE_ALIGN_BY == 1)
            "a1"
#else
#error invalid DUK_USE_ALIGN_BY
#endif
            " "
            /* Architecture, OS, and compiler strings */
                    DUK_USE_ARCH_STRING
            " "
                    DUK_USE_OS_STRING
            " "
                    DUK_USE_COMPILER_STRING);
    duk_xdef_prop_stridx_short(thr, DUK_BIDX_DUKTAPE, DUK_STRIDX_ENV, DUK_PROPDESC_FLAGS_WC);

    /*
     *  Since built-ins are not often extended, compact them.
     */

    DUK_DD(DUK_DDPRINT("compact built-ins"));
    for (i = 0; i < DUK_NUM_ALL_BUILTINS; i++) {
        duk_hobject_compact_props(thr, duk_known_hobject(thr, (duk_idx_t) i));
    }

    DUK_D(DUK_DPRINT("INITBUILTINS END"));

#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 1)
    for (i = 0; i < DUK_NUM_ALL_BUILTINS; i++) {
        DUK_DD(DUK_DDPRINT("built-in object %ld after initialization and compacting: %!@iO",
                           (long) i, (duk_heaphdr *) duk_require_hobject(thr, (duk_idx_t) i)));
    }
#endif

    /*
     *  Pop built-ins from stack: they are now INCREF'd and
     *  reachable from the builtins[] array or indirectly
     *  through builtins[].
     */

    duk_set_top(thr, 0);
    DUK_ASSERT_TOP(thr, 0);
}
#endif  /* DUK_USE_ROM_OBJECTS */

DUK_INTERNAL void duk_hthread_copy_builtin_objects(duk_hthread *thr_from, duk_hthread *thr_to) {
    duk_small_uint_t i;

    for (i = 0; i < DUK_NUM_BUILTINS; i++) {
        thr_to->builtins[i] = thr_from->builtins[i];
        DUK_HOBJECT_INCREF_ALLOWNULL(thr_to, thr_to->builtins[i]);  /* side effect free */
    }
}

/* automatic undefs */
#undef DUK__LENGTH_PROP_BITS
#undef DUK__NARGS_BITS
#undef DUK__NARGS_VARARGS_MARKER
#undef DUK__PROP_FLAGS_BITS
#undef DUK__PROP_TYPE_ACCESSOR
#undef DUK__PROP_TYPE_BITS
#undef DUK__PROP_TYPE_BOOLEAN_FALSE
#undef DUK__PROP_TYPE_BOOLEAN_TRUE
#undef DUK__PROP_TYPE_BUILTIN
#undef DUK__PROP_TYPE_DOUBLE
#undef DUK__PROP_TYPE_STRIDX
#undef DUK__PROP_TYPE_STRING
#undef DUK__PROP_TYPE_UNDEFINED
//#line 1 "duk_hthread_misc.c"
/*
 *  Thread support.
 */

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

DUK_INTERNAL void duk_hthread_terminate(duk_hthread *thr) {
    DUK_ASSERT(thr != NULL);

    while (thr->callstack_curr != NULL) {
        duk_hthread_activation_unwind_norz(thr);
    }

    thr->valstack_bottom = thr->valstack;
    duk_set_top(thr, 0);  /* unwinds valstack, updating refcounts */

    thr->state = DUK_HTHREAD_STATE_TERMINATED;

    /* Here we could remove references to built-ins, but it may not be
     * worth the effort because built-ins are quite likely to be shared
     * with another (unterminated) thread, and terminated threads are also
     * usually garbage collected quite quickly.
     *
     * We could also shrink the value stack here, but that also may not
     * be worth the effort for the same reason.
     */

    DUK_REFZERO_CHECK_SLOW(thr);
}

#if defined(DUK_USE_DEBUGGER_SUPPORT)
DUK_INTERNAL duk_uint_fast32_t duk_hthread_get_act_curr_pc(duk_hthread *thr, duk_activation *act) {
    duk_instr_t *bcode;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(act != NULL);
    DUK_UNREF(thr);

    /* XXX: store 'bcode' pointer to activation for faster lookup? */
    if (act->func && DUK_HOBJECT_IS_COMPFUNC(act->func)) {
        bcode = DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, (duk_hcompfunc *) (act->func));
        return (duk_uint_fast32_t) (act->curr_pc - bcode);
    }
    return 0;
}
#endif  /* DUK_USE_DEBUGGER_SUPPORT */

DUK_INTERNAL duk_uint_fast32_t duk_hthread_get_act_prev_pc(duk_hthread *thr, duk_activation *act) {
    duk_instr_t *bcode;
    duk_uint_fast32_t ret;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(act != NULL);
    DUK_UNREF(thr);

    if (act->func && DUK_HOBJECT_IS_COMPFUNC(act->func)) {
        bcode = DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, (duk_hcompfunc *) (act->func));
        ret = (duk_uint_fast32_t) (act->curr_pc - bcode);
        if (ret > 0) {
            ret--;
        }
        return ret;
    }
    return 0;
}

/* Write bytecode executor's curr_pc back to topmost activation (if any). */
DUK_INTERNAL void duk_hthread_sync_currpc(duk_hthread *thr) {
    duk_activation *act;

    DUK_ASSERT(thr != NULL);

    if (thr->ptr_curr_pc != NULL) {
        /* ptr_curr_pc != NULL only when bytecode executor is active. */
        DUK_ASSERT(thr->callstack_top > 0);
        DUK_ASSERT(thr->callstack_curr != NULL);
        act = thr->callstack_curr;
        DUK_ASSERT(act != NULL);
        act->curr_pc = *thr->ptr_curr_pc;
    }
}

DUK_INTERNAL void duk_hthread_sync_and_null_currpc(duk_hthread *thr) {
    duk_activation *act;

    DUK_ASSERT(thr != NULL);

    if (thr->ptr_curr_pc != NULL) {
        /* ptr_curr_pc != NULL only when bytecode executor is active. */
        DUK_ASSERT(thr->callstack_top > 0);
        DUK_ASSERT(thr->callstack_curr != NULL);
        act = thr->callstack_curr;
        DUK_ASSERT(act != NULL);
        act->curr_pc = *thr->ptr_curr_pc;
        thr->ptr_curr_pc = NULL;
    }
}
//#line 1 "duk_hthread_stacks.c"
/*
 *  Thread stack (mainly call stack) primitives: allocation of activations,
 *  unwinding catchers and activations, etc.
 *
 *  Value stack handling is a part of the API implementation.
 */

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

/* Unwind the topmost catcher of the current activation (caller must check that
 * both exist) without side effects.
 */
DUK_INTERNAL void duk_hthread_catcher_unwind_norz(duk_hthread *thr, duk_activation *act) {
    duk_catcher *cat;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(act != NULL);
    DUK_ASSERT(act->cat != NULL);  /* caller must check */
    cat = act->cat;
    DUK_ASSERT(cat != NULL);

    DUK_DDD(DUK_DDDPRINT("unwinding catch stack entry %p (lexenv check is done)", (void *) cat));

    if (DUK_CAT_HAS_LEXENV_ACTIVE(cat)) {
        duk_hobject *env;

        env = act->lex_env;             /* current lex_env of the activation (created for catcher) */
        DUK_ASSERT(env != NULL);        /* must be, since env was created when catcher was created */
        act->lex_env = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, env);  /* prototype is lex_env before catcher created */
        DUK_HOBJECT_INCREF(thr, act->lex_env);
        DUK_HOBJECT_DECREF_NORZ(thr, env);

        /* There is no need to decref anything else than 'env': if 'env'
         * becomes unreachable, refzero will handle decref'ing its prototype.
         */
    }

    act->cat = cat->parent;
    duk_hthread_catcher_free(thr, cat);
}

/* Same as above, but caller is certain no catcher-related lexenv may exist. */
DUK_INTERNAL void duk_hthread_catcher_unwind_nolexenv_norz(duk_hthread *thr, duk_activation *act) {
    duk_catcher *cat;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(act != NULL);
    DUK_ASSERT(act->cat != NULL);  /* caller must check */
    cat = act->cat;
    DUK_ASSERT(cat != NULL);

    DUK_DDD(DUK_DDDPRINT("unwinding catch stack entry %p (lexenv check is not done)", (void *) cat));

    DUK_ASSERT(!DUK_CAT_HAS_LEXENV_ACTIVE(cat));

    act->cat = cat->parent;
    duk_hthread_catcher_free(thr, cat);
}

DUK_LOCAL
#if defined(DUK_USE_CACHE_CATCHER)
DUK_NOINLINE
#endif
duk_catcher *duk__hthread_catcher_alloc_slow(duk_hthread *thr) {
    duk_catcher *cat;

    cat = (duk_catcher *) DUK_ALLOC_CHECKED(thr, sizeof(duk_catcher));
    DUK_ASSERT(cat != NULL);
    return cat;
}

#if defined(DUK_USE_CACHE_CATCHER)
DUK_INTERNAL DUK_INLINE duk_catcher *duk_hthread_catcher_alloc(duk_hthread *thr) {
    duk_catcher *cat;

    DUK_ASSERT(thr != NULL);

    cat = thr->heap->catcher_free;
    if (DUK_LIKELY(cat != NULL)) {
        thr->heap->catcher_free = cat->parent;
        return cat;
    }

    return duk__hthread_catcher_alloc_slow(thr);
}
#else  /* DUK_USE_CACHE_CATCHER */
DUK_INTERNAL duk_catcher *duk_hthread_catcher_alloc(duk_hthread *thr) {
    return duk__hthread_catcher_alloc_slow(thr);
}
#endif  /* DUK_USE_CACHE_CATCHER */

DUK_INTERNAL void duk_hthread_catcher_free(duk_hthread *thr, duk_catcher *cat) {
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(cat != NULL);

#if defined(DUK_USE_CACHE_CATCHER)
    /* Unconditional caching for now; freed in mark-and-sweep. */
    cat->parent = thr->heap->catcher_free;
    thr->heap->catcher_free = cat;
#else
    DUK_FREE_CHECKED(thr, (void *) cat);
#endif
}

DUK_LOCAL
#if defined(DUK_USE_CACHE_ACTIVATION)
DUK_NOINLINE
#endif
duk_activation *duk__hthread_activation_alloc_slow(duk_hthread *thr) {
    duk_activation *act;

    act = (duk_activation *) DUK_ALLOC_CHECKED(thr, sizeof(duk_activation));
    DUK_ASSERT(act != NULL);
    return act;
}

#if defined(DUK_USE_CACHE_ACTIVATION)
DUK_INTERNAL DUK_INLINE duk_activation *duk_hthread_activation_alloc(duk_hthread *thr) {
    duk_activation *act;

    DUK_ASSERT(thr != NULL);

    act = thr->heap->activation_free;
    if (DUK_LIKELY(act != NULL)) {
        thr->heap->activation_free = act->parent;
        return act;
    }

    return duk__hthread_activation_alloc_slow(thr);
}
#else  /* DUK_USE_CACHE_ACTIVATION */
DUK_INTERNAL duk_activation *duk_hthread_activation_alloc(duk_hthread *thr) {
    return duk__hthread_activation_alloc_slow(thr);
}
#endif  /* DUK_USE_CACHE_ACTIVATION */


DUK_INTERNAL void duk_hthread_activation_free(duk_hthread *thr, duk_activation *act) {
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(act != NULL);

#if defined(DUK_USE_CACHE_ACTIVATION)
    /* Unconditional caching for now; freed in mark-and-sweep. */
    act->parent = thr->heap->activation_free;
    thr->heap->activation_free = act;
#else
    DUK_FREE_CHECKED(thr, (void *) act);
#endif
}

/* Internal helper: process the unwind for the topmost activation of a thread,
 * but leave the duk_activation in place for possible tailcall reuse.
 */
DUK_LOCAL void duk__activation_unwind_nofree_norz(duk_hthread *thr) {
#if defined(DUK_USE_DEBUGGER_SUPPORT)
    duk_heap *heap;
#endif
    duk_activation *act;
    duk_hobject *func;
    duk_hobject *tmp;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->callstack_curr != NULL);  /* caller must check */
    DUK_ASSERT(thr->callstack_top > 0);
    act = thr->callstack_curr;
    DUK_ASSERT(act != NULL);
    /* With lightfuncs, act 'func' may be NULL. */

    /* With duk_activation records allocated separately, 'act' is a stable
     * pointer and not affected by side effects.
     */

#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
    /*
     *  Restore 'caller' property for non-strict callee functions.
     */

    func = DUK_ACT_GET_FUNC(act);
    if (func != NULL && !DUK_HOBJECT_HAS_STRICT(func)) {
        duk_tval *tv_caller;
        duk_tval tv_tmp;
        duk_hobject *h_tmp;

        tv_caller = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, func, DUK_STRIDX_CALLER);

        /* The act->prev_caller should only be set if the entry for 'caller'
         * exists (as it is only set in that case, and the property is not
         * configurable), but handle all the cases anyway.
         */

        if (tv_caller) {
            DUK_TVAL_SET_TVAL(&tv_tmp, tv_caller);
            if (act->prev_caller) {
                /* Just transfer the refcount from act->prev_caller to tv_caller,
                 * so no need for a refcount update.  This is the expected case.
                 */
                DUK_TVAL_SET_OBJECT(tv_caller, act->prev_caller);
                act->prev_caller = NULL;
            } else {
                DUK_TVAL_SET_NULL(tv_caller);   /* no incref needed */
                DUK_ASSERT(act->prev_caller == NULL);
            }
            DUK_TVAL_DECREF_NORZ(thr, &tv_tmp);
        } else {
            h_tmp = act->prev_caller;
            if (h_tmp) {
                act->prev_caller = NULL;
                DUK_HOBJECT_DECREF_NORZ(thr, h_tmp);
            }
        }
        DUK_ASSERT(act->prev_caller == NULL);
    }
#endif

    /*
     *  Unwind debugger state.  If we unwind while stepping
     *  (for any step type), pause execution.  This is the
     *  only place explicitly handling a step out.
     */

#if defined(DUK_USE_DEBUGGER_SUPPORT)
    heap = thr->heap;
    if (heap->dbg_pause_act == thr->callstack_curr) {
        if (heap->dbg_pause_flags & DUK_PAUSE_FLAG_FUNC_EXIT) {
            DUK_D(DUK_DPRINT("PAUSE TRIGGERED by function exit"));
            duk_debug_set_paused(heap);
        } else {
            DUK_D(DUK_DPRINT("unwound past dbg_pause_act, set to NULL"));
            heap->dbg_pause_act = NULL;  /* avoid stale pointers */
        }
        DUK_ASSERT(heap->dbg_pause_act == NULL);
    }
#endif

    /*
     *  Unwind catchers.
     *
     *  Since there are no references in the catcher structure,
     *  unwinding is quite simple.  The only thing we need to
     *  look out for is popping a possible lexical environment
     *  established for an active catch clause.
     */

    while (act->cat != NULL) {
        duk_hthread_catcher_unwind_norz(thr, act);
    }

    /*
     *  Close environment record(s) if they exist.
     *
     *  Only variable environments are closed.  If lex_env != var_env, it
     *  cannot currently contain any register bound declarations.
     *
     *  Only environments created for a NEWENV function are closed.  If an
     *  environment is created for e.g. an eval call, it must not be closed.
     */

    func = DUK_ACT_GET_FUNC(act);
    if (func != NULL && !DUK_HOBJECT_HAS_NEWENV(func)) {
        DUK_DDD(DUK_DDDPRINT("skip closing environments, envs not owned by this activation"));
        goto skip_env_close;
    }
    /* func is NULL for lightfunc */

    /* Catch sites are required to clean up their environments
     * in FINALLY part before propagating, so this should
     * always hold here.
     */
    DUK_ASSERT(act->lex_env == act->var_env);

    /* XXX: Closing the environment record copies values from registers
     * into the scope object.  It's side effect free as such, but may
     * currently run out of memory which causes an error throw.  This is
     * an actual sandboxing problem for error unwinds, and needs to be
     * fixed e.g. by preallocating the scope property slots.
     */
    if (act->var_env != NULL) {
        DUK_DDD(DUK_DDDPRINT("closing var_env record %p -> %!O",
                             (void *) act->var_env, (duk_heaphdr *) act->var_env));
        duk_js_close_environment_record(thr, act->var_env);
    }

 skip_env_close:

    /*
     *  Update preventcount
     */

    if (act->flags & DUK_ACT_FLAG_PREVENT_YIELD) {
        DUK_ASSERT(thr->callstack_preventcount >= 1);
        thr->callstack_preventcount--;
    }

    /*
     *  Reference count updates, using NORZ macros so we don't
     *  need to handle side effects.
     *
     *  duk_activation pointers like act->var_env are intentionally
     *  left as garbage and not NULLed.  Without side effects they
     *  can't be used when the values are dangling/garbage.
     */

    DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, act->var_env);
    DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, act->lex_env);
    tmp = DUK_ACT_GET_FUNC(act);
    DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, tmp);
    DUK_UNREF(tmp);
}

/* Unwind topmost duk_activation of a thread, caller must ensure that an
 * activation exists.  The call is side effect free, except that scope
 * closure may currently throw an out-of-memory error.
 */
DUK_INTERNAL void duk_hthread_activation_unwind_norz(duk_hthread *thr) {
    duk_activation *act;

    duk__activation_unwind_nofree_norz(thr);

    DUK_ASSERT(thr->callstack_curr != NULL);
    DUK_ASSERT(thr->callstack_top > 0);
    act = thr->callstack_curr;
    thr->callstack_curr = act->parent;
    thr->callstack_top--;

    /* Ideally we'd restore value stack reserve here to caller's value.
     * This doesn't work for current unwind call sites however, because
     * the current (unwound) value stack top may be above the reserve.
     * Thus value stack reserve is restored by the call sites.
     */

    /* XXX: inline for performance builds? */
    duk_hthread_activation_free(thr, act);

    /* We could clear the book-keeping variables like retval_byteoff for
     * the topmost activation, but don't do so now as it's not necessary.
     */
}

DUK_INTERNAL void duk_hthread_activation_unwind_reuse_norz(duk_hthread *thr) {
    duk__activation_unwind_nofree_norz(thr);
}

/* Get duk_activation for given callstack level or NULL if level is invalid
 * or deeper than the call stack.  Level -1 refers to current activation, -2
 * to its caller, etc.  Starting from Duktape 2.2 finding the activation is
 * a linked list scan which gets more expensive the deeper the lookup is.
 */
DUK_INTERNAL duk_activation *duk_hthread_get_activation_for_level(duk_hthread *thr, duk_int_t level) {
    duk_activation *act;

    if (level >= 0) {
        return NULL;
    }
    act = thr->callstack_curr;
    for (;;) {
        if (act == NULL) {
            return act;
        }
        if (level == -1) {
            return act;
        }
        level++;
        act = act->parent;
    }
    /* never here */
}

#if defined(DUK_USE_FINALIZER_TORTURE)
DUK_INTERNAL void duk_hthread_valstack_torture_realloc(duk_hthread *thr) {
    duk_size_t alloc_size;
    duk_tval *new_ptr;
    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 (duk_double_equals(d1, 0.0)) {
        /* d1 +/-0 is returned as is (preserving sign) except when
         * d2 is zero or NaN.
         */
        if (duk_double_equals(d2, 0.0) || DUK_ISNAN(d2)) {
            return DUK_DOUBLE_NAN;
        } else {
            return d1;
        }
    }
#else
    /* Some ISO C assumptions. */
    DUK_ASSERT(duk_double_equals(DUK_FMOD(1.0, DUK_DOUBLE_INFINITY), 1.0));
    DUK_ASSERT(duk_double_equals(DUK_FMOD(-1.0, DUK_DOUBLE_INFINITY), -1.0));
    DUK_ASSERT(duk_double_equals(DUK_FMOD(1.0, -DUK_DOUBLE_INFINITY), 1.0));
    DUK_ASSERT(duk_double_equals(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_double_equals(DUK_FMOD(0.0, 1.0), 0.0) && DUK_SIGNBIT(DUK_FMOD(0.0, 1.0)) == 0);
    DUK_ASSERT(duk_double_equals(DUK_FMOD(-0.0, 1.0), 0.0) && DUK_SIGNBIT(DUK_FMOD(-0.0, 1.0)) != 0);
    DUK_ASSERT(duk_double_equals(DUK_FMOD(0.0, DUK_DOUBLE_INFINITY), 0.0) && DUK_SIGNBIT(DUK_FMOD(0.0, DUK_DOUBLE_INFINITY)) == 0);
    DUK_ASSERT(duk_double_equals(DUK_FMOD(-0.0, DUK_DOUBLE_INFINITY), 0.0) && DUK_SIGNBIT(DUK_FMOD(-0.0, DUK_DOUBLE_INFINITY)) != 0);
    DUK_ASSERT(duk_double_equals(DUK_FMOD(0.0, -DUK_DOUBLE_INFINITY), 0.0) && DUK_SIGNBIT(DUK_FMOD(0.0, DUK_DOUBLE_INFINITY)) == 0);
    DUK_ASSERT(duk_double_equals(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_double_equals(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 (duk_double_equals(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_double_equals(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.
 */

/* Check native stack space if DUK_USE_NATIVE_STACK_CHECK() defined. */
DUK_INTERNAL void duk_native_stack_check(duk_hthread *thr) {
#if defined(DUK_USE_NATIVE_STACK_CHECK)
    if (DUK_USE_NATIVE_STACK_CHECK() != 0) {
        DUK_ERROR_RANGE(thr, DUK_STR_NATIVE_STACK_LIMIT);
    }
#else
    DUK_UNREF(thr);
#endif
}

/* 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.
 */
#define DUK__AUGMENT_CALL_RELAX_COUNT  (10 + 2)

/* Stack space required by call handling entry. */
#define DUK__CALL_HANDLING_REQUIRE_STACK  8

DUK_LOCAL DUK_NOINLINE void duk__call_c_recursion_limit_check_slowpath(duk_hthread *thr) {
    /* When augmenting an error, the effective limit is a bit higher.
     * Check for it only if the fast path check fails.
     */
#if defined(DUK_USE_AUGMENT_ERROR_THROW) || defined(DUK_USE_AUGMENT_ERROR_CREATE)
    if (thr->heap->augmenting_error) {
        if (thr->heap->call_recursion_depth < thr->heap->call_recursion_limit + DUK__AUGMENT_CALL_RELAX_COUNT) {
            DUK_D(DUK_DPRINT("C recursion limit reached but augmenting error and within relaxed limit"));
            return;
        }
    }
#endif

    DUK_D(DUK_DPRINT("call prevented because C recursion limit reached"));
    DUK_ERROR_RANGE(thr, DUK_STR_NATIVE_STACK_LIMIT);
    DUK_WO_NORETURN(return;);
}

DUK_LOCAL DUK_ALWAYS_INLINE void duk__call_c_recursion_limit_check(duk_hthread *thr) {
    DUK_ASSERT(thr->heap->call_recursion_depth >= 0);
    DUK_ASSERT(thr->heap->call_recursion_depth <= thr->heap->call_recursion_limit);

    duk_native_stack_check(thr);

    /* This check is forcibly inlined because it's very cheap and almost
     * always passes.  The slow path is forcibly noinline.
     */
    if (DUK_LIKELY(thr->heap->call_recursion_depth < thr->heap->call_recursion_limit)) {
        return;
    }

    duk__call_c_recursion_limit_check_slowpath(thr);
}

DUK_LOCAL DUK_NOINLINE void duk__call_callstack_limit_check_slowpath(duk_hthread *thr) {
    /* When augmenting an error, the effective limit is a bit higher.
     * Check for it only if the fast path check fails.
     */
#if defined(DUK_USE_AUGMENT_ERROR_THROW) || defined(DUK_USE_AUGMENT_ERROR_CREATE)
    if (thr->heap->augmenting_error) {
        if (thr->callstack_top < DUK_USE_CALLSTACK_LIMIT + DUK__AUGMENT_CALL_RELAX_COUNT) {
            DUK_D(DUK_DPRINT("call stack limit reached but augmenting error and within relaxed limit"));
            return;
        }
    }
#endif

    /* XXX: error message is a bit misleading: we reached a recursion
     * limit which is also essentially the same as a C callstack limit
     * (except perhaps with some relaxed threading assumptions).
     */
    DUK_D(DUK_DPRINT("call prevented because call stack limit reached"));
    DUK_ERROR_RANGE(thr, DUK_STR_CALLSTACK_LIMIT);
    DUK_WO_NORETURN(return;);
}

DUK_LOCAL DUK_ALWAYS_INLINE void duk__call_callstack_limit_check(duk_hthread *thr) {
    /* This check is forcibly inlined because it's very cheap and almost
     * always passes.  The slow path is forcibly noinline.
     */
    if (DUK_LIKELY(thr->callstack_top < DUK_USE_CALLSTACK_LIMIT)) {
        return;
    }

    duk__call_callstack_limit_check_slowpath(thr);
}

/*
 *  Interrupt counter fixup (for development only).
 */

#if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG)
DUK_LOCAL void duk__interrupt_fixup(duk_hthread *thr, duk_hthread *entry_curr_thread) {
    /* Currently the bytecode executor and executor interrupt
     * instruction counts are off because we don't execute the
     * interrupt handler when we're about to exit from the initial
     * user call into Duktape.
     *
     * If we were to execute the interrupt handler here, the counts
     * would match.  You can enable this block manually to check
     * that this is the case.
     */

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);

#if defined(DUK_USE_INTERRUPT_DEBUG_FIXUP)
    if (entry_curr_thread == NULL) {
        thr->interrupt_init = thr->interrupt_init - thr->interrupt_counter;
        thr->heap->inst_count_interrupt += thr->interrupt_init;
        DUK_DD(DUK_DDPRINT("debug test: updated interrupt count on exit to "
                           "user code, instruction counts: executor=%ld, interrupt=%ld",
                           (long) thr->heap->inst_count_exec, (long) thr->heap->inst_count_interrupt));
        DUK_ASSERT(thr->heap->inst_count_exec == thr->heap->inst_count_interrupt);
    }
#else
    DUK_UNREF(thr);
    DUK_UNREF(entry_curr_thread);
#endif
}
#endif

/*
 *  Arguments object creation.
 *
 *  Creating arguments objects involves many small details, see E5 Section
 *  10.6 for the specific requirements.  Much of the arguments object exotic
 *  behavior is implemented in duk_hobject_props.c, and is enabled by the
 *  object flag DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS.
 */

DUK_LOCAL void duk__create_arguments_object(duk_hthread *thr,
                                            duk_hobject *func,
                                            duk_hobject *varenv,
                                            duk_idx_t idx_args) {
    duk_hobject *arg;          /* 'arguments' */
    duk_hobject *formals;      /* formals for 'func' (may be NULL if func is a C function) */
    duk_idx_t i_arg;
    duk_idx_t i_map;
    duk_idx_t i_mappednames;
    duk_idx_t i_formals;
    duk_idx_t i_argbase;
    duk_idx_t n_formals;
    duk_idx_t idx;
    duk_idx_t num_stack_args;
    duk_bool_t need_map;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(func != NULL);
    DUK_ASSERT(DUK_HOBJECT_IS_NONBOUND_FUNCTION(func));
    DUK_ASSERT(varenv != NULL);

    /* [ ... func this arg1(@idx_args) ... argN envobj ]
     * [ arg1(@idx_args) ... argN envobj ] (for tailcalls)
     */

    need_map = 0;

    i_argbase = idx_args;
    num_stack_args = duk_get_top(thr) - i_argbase - 1;
    DUK_ASSERT(i_argbase >= 0);
    DUK_ASSERT(num_stack_args >= 0);

    formals = (duk_hobject *) duk_hobject_get_formals(thr, (duk_hobject *) func);
    if (formals) {
        n_formals = (duk_idx_t) ((duk_harray *) formals)->length;
        duk_push_hobject(thr, formals);
    } else {
        /* This shouldn't happen without tampering of internal
         * properties: if a function accesses 'arguments', _Formals
         * is kept.  Check for the case anyway in case internal
         * properties have been modified manually.
         */
        DUK_D(DUK_DPRINT("_Formals is undefined when creating arguments, use n_formals == 0"));
        n_formals = 0;
        duk_push_undefined(thr);
    }
    i_formals = duk_require_top_index(thr);

    DUK_ASSERT(n_formals >= 0);
    DUK_ASSERT(formals != NULL || n_formals == 0);

    DUK_DDD(DUK_DDDPRINT("func=%!O, formals=%!O, n_formals=%ld",
                         (duk_heaphdr *) func, (duk_heaphdr *) formals,
                         (long) n_formals));

    /* [ ... formals ] */

    /*
     *  Create required objects:
     *    - 'arguments' object: array-like, but not an array
     *    - 'map' object: internal object, tied to 'arguments' (bare)
     *    - 'mappedNames' object: temporary value used during construction (bare)
     */

    arg = duk_push_object_helper(thr,
                                 DUK_HOBJECT_FLAG_EXTENSIBLE |
                                 DUK_HOBJECT_FLAG_FASTREFS |
                                 DUK_HOBJECT_FLAG_ARRAY_PART |
                                 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARGUMENTS),
                                 DUK_BIDX_OBJECT_PROTOTYPE);
    DUK_ASSERT(arg != NULL);
    (void) duk_push_object_helper(thr,
                                  DUK_HOBJECT_FLAG_EXTENSIBLE |
                                  DUK_HOBJECT_FLAG_FASTREFS |
                                  DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
                                  -1);  /* no prototype */
    (void) duk_push_object_helper(thr,
                                  DUK_HOBJECT_FLAG_EXTENSIBLE |
                                  DUK_HOBJECT_FLAG_FASTREFS |
                                  DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
                                  -1);  /* no prototype */
    i_arg = duk_get_top(thr) - 3;
    i_map = i_arg + 1;
    i_mappednames = i_arg + 2;
    DUK_ASSERT(!duk_is_bare_object(thr, -3));  /* arguments */
    DUK_ASSERT(duk_is_bare_object(thr, -2));  /* map */
    DUK_ASSERT(duk_is_bare_object(thr, -1));  /* mappedNames */

    /* [ ... formals arguments map mappedNames ] */

    DUK_DDD(DUK_DDDPRINT("created arguments related objects: "
                         "arguments at index %ld -> %!O "
                         "map at index %ld -> %!O "
                         "mappednames at index %ld -> %!O",
                         (long) i_arg, (duk_heaphdr *) duk_get_hobject(thr, i_arg),
                         (long) i_map, (duk_heaphdr *) duk_get_hobject(thr, i_map),
                         (long) i_mappednames, (duk_heaphdr *) duk_get_hobject(thr, i_mappednames)));

    /*
     *  Init arguments properties, map, etc.
     */

    duk_push_int(thr, num_stack_args);
    duk_xdef_prop_stridx(thr, i_arg, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_WC);

    /*
     *  Init argument related properties.
     */

    /* step 11 */
    idx = num_stack_args - 1;
    while (idx >= 0) {
        DUK_DDD(DUK_DDDPRINT("arg idx %ld, argbase=%ld, argidx=%ld",
                             (long) idx, (long) i_argbase, (long) (i_argbase + idx)));

        DUK_DDD(DUK_DDDPRINT("define arguments[%ld]=arg", (long) idx));
        duk_dup(thr, i_argbase + idx);
        duk_xdef_prop_index_wec(thr, i_arg, (duk_uarridx_t) idx);
        DUK_DDD(DUK_DDDPRINT("defined arguments[%ld]=arg", (long) idx));

        /* step 11.c is relevant only if non-strict (checked in 11.c.ii) */
        if (!DUK_HOBJECT_HAS_STRICT(func) && idx < n_formals) {
            DUK_ASSERT(formals != NULL);

            DUK_DDD(DUK_DDDPRINT("strict function, index within formals (%ld < %ld)",
                                 (long) idx, (long) n_formals));

            duk_get_prop_index(thr, i_formals, (duk_uarridx_t) idx);
            DUK_ASSERT(duk_is_string(thr, -1));

            duk_dup_top(thr);  /* [ ... name name ] */

            if (!duk_has_prop(thr, i_mappednames)) {
                /* steps 11.c.ii.1 - 11.c.ii.4, but our internal book-keeping
                 * differs from the reference model
                 */

                /* [ ... name ] */

                need_map = 1;

                DUK_DDD(DUK_DDDPRINT("set mappednames[%s]=%ld",
                                     (const char *) duk_get_string(thr, -1),
                                     (long) idx));
                duk_dup_top(thr);                      /* name */
                (void) duk_push_uint_to_hstring(thr, (duk_uint_t) idx);  /* index */
                duk_xdef_prop_wec(thr, i_mappednames);  /* out of spec, must be configurable */

                DUK_DDD(DUK_DDDPRINT("set map[%ld]=%s",
                                     (long) idx,
                                     duk_get_string(thr, -1)));
                duk_dup_top(thr);         /* name */
                duk_xdef_prop_index_wec(thr, i_map, (duk_uarridx_t) idx);  /* out of spec, must be configurable */
            } else {
                /* duk_has_prop() popped the second 'name' */
            }

            /* [ ... name ] */
            duk_pop(thr);  /* pop 'name' */
        }

        idx--;
    }

    DUK_DDD(DUK_DDDPRINT("actual arguments processed"));

    /* step 12 */
    if (need_map) {
        DUK_DDD(DUK_DDDPRINT("adding 'map' and 'varenv' to arguments object"));

        /* should never happen for a strict callee */
        DUK_ASSERT(!DUK_HOBJECT_HAS_STRICT(func));

        duk_dup(thr, i_map);
        duk_xdef_prop_stridx(thr, i_arg, DUK_STRIDX_INT_MAP, DUK_PROPDESC_FLAGS_NONE);  /* out of spec, don't care */

        /* The variable environment for magic variable bindings needs to be
         * given by the caller and recorded in the arguments object.
         *
         * See E5 Section 10.6, the creation of setters/getters.
         *
         * The variable environment also provides access to the callee, so
         * an explicit (internal) callee property is not needed.
         */

        duk_push_hobject(thr, varenv);
        duk_xdef_prop_stridx(thr, i_arg, DUK_STRIDX_INT_VARENV, DUK_PROPDESC_FLAGS_NONE);  /* out of spec, don't care */
    }

    /* steps 13-14 */
    if (DUK_HOBJECT_HAS_STRICT(func)) {
        /* Callee/caller are throwers and are not deletable etc.  They
         * could be implemented as virtual properties, but currently
         * there is no support for virtual properties which are accessors
         * (only plain virtual properties).  This would not be difficult
         * to change in duk_hobject_props, but we can make the throwers
         * normal, concrete properties just as easily.
         *
         * Note that the specification requires that the *same* thrower
         * built-in object is used here!  See E5 Section 10.6 main
         * algoritm, step 14, and Section 13.2.3 which describes the
         * thrower.  See test case test-arguments-throwers.js.
         */

        DUK_DDD(DUK_DDDPRINT("strict function, setting caller/callee to throwers"));

        /* In ES2017 .caller is no longer set at all. */
        duk_xdef_prop_stridx_thrower(thr, i_arg, DUK_STRIDX_CALLEE);
    } else {
        DUK_DDD(DUK_DDDPRINT("non-strict function, setting callee to actual value"));
        duk_push_hobject(thr, func);
        duk_xdef_prop_stridx(thr, i_arg, DUK_STRIDX_CALLEE, DUK_PROPDESC_FLAGS_WC);
    }

    /* set exotic behavior only after we're done */
    if (need_map) {
        /* Exotic behaviors are only enabled for arguments objects
         * which have a parameter map (see E5 Section 10.6 main
         * algorithm, step 12).
         *
         * In particular, a non-strict arguments object with no
         * mapped formals does *NOT* get exotic behavior, even
         * for e.g. "caller" property.  This seems counterintuitive
         * but seems to be the case.
         */

        /* cannot be strict (never mapped variables) */
        DUK_ASSERT(!DUK_HOBJECT_HAS_STRICT(func));

        DUK_DDD(DUK_DDDPRINT("enabling exotic behavior for arguments object"));
        DUK_HOBJECT_SET_EXOTIC_ARGUMENTS(arg);
    } else {
        DUK_DDD(DUK_DDDPRINT("not enabling exotic behavior for arguments object"));
    }

    DUK_DDD(DUK_DDDPRINT("final arguments related objects: "
                         "arguments at index %ld -> %!O "
                         "map at index %ld -> %!O "
                         "mappednames at index %ld -> %!O",
                         (long) i_arg, (duk_heaphdr *) duk_get_hobject(thr, i_arg),
                         (long) i_map, (duk_heaphdr *) duk_get_hobject(thr, i_map),
                         (long) i_mappednames, (duk_heaphdr *) duk_get_hobject(thr, i_mappednames)));

    /* [ args(n) envobj formals arguments map mappednames ] */

    duk_pop_2(thr);
    duk_remove_m2(thr);

    /* [ args(n) envobj arguments ] */
}

/* Helper for creating the arguments object and adding it to the env record
 * on top of the value stack.
 */
DUK_LOCAL void duk__handle_createargs_for_call(duk_hthread *thr,
                                               duk_hobject *func,
                                               duk_hobject *env,
                                               duk_idx_t idx_args) {
    DUK_DDD(DUK_DDDPRINT("creating arguments object for function call"));

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(func != NULL);
    DUK_ASSERT(env != NULL);
    DUK_ASSERT(DUK_HOBJECT_HAS_CREATEARGS(func));

    /* [ ... arg1 ... argN envobj ] */

    duk__create_arguments_object(thr,
                                 func,
                                 env,
                                 idx_args);

    /* [ ... arg1 ... argN envobj argobj ] */

    duk_xdef_prop_stridx_short(thr,
                               -2,
                               DUK_STRIDX_LC_ARGUMENTS,
                               DUK_HOBJECT_HAS_STRICT(func) ? DUK_PROPDESC_FLAGS_E :   /* strict: non-deletable, non-writable */
                                                              DUK_PROPDESC_FLAGS_WE);  /* non-strict: non-deletable, writable */
    /* [ ... arg1 ... argN envobj ] */
}

/*
 *  Helpers for constructor call handling.
 *
 *  There are two [[Construct]] operations in the specification:
 *
 *    - E5 Section 13.2.2: for Function objects
 *    - E5 Section 15.3.4.5.2: for "bound" Function objects
 *
 *  The chain of bound functions is resolved in Section 15.3.4.5.2,
 *  with arguments "piling up" until the [[Construct]] internal
 *  method is called on the final, actual Function object.  Note
 *  that the "prototype" property is looked up *only* from the
 *  final object, *before* calling the constructor.
 *
 *  Since Duktape 2.2 bound functions are represented with the
 *  duk_hboundfunc internal type, and bound function chains are
 *  collapsed when a bound function is created.  As a result, the
 *  direct target of a duk_hboundfunc is always non-bound and the
 *  this/argument lists have been resolved.
 *
 *  When constructing new Array instances, an unnecessary object is
 *  created and discarded now: the standard [[Construct]] creates an
 *  object, and calls the Array constructor.  The Array constructor
 *  returns an Array instance, which is used as the result value for
 *  the "new" operation; the object created before the Array constructor
 *  call is discarded.
 *
 *  This would be easy to fix, e.g. by knowing that the Array constructor
 *  will always create a replacement object and skip creating the fallback
 *  object in that case.
 */

/* Update default instance prototype for constructor call. */
DUK_LOCAL void duk__update_default_instance_proto(duk_hthread *thr, duk_idx_t idx_func) {
    duk_hobject *proto;
    duk_hobject *fallback;

    DUK_ASSERT(duk_is_constructable(thr, idx_func));

    duk_get_prop_stridx_short(thr, idx_func, DUK_STRIDX_PROTOTYPE);
    proto = duk_get_hobject(thr, -1);
    if (proto == NULL) {
        DUK_DDD(DUK_DDDPRINT("constructor has no 'prototype' property, or value not an object "
                             "-> leave standard Object prototype as fallback prototype"));
    } else {
        DUK_DDD(DUK_DDDPRINT("constructor has 'prototype' property with object value "
                             "-> set fallback prototype to that value: %!iO", (duk_heaphdr *) proto));
        /* Original fallback (default instance) is untouched when
         * resolving bound functions etc.
         */
        fallback = duk_known_hobject(thr, idx_func + 1);
        DUK_ASSERT(fallback != NULL);
        DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, fallback, proto);
    }
    duk_pop(thr);
}

/* Postprocess: return value special handling, error augmentation. */
DUK_INTERNAL void duk_call_construct_postprocess(duk_hthread *thr, duk_small_uint_t proxy_invariant) {
    /* Use either fallback (default instance) or retval depending
     * on retval type.  Needs to be called before unwind because
     * the default instance is read from the current (immutable)
     * 'this' binding.
     *
     * For Proxy 'construct' calls the return value must be an
     * Object (we accept object-like values like buffers and
     * lightfuncs too).  If not, TypeError.
     */
    if (duk_check_type_mask(thr, -1, DUK_TYPE_MASK_OBJECT |
                                     DUK_TYPE_MASK_BUFFER |
                                     DUK_TYPE_MASK_LIGHTFUNC)) {
        DUK_DDD(DUK_DDDPRINT("replacement value"));
    } else {
        if (DUK_UNLIKELY(proxy_invariant != 0U)) {
            /* Proxy 'construct' return value invariant violated. */
            DUK_ERROR_TYPE_INVALID_TRAP_RESULT(thr);
            DUK_WO_NORETURN(return;);
        }
        /* XXX: direct value stack access */
        duk_pop(thr);
        duk_push_this(thr);
    }

#if defined(DUK_USE_AUGMENT_ERROR_CREATE)
    /* Augment created errors upon creation, not when they are thrown or
     * rethrown.  __FILE__ and __LINE__ are not desirable here; the call
     * stack reflects the caller which is correct.  Skip topmost, unwound
     * activation when creating a traceback.  If thr->ptr_curr_pc was !=
     * NULL we'd need to sync the current PC so that the traceback comes
     * out right; however it is always synced here so just assert for it.
     */
    DUK_ASSERT(thr->ptr_curr_pc == NULL);
    duk_err_augment_error_create(thr, thr, NULL, 0, DUK_AUGMENT_FLAG_NOBLAME_FILELINE |
                                                    DUK_AUGMENT_FLAG_SKIP_ONE);
#endif
}

/*
 *  Helper for handling a bound function when a call is being made.
 *
 *  Assumes that bound function chains have been "collapsed" so that either
 *  the target is non-bound or there is one bound function that points to a
 *  nonbound target.
 *
 *  Prepends the bound arguments to the value stack (at idx_func + 2).
 *  The 'this' binding is also updated if necessary (at idx_func + 1).
 *  Note that for constructor calls the 'this' binding is never updated by
 *  [[BoundThis]].
 */

DUK_LOCAL void duk__handle_bound_chain_for_call(duk_hthread *thr,
                                                duk_idx_t idx_func,
                                                duk_bool_t is_constructor_call) {
    duk_tval *tv_func;
    duk_hobject *func;
    duk_idx_t len;

    DUK_ASSERT(thr != NULL);

    /* On entry, item at idx_func is a bound, non-lightweight function,
     * but we don't rely on that below.
     */

    DUK_ASSERT(duk_get_top(thr) >= idx_func + 2);

    tv_func = duk_require_tval(thr, idx_func);
    DUK_ASSERT(tv_func != NULL);

    if (DUK_TVAL_IS_OBJECT(tv_func)) {
        func = DUK_TVAL_GET_OBJECT(tv_func);

        /* XXX: separate helper function, out of fast path? */
        if (DUK_HOBJECT_HAS_BOUNDFUNC(func)) {
            duk_hboundfunc *h_bound;
            duk_tval *tv_args;
            duk_tval *tv_gap;

            h_bound = (duk_hboundfunc *) (void *) func;
            tv_args = h_bound->args;
            len = h_bound->nargs;
            DUK_ASSERT(len == 0 || tv_args != NULL);

            DUK_DDD(DUK_DDDPRINT("bound function encountered, ptr=%p: %!T",
                                 (void *) DUK_TVAL_GET_OBJECT(tv_func), tv_func));

            /* [ ... func this arg1 ... argN ] */

            if (is_constructor_call) {
                /* See: tests/ecmascript/test-spec-bound-constructor.js */
                DUK_DDD(DUK_DDDPRINT("constructor call: don't update this binding"));
            } else {
                /* XXX: duk_replace_tval */
                duk_push_tval(thr, &h_bound->this_binding);
                duk_replace(thr, idx_func + 1);  /* idx_this = idx_func + 1 */
            }

            /* [ ... func this arg1 ... argN ] */

            duk_require_stack(thr, len);

            tv_gap = duk_reserve_gap(thr, idx_func + 2, len);
            duk_copy_tvals_incref(thr, tv_gap, tv_args, (duk_size_t) len);

            /* [ ... func this <bound args> arg1 ... argN ] */

            duk_push_tval(thr, &h_bound->target);
            duk_replace(thr, idx_func);  /* replace in stack */

            DUK_DDD(DUK_DDDPRINT("bound function handled, idx_func=%ld, curr func=%!T",
                                 (long) idx_func, duk_get_tval(thr, idx_func)));
        }
    } else if (DUK_TVAL_IS_LIGHTFUNC(tv_func)) {
        /* Lightweight function: never bound, so terminate. */
        ;
    } else {
        /* Shouldn't happen, so ugly error is enough. */
        DUK_ERROR_INTERNAL(thr);
        DUK_WO_NORETURN(return;);
    }

    DUK_ASSERT(duk_get_top(thr) >= idx_func + 2);

    DUK_DDD(DUK_DDDPRINT("final non-bound function is: %!T", duk_get_tval(thr, idx_func)));

#if defined(DUK_USE_ASSERTIONS)
    tv_func = duk_require_tval(thr, idx_func);
    DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv_func) || DUK_TVAL_IS_OBJECT(tv_func));
    if (DUK_TVAL_IS_OBJECT(tv_func)) {
        func = DUK_TVAL_GET_OBJECT(tv_func);
        DUK_ASSERT(func != NULL);
        DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func));
        DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(func) ||
                   DUK_HOBJECT_HAS_NATFUNC(func) ||
                   DUK_HOBJECT_IS_PROXY(func));
    }
#endif
}

/*
 *  Helper for inline handling of .call(), .apply(), and .construct().
 */

DUK_LOCAL duk_bool_t duk__handle_specialfuncs_for_call(duk_hthread *thr, duk_idx_t idx_func, duk_hobject *func, duk_small_uint_t *call_flags, duk_bool_t first) {
#if defined(DUK_USE_ASSERTIONS)
    duk_c_function natfunc;
#endif
    duk_tval *tv_args;

    DUK_ASSERT(func != NULL);
    DUK_ASSERT((*call_flags & DUK_CALL_FLAG_CONSTRUCT) == 0);  /* Caller. */

#if defined(DUK_USE_ASSERTIONS)
    natfunc = ((duk_hnatfunc *) func)->func;
    DUK_ASSERT(natfunc != NULL);
#endif

    /* On every round of function resolution at least target function and
     * 'this' binding are set.  We can assume that here, and must guarantee
     * it on exit.  Value stack reserve is extended for bound function and
     * .apply() unpacking so we don't need to extend it here when we need a
     * few slots.
     */
    DUK_ASSERT(duk_get_top(thr) >= idx_func + 2);

    /* Handle native 'eval' specially.  A direct eval check is only made
     * for the first resolution attempt; e.g. a bound eval call is -not-
     * a direct eval call.
     */
    if (DUK_UNLIKELY(((duk_hnatfunc *) func)->magic == 15)) {
        /* For now no special handling except for direct eval
         * detection.
         */
        DUK_ASSERT(((duk_hnatfunc *) func)->func == duk_bi_global_object_eval);
        if (first && (*call_flags & DUK_CALL_FLAG_CALLED_AS_EVAL)) {
            *call_flags = (*call_flags & ~DUK_CALL_FLAG_CALLED_AS_EVAL) | DUK_CALL_FLAG_DIRECT_EVAL;
        }
        DUK_ASSERT(duk_get_top(thr) >= idx_func + 2);
        return 1;  /* stop resolving */
    }

    /* Handle special functions based on the DUK_HOBJECT_FLAG_SPECIAL_CALL
     * flag; their magic value is used for switch-case.
     *
     * NOTE: duk_unpack_array_like() reserves value stack space
     * for the result values (unlike most other value stack calls).
     */
    switch (((duk_hnatfunc *) func)->magic) {
    case 0: {  /* 0=Function.prototype.call() */
        /* Value stack:
         * idx_func + 0: Function.prototype.call()  [removed]
         * idx_func + 1: this binding for .call (target function)
         * idx_func + 2: 1st argument to .call, desired 'this' binding
         * idx_func + 3: 2nd argument to .call, desired 1st argument for ultimate target
         * ...
         *
         * Remove idx_func + 0 to get:
         * idx_func + 0: target function
         * idx_func + 1: this binding
         * idx_func + 2: call arguments
         * ...
         */
        DUK_ASSERT(natfunc == duk_bi_function_prototype_call);
        duk_remove_unsafe(thr, idx_func);
        tv_args = thr->valstack_bottom + idx_func + 2;
        if (thr->valstack_top < tv_args) {
            DUK_ASSERT(tv_args <= thr->valstack_end);
            thr->valstack_top = tv_args;  /* at least target function and 'this' binding present */
        }
        break;
    }
    case 1: {  /* 1=Function.prototype.apply() */
        /* Value stack:
         * idx_func + 0: Function.prototype.apply()  [removed]
         * idx_func + 1: this binding for .apply (target function)
         * idx_func + 2: 1st argument to .apply, desired 'this' binding
         * idx_func + 3: 2nd argument to .apply, argArray
         * [anything after this MUST be ignored]
         *
         * Remove idx_func + 0 and unpack the argArray to get:
         * idx_func + 0: target function
         * idx_func + 1: this binding
         * idx_func + 2: call arguments
         * ...
         */
        DUK_ASSERT(natfunc == duk_bi_function_prototype_apply);
        duk_remove_unsafe(thr, idx_func);
        goto apply_shared;
    }
#if defined(DUK_USE_REFLECT_BUILTIN)
    case 2: {  /* 2=Reflect.apply() */
        /* Value stack:
         * idx_func + 0: Reflect.apply()  [removed]
         * idx_func + 1: this binding for .apply (ignored, usually Reflect)  [removed]
         * idx_func + 2: 1st argument to .apply, target function
         * idx_func + 3: 2nd argument to .apply, desired 'this' binding
         * idx_func + 4: 3rd argument to .apply, argArray
         * [anything after this MUST be ignored]
         *
         * Remove idx_func + 0 and idx_func + 1, and unpack the argArray to get:
         * idx_func + 0: target function
         * idx_func + 1: this binding
         * idx_func + 2: call arguments
         * ...
         */
        DUK_ASSERT(natfunc == duk_bi_reflect_apply);
        duk_remove_n_unsafe(thr, idx_func, 2);
        goto apply_shared;
    }
    case 3: {  /* 3=Reflect.construct() */
        /* Value stack:
         * idx_func + 0: Reflect.construct()  [removed]
         * idx_func + 1: this binding for .construct (ignored, usually Reflect)  [removed]
         * idx_func + 2: 1st argument to .construct, target function
         * idx_func + 3: 2nd argument to .construct, argArray
         * idx_func + 4: 3rd argument to .construct, newTarget
         * [anything after this MUST be ignored]
         *
         * Remove idx_func + 0 and idx_func + 1, unpack the argArray,
         * and insert default instance (prototype not yet updated), to get:
         * idx_func + 0: target function
         * idx_func + 1: this binding (default instance)
         * idx_func + 2: constructor call arguments
         * ...
         *
         * Call flags must be updated to reflect the fact that we're
         * now dealing with a constructor call, and e.g. the 'this'
         * binding cannot be overwritten if the target is bound.
         *
         * newTarget is checked but not yet passed onwards.
         */

        duk_idx_t top;

        DUK_ASSERT(natfunc == duk_bi_reflect_construct);
        *call_flags |= DUK_CALL_FLAG_CONSTRUCT;
        duk_remove_n_unsafe(thr, idx_func, 2);
        top = duk_get_top(thr);
        if (!duk_is_constructable(thr, idx_func)) {
            /* Target constructability must be checked before
             * unpacking argArray (which may cause side effects).
             * Just return; caller will throw the error.
             */
            duk_set_top_unsafe(thr, idx_func + 2);  /* satisfy asserts */
            break;
        }
        duk_push_object(thr);
        duk_insert(thr, idx_func + 1);  /* default instance */

        /* [ ... func default_instance argArray newTarget? ] */

        top = duk_get_top(thr);
        if (top < idx_func + 3) {
            /* argArray is a mandatory argument for Reflect.construct(). */
            DUK_ERROR_TYPE_INVALID_ARGS(thr);
            DUK_WO_NORETURN(return 0;);
        }
        if (top > idx_func + 3) {
            if (!duk_strict_equals(thr, idx_func, idx_func + 3)) {
                /* XXX: [[Construct]] newTarget currently unsupported */
                DUK_ERROR_UNSUPPORTED(thr);
                DUK_WO_NORETURN(return 0;);
            }
            duk_set_top_unsafe(thr, idx_func + 3);  /* remove any args beyond argArray */
        }
        DUK_ASSERT(duk_get_top(thr) == idx_func + 3);
        DUK_ASSERT(duk_is_valid_index(thr, idx_func + 2));
        (void) duk_unpack_array_like(thr, idx_func + 2);  /* XXX: should also remove target to be symmetric with duk_pack()? */
        duk_remove(thr, idx_func + 2);
        DUK_ASSERT(duk_get_top(thr) >= idx_func + 2);
        break;
    }
#endif  /* DUK_USE_REFLECT_BUILTIN */
    default: {
        DUK_ASSERT(0);
        DUK_UNREACHABLE();
    }
    }

    DUK_ASSERT(duk_get_top(thr) >= idx_func + 2);
    return 0;  /* keep resolving */

 apply_shared:
    tv_args = thr->valstack_bottom + idx_func + 2;
    if (thr->valstack_top <= tv_args) {
        DUK_ASSERT(tv_args <= thr->valstack_end);
        thr->valstack_top = tv_args;  /* at least target func and 'this' binding present */
        /* No need to check for argArray. */
    } else {
        DUK_ASSERT(duk_get_top(thr) >= idx_func + 3);  /* idx_func + 2 covered above */
        if (thr->valstack_top > tv_args + 1) {
            duk_set_top_unsafe(thr, idx_func + 3);  /* remove any args beyond argArray */
        }
        DUK_ASSERT(duk_is_valid_index(thr, idx_func + 2));
        if (!duk_is_callable(thr, idx_func)) {
            /* Avoid unpack side effects if the target isn't callable.
             * Calling code will throw the actual error.
             */
        } else {
            (void) duk_unpack_array_like(thr, idx_func + 2);
            duk_remove(thr, idx_func + 2);
        }
    }
    DUK_ASSERT(duk_get_top(thr) >= idx_func + 2);
    return 0;  /* keep resolving */
}

/*
 *  Helper for Proxy handling.
 */

#if defined(DUK_USE_ES6_PROXY)
DUK_LOCAL void duk__handle_proxy_for_call(duk_hthread *thr, duk_idx_t idx_func, duk_hproxy *h_proxy, duk_small_uint_t *call_flags) {
    duk_bool_t rc;

    /* Value stack:
     * idx_func + 0: Proxy object
     * idx_func + 1: this binding for call
     * idx_func + 2: 1st argument for call
     * idx_func + 3: 2nd argument for call
     * ...
     *
     * If Proxy doesn't have a trap for the call ('apply' or 'construct'),
     * replace Proxy object with target object.
     *
     * If we're dealing with a normal call and the Proxy has an 'apply'
     * trap, manipulate value stack to:
     *
     * idx_func + 0: trap
     * idx_func + 1: Proxy's handler
     * idx_func + 2: Proxy's target
     * idx_func + 3: this binding for call (from idx_func + 1)
     * idx_func + 4: call arguments packed to an array
     *
     * If we're dealing with a constructor call and the Proxy has a
     * 'construct' trap, manipulate value stack to:
     *
     * idx_func + 0: trap
     * idx_func + 1: Proxy's handler
     * idx_func + 2: Proxy's target
     * idx_func + 3: call arguments packed to an array
     * idx_func + 4: newTarget == Proxy object here
     *
     * As we don't yet have proper newTarget support, the newTarget at
     * idx_func + 3 is just the original constructor being called, i.e.
     * the Proxy object (not the target).  Note that the default instance
     * (original 'this' binding) is dropped and ignored.
     */

    duk_push_hobject(thr, h_proxy->handler);
    rc = duk_get_prop_stridx_short(thr, -1, (*call_flags & DUK_CALL_FLAG_CONSTRUCT) ? DUK_STRIDX_CONSTRUCT : DUK_STRIDX_APPLY);
    if (rc == 0) {
        /* Not found, continue to target.  If this is a construct
         * call, update default instance prototype using the Proxy,
         * not the target.
         */
        if (*call_flags & DUK_CALL_FLAG_CONSTRUCT) {
            if (!(*call_flags & DUK_CALL_FLAG_DEFAULT_INSTANCE_UPDATED)) {
                *call_flags |= DUK_CALL_FLAG_DEFAULT_INSTANCE_UPDATED;
                duk__update_default_instance_proto(thr, idx_func);
            }
        }
        duk_pop_2(thr);
        duk_push_hobject(thr, h_proxy->target);
        duk_replace(thr, idx_func);
        return;
    }

    /* Here we must be careful not to replace idx_func while
     * h_proxy is still needed, otherwise h_proxy may become
     * dangling.  This could be improved e.g. using a
     * duk_pack_slice() with a freeform slice.
     */

    /* Here:
     * idx_func + 0: Proxy object
     * idx_func + 1: this binding for call
     * idx_func + 2: 1st argument for call
     * idx_func + 3: 2nd argument for call
     * ...
     * idx_func + N: handler
     * idx_func + N + 1: trap
     */

    duk_insert(thr, idx_func + 1);
    duk_insert(thr, idx_func + 2);
    duk_push_hobject(thr, h_proxy->target);
    duk_insert(thr, idx_func + 3);
    duk_pack(thr, duk_get_top(thr) - (idx_func + 5));
    DUK_ASSERT(!duk_is_bare_object(thr, -1));

    /* Here:
     * idx_func + 0: Proxy object
     * idx_func + 1: trap
     * idx_func + 2: Proxy's handler
     * idx_func + 3: Proxy's target
     * idx_func + 4: this binding for call
     * idx_func + 5: arguments array
     */
    DUK_ASSERT(duk_get_top(thr) == idx_func + 6);

    if (*call_flags & DUK_CALL_FLAG_CONSTRUCT) {
        *call_flags |= DUK_CALL_FLAG_CONSTRUCT_PROXY;  /* Enable 'construct' trap return invariant check. */
        *call_flags &= ~(DUK_CALL_FLAG_CONSTRUCT);     /* Resume as non-constructor call to the trap. */

        /* 'apply' args: target, thisArg, argArray
         * 'construct' args: target, argArray, newTarget
         */
        duk_remove(thr, idx_func + 4);
        duk_push_hobject(thr, (duk_hobject *) h_proxy);
    }

    /* Finalize value stack layout by removing Proxy reference. */
    duk_remove(thr, idx_func);
    h_proxy = NULL;  /* invalidated */
    DUK_ASSERT(duk_get_top(thr) == idx_func + 5);
}
#endif  /* DUK_USE_ES6_PROXY */

/*
 *  Helper for setting up var_env and lex_env of an activation,
 *  assuming it does NOT have the DUK_HOBJECT_FLAG_NEWENV flag.
 */

DUK_LOCAL void duk__handle_oldenv_for_call(duk_hthread *thr,
                                           duk_hobject *func,
                                           duk_activation *act) {
    duk_hcompfunc *f;
    duk_hobject *h_lex;
    duk_hobject *h_var;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(func != NULL);
    DUK_ASSERT(act != NULL);
    DUK_ASSERT(!DUK_HOBJECT_HAS_NEWENV(func));
    DUK_ASSERT(!DUK_HOBJECT_HAS_CREATEARGS(func));
    DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(func));
    DUK_UNREF(thr);

    f = (duk_hcompfunc *) func;
    h_lex = DUK_HCOMPFUNC_GET_LEXENV(thr->heap, f);
    h_var = DUK_HCOMPFUNC_GET_VARENV(thr->heap, f);
    DUK_ASSERT(h_lex != NULL);  /* Always true for closures (not for templates) */
    DUK_ASSERT(h_var != NULL);
    act->lex_env = h_lex;
    act->var_env = h_var;
    DUK_HOBJECT_INCREF(thr, h_lex);
    DUK_HOBJECT_INCREF(thr, h_var);
}

/*
 *  Helper for updating callee 'caller' property.
 */

#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
DUK_LOCAL void duk__update_func_caller_prop(duk_hthread *thr, duk_hobject *func) {
    duk_tval *tv_caller;
    duk_hobject *h_tmp;
    duk_activation *act_callee;
    duk_activation *act_caller;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(func != NULL);
    DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func));  /* bound chain resolved */
    DUK_ASSERT(thr->callstack_top >= 1);

    if (DUK_HOBJECT_HAS_STRICT(func)) {
        /* Strict functions don't get their 'caller' updated. */
        return;
    }

    DUK_ASSERT(thr->callstack_top > 0);
    act_callee = thr->callstack_curr;
    DUK_ASSERT(act_callee != NULL);
    act_caller = (thr->callstack_top >= 2 ? act_callee->parent : NULL);

    /* XXX: check .caller writability? */

    /* Backup 'caller' property and update its value. */
    tv_caller = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, func, DUK_STRIDX_CALLER);
    if (tv_caller) {
        /* If caller is global/eval code, 'caller' should be set to
         * 'null'.
         *
         * XXX: there is no exotic flag to infer this correctly now.
         * The NEWENV flag is used now which works as intended for
         * everything (global code, non-strict eval code, and functions)
         * except strict eval code.  Bound functions are never an issue
         * because 'func' has been resolved to a non-bound function.
         */

        if (act_caller != NULL) {
            /* act_caller->func may be NULL in some finalization cases,
             * just treat like we don't know the caller.
             */
            if (act_caller->func && !DUK_HOBJECT_HAS_NEWENV(act_caller->func)) {
                /* Setting to NULL causes 'caller' to be set to
                 * 'null' as desired.
                 */
                act_caller = NULL;
            }
        }

        if (DUK_TVAL_IS_OBJECT(tv_caller)) {
            h_tmp = DUK_TVAL_GET_OBJECT(tv_caller);
            DUK_ASSERT(h_tmp != NULL);
            act_callee->prev_caller = h_tmp;

            /* Previous value doesn't need refcount changes because its ownership
             * is transferred to prev_caller.
             */

            if (act_caller != NULL) {
                DUK_ASSERT(act_caller->func != NULL);
                DUK_TVAL_SET_OBJECT(tv_caller, act_caller->func);
                DUK_TVAL_INCREF(thr, tv_caller);
            } else {
                DUK_TVAL_SET_NULL(tv_caller);  /* no incref */
            }
        } else {
            /* 'caller' must only take on 'null' or function value */
            DUK_ASSERT(!DUK_TVAL_IS_HEAP_ALLOCATED(tv_caller));
            DUK_ASSERT(act_callee->prev_caller == NULL);
            if (act_caller != NULL && act_caller->func) {
                /* Tolerate act_caller->func == NULL which happens in
                 * some finalization cases; treat like unknown caller.
                 */
                DUK_TVAL_SET_OBJECT(tv_caller, act_caller->func);
                DUK_TVAL_INCREF(thr, tv_caller);
            } else {
                DUK_TVAL_SET_NULL(tv_caller);  /* no incref */
            }
        }
    }
}
#endif  /* DUK_USE_NONSTD_FUNC_CALLER_PROPERTY */

/*
 *  Shared helpers for resolving the final, non-bound target function of the
 *  call and the effective 'this' binding.  Resolves bound functions and
 *  applies .call(), .apply(), and .construct() inline.
 *
 *  Proxy traps are also handled inline so that if the target is a Proxy with
 *  a 'call' or 'construct' trap, the trap handler is called with a modified
 *  argument list.
 *
 *  Once the bound function / .call() / .apply() / .construct() sequence has
 *  been resolved, the value at idx_func + 1 may need coercion described in
 *  E5 Section 10.4.3.
 *
 *  A call that begins as a non-constructor call may be converted into a
 *  constructor call during the resolution process if Reflect.construct()
 *  is invoked.  This is handled by updating the caller's call_flags.
 *
 *  For global and eval code (E5 Sections 10.4.1 and 10.4.2), we assume
 *  that the caller has provided the correct 'this' binding explicitly
 *  when calling, i.e.:
 *
 *    - global code: this=global object
 *    - direct eval: this=copy from eval() caller's this binding
 *    - other eval:  this=global object
 *
 *  The 'this' coercion may cause a recursive function call with arbitrary
 *  side effects, because ToObject() may be called.
 */

DUK_LOCAL DUK_INLINE void duk__coerce_nonstrict_this_binding(duk_hthread *thr, duk_idx_t idx_this) {
    duk_tval *tv_this;
    duk_hobject *obj_global;

    tv_this = thr->valstack_bottom + idx_this;
    switch (DUK_TVAL_GET_TAG(tv_this)) {
    case DUK_TAG_OBJECT:
        DUK_DDD(DUK_DDDPRINT("this binding: non-strict, object -> use directly"));
        break;
    case DUK_TAG_UNDEFINED:
    case DUK_TAG_NULL:
        DUK_DDD(DUK_DDDPRINT("this binding: non-strict, undefined/null -> use global object"));
        obj_global = thr->builtins[DUK_BIDX_GLOBAL];
        /* XXX: avoid this check somehow */
        if (DUK_LIKELY(obj_global != NULL)) {
            DUK_ASSERT(!DUK_TVAL_IS_HEAP_ALLOCATED(tv_this));  /* no need to decref previous value */
            DUK_TVAL_SET_OBJECT(tv_this, obj_global);
            DUK_HOBJECT_INCREF(thr, obj_global);
        } else {
            /* This may only happen if built-ins are being "torn down".
             * This behavior is out of specification scope.
             */
            DUK_D(DUK_DPRINT("this binding: wanted to use global object, but it is NULL -> using undefined instead"));
            DUK_ASSERT(!DUK_TVAL_IS_HEAP_ALLOCATED(tv_this));  /* no need to decref previous value */
            DUK_TVAL_SET_UNDEFINED(tv_this);  /* nothing to incref */
        }
        break;
    default:
        /* Plain buffers and lightfuncs are object coerced.  Lightfuncs
         * very rarely come here however, because the call target would
         * need to be a non-strict non-lightfunc (lightfuncs are considered
         * strict) with an explicit lightfunc 'this' binding.
         */
        DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv_this));
        DUK_DDD(DUK_DDDPRINT("this binding: non-strict, not object/undefined/null -> use ToObject(value)"));
        duk_to_object(thr, idx_this);  /* may have side effects */
        break;
    }
}

DUK_LOCAL DUK_ALWAYS_INLINE duk_bool_t duk__resolve_target_fastpath_check(duk_hthread *thr, duk_idx_t idx_func, duk_hobject **out_func, duk_small_uint_t call_flags) {
#if defined(DUK_USE_PREFER_SIZE)
    DUK_UNREF(thr);
    DUK_UNREF(idx_func);
    DUK_UNREF(out_func);
    DUK_UNREF(call_flags);
#else  /* DUK_USE_PREFER_SIZE */
    duk_tval *tv_func;
    duk_hobject *func;

    if (DUK_UNLIKELY(call_flags & DUK_CALL_FLAG_CONSTRUCT)) {
        return 0;
    }

    tv_func = DUK_GET_TVAL_POSIDX(thr, idx_func);
    DUK_ASSERT(tv_func != NULL);

    if (DUK_LIKELY(DUK_TVAL_IS_OBJECT(tv_func))) {
        func = DUK_TVAL_GET_OBJECT(tv_func);
        if (DUK_HOBJECT_IS_CALLABLE(func) &&
            !DUK_HOBJECT_HAS_BOUNDFUNC(func) &&
            !DUK_HOBJECT_HAS_SPECIAL_CALL(func)) {
            *out_func = func;

            if (DUK_HOBJECT_HAS_STRICT(func)) {
                /* Strict function: no 'this' coercion. */
                return 1;
            }

            duk__coerce_nonstrict_this_binding(thr, idx_func + 1);
            return 1;
        }
    } else if (DUK_TVAL_IS_LIGHTFUNC(tv_func)) {
        *out_func = NULL;

        /* Lightfuncs are considered strict, so 'this' binding is
         * used as is.  They're never bound, always constructable,
         * and never special functions.
         */
        return 1;
    }
#endif  /* DUK_USE_PREFER_SIZE */
    return 0;  /* let slow path deal with it */
}

DUK_LOCAL duk_hobject *duk__resolve_target_func_and_this_binding(duk_hthread *thr,
                                                                 duk_idx_t idx_func,
                                                                 duk_small_uint_t *call_flags) {
    duk_tval *tv_func;
    duk_hobject *func;
    duk_bool_t first;

    DUK_ASSERT(duk_get_top(thr) >= idx_func + 2);

    for (first = 1;; first = 0) {
        DUK_ASSERT(duk_get_top(thr) >= idx_func + 2);

        tv_func = DUK_GET_TVAL_POSIDX(thr, idx_func);
        DUK_ASSERT(tv_func != NULL);

        DUK_DD(DUK_DDPRINT("target func: %!iT", tv_func));

        if (DUK_TVAL_IS_OBJECT(tv_func)) {
            func = DUK_TVAL_GET_OBJECT(tv_func);

            if (*call_flags & DUK_CALL_FLAG_CONSTRUCT) {
                if (DUK_UNLIKELY(!DUK_HOBJECT_HAS_CONSTRUCTABLE(func))) {
                    goto not_constructable;
                }
            } else {
                if (DUK_UNLIKELY(!DUK_HOBJECT_IS_CALLABLE(func))) {
                    goto not_callable;
                }
            }

            if (DUK_LIKELY(!DUK_HOBJECT_HAS_BOUNDFUNC(func) &&
                           !DUK_HOBJECT_HAS_SPECIAL_CALL(func) &&
                           !DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(func))) {
                /* Common case, so test for using a single bitfield test.
                 * Break out to handle this coercion etc.
                 */
                break;
            }

            /* XXX: could set specialcall for boundfuncs too, simplify check above */

            if (DUK_HOBJECT_HAS_BOUNDFUNC(func)) {
                DUK_ASSERT(!DUK_HOBJECT_HAS_SPECIAL_CALL(func));
                DUK_ASSERT(!DUK_HOBJECT_IS_NATFUNC(func));

                /* Callable/constructable flags are the same
                 * for the bound function and its target, so
                 * we don't need to check them here, we can
                 * check them from the target only.
                 */
                duk__handle_bound_chain_for_call(thr, idx_func, *call_flags & DUK_CALL_FLAG_CONSTRUCT);

                DUK_ASSERT(DUK_TVAL_IS_OBJECT(duk_require_tval(thr, idx_func)) ||
                           DUK_TVAL_IS_LIGHTFUNC(duk_require_tval(thr, idx_func)));
            } else {
                DUK_ASSERT(DUK_HOBJECT_HAS_SPECIAL_CALL(func));

#if defined(DUK_USE_ES6_PROXY)
                if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(func)) {
                    /* If no trap, resume processing from Proxy trap.
                     * If trap exists, helper converts call into a trap
                     * call; this may change a constructor call into a
                     * normal (non-constructor) trap call.  We must
                     * continue processing even when a trap is found as
                     * the trap may be bound.
                     */
                    duk__handle_proxy_for_call(thr, idx_func, (duk_hproxy *) func, call_flags);
                }
                else
#endif
                {
                    DUK_ASSERT(DUK_HOBJECT_IS_NATFUNC(func));
                    DUK_ASSERT(DUK_HOBJECT_HAS_CALLABLE(func));
                    DUK_ASSERT(!DUK_HOBJECT_HAS_CONSTRUCTABLE(func));
                    /* Constructable check already done above. */

                    if (duk__handle_specialfuncs_for_call(thr, idx_func, func, call_flags, first) != 0) {
                        /* Encountered native eval call, normal call
                         * context.  Break out, handle this coercion etc.
                         */
                        break;
                    }
                }
            }
            /* Retry loop. */
        } else if (DUK_TVAL_IS_LIGHTFUNC(tv_func)) {
            /* Lightfuncs are:
             *   - Always strict, so no 'this' coercion.
             *   - Always callable.
             *   - Always constructable.
             *   - Never specialfuncs.
             */
            func = NULL;
            goto finished;
        } else {
            goto not_callable;
        }
    }

    DUK_ASSERT(func != NULL);

    if (!DUK_HOBJECT_HAS_STRICT(func)) {
        /* Non-strict target needs 'this' coercion.
         * This has potential side effects invalidating
         * 'tv_func'.
         */
        duk__coerce_nonstrict_this_binding(thr, idx_func + 1);
    }
    if (*call_flags & DUK_CALL_FLAG_CONSTRUCT) {
        if (!(*call_flags & DUK_CALL_FLAG_DEFAULT_INSTANCE_UPDATED)) {
            *call_flags |= DUK_CALL_FLAG_DEFAULT_INSTANCE_UPDATED;
            duk__update_default_instance_proto(thr, idx_func);
        }
    }

 finished:

#if defined(DUK_USE_ASSERTIONS)
    {
        duk_tval *tv_tmp;

        tv_tmp = duk_get_tval(thr, idx_func);
        DUK_ASSERT(tv_tmp != NULL);

        DUK_ASSERT((DUK_TVAL_IS_OBJECT(tv_tmp) && DUK_HOBJECT_IS_CALLABLE(DUK_TVAL_GET_OBJECT(tv_tmp))) ||
                   DUK_TVAL_IS_LIGHTFUNC(tv_tmp));
        DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func));
        DUK_ASSERT(func == NULL || (DUK_HOBJECT_IS_COMPFUNC(func) ||
                                    DUK_HOBJECT_IS_NATFUNC(func)));
        DUK_ASSERT(func == NULL || (DUK_HOBJECT_HAS_CONSTRUCTABLE(func) ||
                                    (*call_flags & DUK_CALL_FLAG_CONSTRUCT) == 0));
    }
#endif

    return func;

 not_callable:
    DUK_ASSERT(tv_func != NULL);

#if defined(DUK_USE_VERBOSE_ERRORS)
    /* GETPROPC delayed error handling: when target is not callable,
     * GETPROPC replaces idx_func+0 with a non-callable wrapper object
     * with a hidden Symbol to signify it's to be handled here.  If
     * found, unwrap the original Error and throw it as is here.  The
     * hidden Symbol is only checked as an own property, not inherited
     * (which would be dangerous).
     */
    if (DUK_TVAL_IS_OBJECT(tv_func)) {
        duk_tval *tv_wrap = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, DUK_TVAL_GET_OBJECT(tv_func), DUK_STRIDX_INT_TARGET);
        if (tv_wrap != NULL) {
            DUK_DD(DUK_DDPRINT("delayed error from GETPROPC: %!T", tv_wrap));
            duk_push_tval(thr, tv_wrap);
            (void) duk_throw(thr);
            DUK_WO_NORETURN(return NULL;);
        }
    }
#endif

#if defined(DUK_USE_VERBOSE_ERRORS)
#if defined(DUK_USE_PARANOID_ERRORS)
    DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not callable", duk_get_type_name(thr, idx_func));
#else
    DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not callable", duk_push_string_tval_readable(thr, tv_func));
#endif
#else
    DUK_ERROR_TYPE(thr, DUK_STR_NOT_CALLABLE);
#endif
    DUK_WO_NORETURN(return NULL;);

 not_constructable:
    /* For now GETPROPC delayed error not needed for constructor calls. */
#if defined(DUK_USE_VERBOSE_ERRORS)
#if defined(DUK_USE_PARANOID_ERRORS)
    DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not constructable", duk_get_type_name(thr, idx_func));
#else
    DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not constructable", duk_push_string_tval_readable(thr, tv_func));
#endif
#else
    DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONSTRUCTABLE);
#endif
    DUK_WO_NORETURN(return NULL;);
}

/*
 *  Manipulate value stack so that exactly 'num_stack_rets' return
 *  values are at 'idx_retbase' in every case, assuming there are
 *  'rc' return values on top of stack.
 *
 *  This is a bit tricky, because the called C function operates in
 *  the same activation record and may have e.g. popped the stack
 *  empty (below idx_retbase).
 */

DUK_LOCAL void duk__safe_call_adjust_valstack(duk_hthread *thr, duk_idx_t idx_retbase, duk_idx_t num_stack_rets, duk_idx_t num_actual_rets) {
    duk_idx_t idx_rcbase;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(idx_retbase >= 0);
    DUK_ASSERT(num_stack_rets >= 0);
    DUK_ASSERT(num_actual_rets >= 0);

    idx_rcbase = duk_get_top(thr) - num_actual_rets;  /* base of known return values */
    if (DUK_UNLIKELY(idx_rcbase < 0)) {
        DUK_ERROR_TYPE(thr, DUK_STR_INVALID_CFUNC_RC);
        DUK_WO_NORETURN(return;);
    }

    DUK_DDD(DUK_DDDPRINT("adjust valstack after func call: "
                         "num_stack_rets=%ld, num_actual_rets=%ld, stack_top=%ld, idx_retbase=%ld, idx_rcbase=%ld",
                         (long) num_stack_rets, (long) num_actual_rets, (long) duk_get_top(thr),
                         (long) idx_retbase, (long) idx_rcbase));

    DUK_ASSERT(idx_rcbase >= 0);  /* caller must check */

    /* Space for num_stack_rets was reserved before the safe call.
     * Because value stack reserve cannot shrink except in call returns,
     * the reserve is still in place.  Adjust valstack, carefully
     * ensuring we don't overstep the reserve.
     */

    /* Match idx_rcbase with idx_retbase so that the return values
     * start at the correct index.
     */
    if (idx_rcbase > idx_retbase) {
        duk_idx_t count = idx_rcbase - idx_retbase;

        DUK_DDD(DUK_DDDPRINT("elements at/after idx_retbase have enough to cover func retvals "
                             "(idx_retbase=%ld, idx_rcbase=%ld)", (long) idx_retbase, (long) idx_rcbase));

        /* Remove values between irc_rcbase (start of intended return
         * values) and idx_retbase to lower return values to idx_retbase.
         */
        DUK_ASSERT(count > 0);
        duk_remove_n(thr, idx_retbase, count);  /* may be NORZ */
    } else {
        duk_idx_t count = idx_retbase - idx_rcbase;

        DUK_DDD(DUK_DDDPRINT("not enough elements at/after idx_retbase to cover func retvals "
                             "(idx_retbase=%ld, idx_rcbase=%ld)", (long) idx_retbase, (long) idx_rcbase));

        /* Insert 'undefined' at idx_rcbase (start of intended return
         * values) to lift return values to idx_retbase.
         */
        DUK_ASSERT(count >= 0);
        DUK_ASSERT(thr->valstack_end - thr->valstack_top >= count);  /* reserve cannot shrink */
        duk_insert_undefined_n(thr, idx_rcbase, count);
    }

    /* Chop extra retvals away / extend with undefined. */
    duk_set_top_unsafe(thr, idx_retbase + num_stack_rets);
}

/*
 *  Activation setup for tailcalls and non-tailcalls.
 */

#if defined(DUK_USE_TAILCALL)
DUK_LOCAL duk_small_uint_t duk__call_setup_act_attempt_tailcall(duk_hthread *thr,
                                                                duk_small_uint_t call_flags,
                                                                duk_idx_t idx_func,
                                                                duk_hobject *func,
                                                                duk_size_t entry_valstack_bottom_byteoff,
                                                                duk_size_t entry_valstack_end_byteoff,
                                                                duk_idx_t *out_nargs,
                                                                duk_idx_t *out_nregs,
                                                                duk_size_t *out_vs_min_bytes,
                                                                duk_activation **out_act) {
    duk_activation *act;
    duk_tval *tv1, *tv2;
    duk_idx_t idx_args;
    duk_small_uint_t flags1, flags2;
#if defined(DUK_USE_DEBUGGER_SUPPORT)
    duk_activation *prev_pause_act;
#endif

    DUK_UNREF(entry_valstack_end_byteoff);

    /* Tailcall cannot be flagged to resume calls, and a
     * previous frame must exist.
     */
    DUK_ASSERT(thr->callstack_top >= 1);

    act = thr->callstack_curr;
    DUK_ASSERT(act != NULL);
    *out_act = act;

    if (func == NULL || !DUK_HOBJECT_IS_COMPFUNC(func)) {
        DUK_DDD(DUK_DDDPRINT("tail call prevented by target not being ecma function"));
        return 0;
    }
    if (act->flags & DUK_ACT_FLAG_PREVENT_YIELD) {
        DUK_DDD(DUK_DDDPRINT("tail call prevented by current activation having DUK_ACT_FLAG_PREVENT_YIELD"));
        return 0;
    }
    /* Tailcall is only allowed if current and candidate
     * function have identical return value handling.  There
     * are three possible return value handling cases:
     *   1. Normal function call, no special return value handling.
     *   2. Constructor call, return value replacement object check.
     *   3. Proxy 'construct' trap call, return value invariant check.
     */
    flags1 = (duk_small_uint_t) ((act->flags & DUK_ACT_FLAG_CONSTRUCT) ? 1 : 0)
#if defined(DUK_USE_ES6_PROXY)
             | (duk_small_uint_t) ((act->flags & DUK_ACT_FLAG_CONSTRUCT_PROXY) ? 2 : 0)
#endif
             ;
    flags2 = (duk_small_uint_t) ((call_flags & DUK_CALL_FLAG_CONSTRUCT) ? 1 : 0)
#if defined(DUK_USE_ES6_PROXY)
             | (duk_small_uint_t) ((call_flags & DUK_CALL_FLAG_CONSTRUCT_PROXY) ? 2 : 0);
#endif
             ;
    if (flags1 != flags2) {
        DUK_DDD(DUK_DDDPRINT("tail call prevented by incompatible return value handling"));
        return 0;
    }
    DUK_ASSERT(((act->flags & DUK_ACT_FLAG_CONSTRUCT) && (call_flags & DUK_CALL_FLAG_CONSTRUCT)) ||
               (!(act->flags & DUK_ACT_FLAG_CONSTRUCT) && !(call_flags & DUK_CALL_FLAG_CONSTRUCT)));
    DUK_ASSERT(((act->flags & DUK_ACT_FLAG_CONSTRUCT_PROXY) && (call_flags & DUK_CALL_FLAG_CONSTRUCT_PROXY)) ||
               (!(act->flags & DUK_ACT_FLAG_CONSTRUCT_PROXY) && !(call_flags & DUK_CALL_FLAG_CONSTRUCT_PROXY)));
    if (DUK_HOBJECT_HAS_NOTAIL(func)) {
        /* See: test-bug-tailcall-preventyield-assert.c. */
        DUK_DDD(DUK_DDDPRINT("tail call prevented by function having a notail flag"));
        return 0;
    }

    /*
     *  Tailcall handling
     *
     *  Although the callstack entry is reused, we need to explicitly unwind
     *  the current activation (or simulate an unwind).  In particular, the
     *  current activation must be closed, otherwise something like
     *  test-bug-reduce-judofyr.js results.  Also catchers need to be unwound
     *  because there may be non-error-catching label entries in valid tail calls.
     *
     *  Special attention is needed for debugger and pause behavior when
     *  reusing an activation.
     *    - Disable StepOut processing for the activation unwind because
     *      we reuse the activation, see:
     *      https://github.com/svaarala/duktape/issues/1684.
     *    - Disable line change pause flag permanently if act == dbg_pause_act
     *      (if set) because it would no longer be relevant, see:
     *      https://github.com/svaarala/duktape/issues/1726,
     *      https://github.com/svaarala/duktape/issues/1786.
     *    - Check for function entry (e.g. StepInto) pause flag here, because
     *      the executor pause check won't trigger due to shared activation, see:
     *      https://github.com/svaarala/duktape/issues/1726.
     */

    DUK_DDD(DUK_DDDPRINT("is tail call, reusing activation at callstack top, at index %ld",
                             (long) (thr->callstack_top - 1)));

    DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func));
    DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(func));
    DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(func));
    DUK_ASSERT((act->flags & DUK_ACT_FLAG_PREVENT_YIELD) == 0);
    DUK_ASSERT(call_flags & DUK_CALL_FLAG_ALLOW_ECMATOECMA);

    /* Unwind the topmost callstack entry before reusing it.  This
     * also unwinds the catchers related to the topmost entry.
     */
    DUK_ASSERT(thr->callstack_top > 0);
    DUK_ASSERT(thr->callstack_curr != NULL);
#if defined(DUK_USE_DEBUGGER_SUPPORT)
    if (act == thr->heap->dbg_pause_act) {
        thr->heap->dbg_pause_flags &= ~DUK_PAUSE_FLAG_LINE_CHANGE;
    }

    prev_pause_act = thr->heap->dbg_pause_act;
    thr->heap->dbg_pause_act = NULL;
    if (thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_FUNC_ENTRY) {
        DUK_D(DUK_DPRINT("PAUSE TRIGGERED by function entry (tailcall)"));
        duk_debug_set_paused(thr->heap);
    }
#endif
    duk_hthread_activation_unwind_reuse_norz(thr);
#if defined(DUK_USE_DEBUGGER_SUPPORT)
    thr->heap->dbg_pause_act = prev_pause_act;
#endif
    DUK_ASSERT(act == thr->callstack_curr);

    /* XXX: We could restore the caller's value stack reserve
     * here, as if we did an actual unwind-and-call.  Without
     * the restoration, value stack reserve may remain higher
     * than would otherwise be possible until we return to a
     * non-tailcall.
     */

    /* Then reuse the unwound activation. */
    act->cat = NULL;
    act->var_env = NULL;
    act->lex_env = NULL;
    DUK_ASSERT(func != NULL);
    DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(func));
    act->func = func;  /* don't want an intermediate exposed state with func == NULL */
#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
    act->prev_caller = NULL;
#endif
    /* don't want an intermediate exposed state with invalid pc */
    act->curr_pc = DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, (duk_hcompfunc *) func);
#if defined(DUK_USE_DEBUGGER_SUPPORT)
    act->prev_line = 0;
#endif
    DUK_TVAL_SET_OBJECT(&act->tv_func, func);  /* borrowed, no refcount */
    DUK_HOBJECT_INCREF(thr, func);

    act->flags = DUK_ACT_FLAG_TAILCALLED;
    if (DUK_HOBJECT_HAS_STRICT(func)) {
        act->flags |= DUK_ACT_FLAG_STRICT;
    }
    if (call_flags & DUK_CALL_FLAG_CONSTRUCT) {
        act->flags |= DUK_ACT_FLAG_CONSTRUCT;
    }
#if defined(DUK_USE_ES6_PROXY)
    if (call_flags & DUK_CALL_FLAG_CONSTRUCT_PROXY) {
        act->flags |= DUK_ACT_FLAG_CONSTRUCT_PROXY;
    }
#endif

    DUK_ASSERT(DUK_ACT_GET_FUNC(act) == func);      /* already updated */
    DUK_ASSERT(act->var_env == NULL);
    DUK_ASSERT(act->lex_env == NULL);
    act->bottom_byteoff = entry_valstack_bottom_byteoff;  /* tail call -> reuse current "frame" */
#if 0
    /* Topmost activation retval_byteoff is considered garbage, no need to init. */
    act->retval_byteoff = 0;
#endif
    /* Filled in when final reserve is known, dummy value doesn't matter
     * even in error unwind because reserve_byteoff is only used when
     * returning to -this- activation.
     */
    act->reserve_byteoff = 0;

    /*
     *  Manipulate valstack so that args are on the current bottom and the
     *  previous caller's 'this' binding (which is the value preceding the
     *  current bottom) is replaced with the new 'this' binding:
     *
     *       [ ... this_old | (crud) func this_new arg1 ... argN ]
     *  -->  [ ... this_new | arg1 ... argN ]
     *
     *  For tail calling to work properly, the valstack bottom must not grow
     *  here; otherwise crud would accumulate on the valstack.
     */

    tv1 = thr->valstack_bottom - 1;
    tv2 = thr->valstack_bottom + idx_func + 1;
    DUK_ASSERT(tv1 >= thr->valstack && tv1 < thr->valstack_top);  /* tv1 is -below- valstack_bottom */
    DUK_ASSERT(tv2 >= thr->valstack_bottom && tv2 < thr->valstack_top);
    DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv2);  /* side effects */

    idx_args = idx_func + 2;
    duk_remove_n(thr, 0, idx_args);  /* may be NORZ */

    idx_func = 0; DUK_UNREF(idx_func);  /* really 'not applicable' anymore, should not be referenced after this */
    idx_args = 0;

    *out_nargs = ((duk_hcompfunc *) func)->nargs;
    *out_nregs = ((duk_hcompfunc *) func)->nregs;
    DUK_ASSERT(*out_nregs >= 0);
    DUK_ASSERT(*out_nregs >= *out_nargs);
    *out_vs_min_bytes = entry_valstack_bottom_byteoff + sizeof(duk_tval) * ((duk_size_t) *out_nregs + DUK_VALSTACK_INTERNAL_EXTRA);


#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
#if defined(DUK_USE_TAILCALL)
#error incorrect options: tail calls enabled with function caller property
#endif
    /* XXX: This doesn't actually work properly for tail calls, so
     * tail calls are disabled when DUK_USE_NONSTD_FUNC_CALLER_PROPERTY
     * is in use.
     */
    duk__update_func_caller_prop(thr, func);
#endif

    /* [ ... this_new | arg1 ... argN ] */

    return 1;
}
#endif  /* DUK_USE_TAILCALL */

DUK_LOCAL void duk__call_setup_act_not_tailcall(duk_hthread *thr,
                                                duk_small_uint_t call_flags,
                                                duk_idx_t idx_func,
                                                duk_hobject *func,
                                                duk_size_t entry_valstack_bottom_byteoff,
                                                duk_size_t entry_valstack_end_byteoff,
                                                duk_idx_t *out_nargs,
                                                duk_idx_t *out_nregs,
                                                duk_size_t *out_vs_min_bytes,
                                                duk_activation **out_act) {
    duk_activation *act;
    duk_activation *new_act;

    DUK_UNREF(entry_valstack_end_byteoff);

    DUK_DDD(DUK_DDDPRINT("not a tail call, pushing a new activation to callstack, to index %ld",
                         (long) (thr->callstack_top)));

    duk__call_callstack_limit_check(thr);
    new_act = duk_hthread_activation_alloc(thr);
    DUK_ASSERT(new_act != NULL);

    act = thr->callstack_curr;
    if (act != NULL) {
        /*
         *  Update return value stack index of current activation (if any).
         *
         *  Although it might seem this is not necessary (bytecode executor
         *  does this for ECMAScript-to-ECMAScript calls; other calls are
         *  handled here), this turns out to be necessary for handling yield
         *  and resume.  For them, an ECMAScript-to-native call happens, and
         *  the ECMAScript call's retval_byteoff must be set for things to work.
         */

        act->retval_byteoff = entry_valstack_bottom_byteoff + (duk_size_t) idx_func * sizeof(duk_tval);
    }

    new_act->parent = act;
    thr->callstack_curr = new_act;
    thr->callstack_top++;
    act = new_act;
    *out_act = act;

    DUK_ASSERT(thr->valstack_top > thr->valstack_bottom);  /* at least effective 'this' */
    DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func));

    act->cat = NULL;

    act->flags = 0;
    if (call_flags & DUK_CALL_FLAG_CONSTRUCT) {
        act->flags |= DUK_ACT_FLAG_CONSTRUCT;
    }
#if defined(DUK_USE_ES6_PROXY)
    if (call_flags & DUK_CALL_FLAG_CONSTRUCT_PROXY) {
        act->flags |= DUK_ACT_FLAG_CONSTRUCT_PROXY;
    }
#endif
    if (call_flags & DUK_CALL_FLAG_DIRECT_EVAL) {
        act->flags |= DUK_ACT_FLAG_DIRECT_EVAL;
    }

    /* start of arguments: idx_func + 2. */
    act->func = func;  /* NULL for lightfunc */
    if (DUK_LIKELY(func != NULL)) {
        DUK_TVAL_SET_OBJECT(&act->tv_func, func);  /* borrowed, no refcount */
        if (DUK_HOBJECT_HAS_STRICT(func)) {
            act->flags |= DUK_ACT_FLAG_STRICT;
        }
        if (DUK_HOBJECT_IS_COMPFUNC(func)) {
            *out_nargs = ((duk_hcompfunc *) func)->nargs;
            *out_nregs = ((duk_hcompfunc *) func)->nregs;
            DUK_ASSERT(*out_nregs >= 0);
            DUK_ASSERT(*out_nregs >= *out_nargs);
            *out_vs_min_bytes = entry_valstack_bottom_byteoff +
                sizeof(duk_tval) * ((duk_size_t) idx_func + 2U + (duk_size_t) *out_nregs + DUK_VALSTACK_INTERNAL_EXTRA);
        } else {
            /* True because of call target lookup checks. */
            DUK_ASSERT(DUK_HOBJECT_IS_NATFUNC(func));

            *out_nargs = ((duk_hnatfunc *) func)->nargs;
            *out_nregs = *out_nargs;
            if (*out_nargs >= 0) {
                *out_vs_min_bytes = entry_valstack_bottom_byteoff +
                    sizeof(duk_tval) * ((duk_size_t) idx_func + 2U + (duk_size_t) *out_nregs + DUK_VALSTACK_API_ENTRY_MINIMUM + DUK_VALSTACK_INTERNAL_EXTRA);
            } else {
                /* Vararg function. */
                duk_size_t valstack_top_byteoff = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - ((duk_uint8_t *) thr->valstack));
                *out_vs_min_bytes = valstack_top_byteoff +
                    sizeof(duk_tval) * (DUK_VALSTACK_API_ENTRY_MINIMUM + DUK_VALSTACK_INTERNAL_EXTRA);
            }
        }
    } else {
        duk_small_uint_t lf_flags;
        duk_tval *tv_func;

        act->flags |= DUK_ACT_FLAG_STRICT;

        tv_func = DUK_GET_TVAL_POSIDX(thr, idx_func);
        DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv_func));
        DUK_TVAL_SET_TVAL(&act->tv_func, tv_func);  /* borrowed, no refcount */

        lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv_func);
        *out_nargs = DUK_LFUNC_FLAGS_GET_NARGS(lf_flags);
        if (*out_nargs != DUK_LFUNC_NARGS_VARARGS) {
            *out_vs_min_bytes = entry_valstack_bottom_byteoff +
                sizeof(duk_tval) * ((duk_size_t) idx_func + 2U + (duk_size_t) *out_nargs + DUK_VALSTACK_API_ENTRY_MINIMUM + DUK_VALSTACK_INTERNAL_EXTRA);
        } else {
            duk_size_t valstack_top_byteoff = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - ((duk_uint8_t *) thr->valstack));
            *out_vs_min_bytes = valstack_top_byteoff +
                sizeof(duk_tval) * (DUK_VALSTACK_API_ENTRY_MINIMUM + DUK_VALSTACK_INTERNAL_EXTRA);
            *out_nargs = -1;  /* vararg */
        }
        *out_nregs = *out_nargs;
    }

    act->var_env = NULL;
    act->lex_env = NULL;
#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
    act->prev_caller = NULL;
#endif
    act->curr_pc = NULL;
#if defined(DUK_USE_DEBUGGER_SUPPORT)
    act->prev_line = 0;
#endif
    act->bottom_byteoff = entry_valstack_bottom_byteoff + sizeof(duk_tval) * ((duk_size_t) idx_func + 2U);
#if 0
    act->retval_byteoff = 0;   /* topmost activation retval_byteoff is considered garbage, no need to init */
#endif
    /* Filled in when final reserve is known, dummy value doesn't matter
     * even in error unwind because reserve_byteoff is only used when
     * returning to -this- activation.
     */
    act->reserve_byteoff = 0;  /* filled in by caller */

    /* XXX: Is this INCREF necessary? 'func' is always a borrowed
     * reference reachable through the value stack?  If changed, stack
     * unwind code also needs to be fixed to match.
     */
    DUK_HOBJECT_INCREF_ALLOWNULL(thr, func);  /* act->func */

#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
    if (func) {
        duk__update_func_caller_prop(thr, func);
    }
#endif
}

/*
 *  Environment setup.
 */

DUK_LOCAL void duk__call_env_setup(duk_hthread *thr, duk_hobject *func, duk_activation *act, duk_idx_t idx_args) {
    duk_hobject *env;

    DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func));  /* bound function has already been resolved */

    if (DUK_LIKELY(func != NULL)) {
        if (DUK_LIKELY(DUK_HOBJECT_HAS_NEWENV(func))) {
            DUK_STATS_INC(thr->heap, stats_envrec_newenv);
            if (DUK_LIKELY(!DUK_HOBJECT_HAS_CREATEARGS(func))) {
                /* Use a new environment but there's no 'arguments' object;
                 * delayed environment initialization.  This is the most
                 * common case.
                 */
                DUK_ASSERT(act->lex_env == NULL);
                DUK_ASSERT(act->var_env == NULL);
            } else {
                /* Use a new environment and there's an 'arguments' object.
                 * We need to initialize it right now.
                 */

                /* third arg: absolute index (to entire valstack) of bottom_byteoff of new activation */
                env = duk_create_activation_environment_record(thr, func, act->bottom_byteoff);
                DUK_ASSERT(env != NULL);

                /* [ ... func this arg1 ... argN envobj ] */

                DUK_ASSERT(DUK_HOBJECT_HAS_CREATEARGS(func));
                duk__handle_createargs_for_call(thr, func, env, idx_args);

                /* [ ... func this arg1 ... argN envobj ] */

                act->lex_env = env;
                act->var_env = env;
                DUK_HOBJECT_INCREF(thr, env);
                DUK_HOBJECT_INCREF(thr, env);  /* XXX: incref by count (2) directly */
                duk_pop(thr);
            }
        } else {
            /* Use existing env (e.g. for non-strict eval); cannot have
             * an own 'arguments' object (but can refer to an existing one).
             */

            DUK_ASSERT(!DUK_HOBJECT_HAS_CREATEARGS(func));

            DUK_STATS_INC(thr->heap, stats_envrec_oldenv);
            duk__handle_oldenv_for_call(thr, func, act);

            DUK_ASSERT(act->lex_env != NULL);
            DUK_ASSERT(act->var_env != NULL);
        }
    } else {
        /* Lightfuncs are always native functions and have "newenv". */
        DUK_ASSERT(act->lex_env == NULL);
        DUK_ASSERT(act->var_env == NULL);
        DUK_STATS_INC(thr->heap, stats_envrec_newenv);
    }
}

/*
 *  Misc shared helpers.
 */

/* Check thread state, update current thread. */
DUK_LOCAL void duk__call_thread_state_update(duk_hthread *thr) {
    DUK_ASSERT(thr != NULL);

    if (DUK_LIKELY(thr == thr->heap->curr_thread)) {
        if (DUK_UNLIKELY(thr->state != DUK_HTHREAD_STATE_RUNNING)) {
            /* Should actually never happen, but check anyway. */
            goto thread_state_error;
        }
    } else {
        DUK_ASSERT(thr->heap->curr_thread == NULL ||
                   thr->heap->curr_thread->state == DUK_HTHREAD_STATE_RUNNING);
        if (DUK_UNLIKELY(thr->state != DUK_HTHREAD_STATE_INACTIVE)) {
            goto thread_state_error;
        }
        DUK_HEAP_SWITCH_THREAD(thr->heap, thr);
        thr->state = DUK_HTHREAD_STATE_RUNNING;

        /* Multiple threads may be simultaneously in the RUNNING
         * state, but not in the same "resume chain".
         */
    }
    DUK_ASSERT(thr->heap->curr_thread == thr);
    DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
    return;

 thread_state_error:
    DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "invalid thread state (%ld)", (long) thr->state);
    DUK_WO_NORETURN(return;);
}

/*
 *  Main unprotected call handler, handles:
 *
 *    - All combinations of native/ECMAScript caller and native/ECMAScript
 *      target.
 *
 *    - Optimized ECMAScript-to-ECMAScript call where call handling only
 *      sets up a new duk_activation but reuses an existing bytecode executor
 *      (the caller) without native recursion.
 *
 *    - Tailcalls, where an activation is reused without increasing call
 *      stack (duk_activation) depth.
 *
 *    - Setup for an initial Duktape.Thread.resume().
 *
 *  The call handler doesn't provide any protection guarantees, protected calls
 *  must be implemented e.g. by wrapping the call in a duk_safe_call().
 *  Call setup may fail at any stage, even when the new activation is in
 *  place; the only guarantee is that the state is consistent for unwinding.
 */

DUK_LOCAL duk_int_t duk__handle_call_raw(duk_hthread *thr,
                                         duk_idx_t idx_func,
                                         duk_small_uint_t call_flags) {
#if defined(DUK_USE_ASSERTIONS)
    duk_activation *entry_act;
    duk_size_t entry_callstack_top;
#endif
    duk_size_t entry_valstack_bottom_byteoff;
    duk_size_t entry_valstack_end_byteoff;
    duk_int_t entry_call_recursion_depth;
    duk_hthread *entry_curr_thread;
    duk_uint_fast8_t entry_thread_state;
    duk_instr_t **entry_ptr_curr_pc;
    duk_idx_t idx_args;
    duk_idx_t nargs;            /* # argument registers target function wants (< 0 => "as is") */
    duk_idx_t nregs;            /* # total registers target function wants on entry (< 0 => "as is") */
    duk_size_t vs_min_bytes;    /* minimum value stack size (bytes) for handling call */
    duk_hobject *func;          /* 'func' on stack (borrowed reference) */
    duk_activation *act;
    duk_ret_t rc;
    duk_small_uint_t use_tailcall;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    /* Asserts for heap->curr_thread omitted: it may be NULL, 'thr', or
     * any other thread (e.g. when heap thread is used to run finalizers).
     */
    DUK_CTX_ASSERT_VALID(thr);
    DUK_ASSERT(duk_is_valid_index(thr, idx_func));
    DUK_ASSERT(idx_func >= 0);

    DUK_STATS_INC(thr->heap, stats_call_all);

    /* If a tail call:
     *   - an ECMAScript activation must be on top of the callstack
     *   - there cannot be any catch stack entries that would catch
     *     a return
     */
#if defined(DUK_USE_ASSERTIONS)
    if (call_flags & DUK_CALL_FLAG_TAILCALL) {
        duk_activation *tmp_act;
        duk_catcher *tmp_cat;

        DUK_ASSERT(thr->callstack_top >= 1);
        DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL);
        DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr)));

        /* No entry in the catch stack which would actually catch a
         * throw can refer to the callstack entry being reused.
         * There *can* be catch stack entries referring to the current
         * callstack entry as long as they don't catch (e.g. label sites).
         */

        tmp_act = thr->callstack_curr;
        for (tmp_cat = tmp_act->cat; tmp_cat != NULL; tmp_cat = tmp_cat->parent) {
            DUK_ASSERT(DUK_CAT_GET_TYPE(tmp_cat) == DUK_CAT_TYPE_LABEL); /* a non-catching entry */
        }
    }
#endif  /* DUK_USE_ASSERTIONS */

    /*
     *  Store entry state.
     */

#if defined(DUK_USE_ASSERTIONS)
    entry_act = thr->callstack_curr;
    entry_callstack_top = thr->callstack_top;
#endif
    entry_valstack_bottom_byteoff = (duk_size_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack);
    entry_valstack_end_byteoff = (duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack);
    entry_call_recursion_depth = thr->heap->call_recursion_depth;
    entry_curr_thread = thr->heap->curr_thread;  /* may be NULL if first call */
    entry_thread_state = thr->state;
    entry_ptr_curr_pc = thr->ptr_curr_pc;  /* may be NULL */

    /* If thr->ptr_curr_pc is set, sync curr_pc to act->pc.  Then NULL
     * thr->ptr_curr_pc so that it's not accidentally used with an incorrect
     * activation when side effects occur.
     */
    duk_hthread_sync_and_null_currpc(thr);
    DUK_ASSERT(thr->ptr_curr_pc == NULL);

    DUK_DD(DUK_DDPRINT("duk__handle_call_raw: thr=%p, idx_func=%ld, "
                       "call_flags=0x%08lx (constructor=%ld), "
                       "valstack_top=%ld, idx_func=%ld, idx_args=%ld, rec_depth=%ld/%ld, "
                       "entry_valstack_bottom_byteoff=%ld, entry_valstack_end_byteoff=%ld, "
                       "entry_call_recursion_depth=%ld, "
                       "entry_curr_thread=%p, entry_thread_state=%ld",
                       (void *) thr,
                       (long) idx_func,
                       (unsigned long) call_flags,
                       (long) ((call_flags & DUK_CALL_FLAG_CONSTRUCT) != 0 ? 1 : 0),
                       (long) duk_get_top(thr),
                       (long) idx_func,
                       (long) (idx_func + 2),
                       (long) thr->heap->call_recursion_depth,
                       (long) thr->heap->call_recursion_limit,
                       (long) entry_valstack_bottom_byteoff,
                       (long) entry_valstack_end_byteoff,
                       (long) entry_call_recursion_depth,
                       (void *) entry_curr_thread,
                       (long) entry_thread_state));

    /*
     *  Thread state check and book-keeping.
     */

    duk__call_thread_state_update(thr);

    /*
     *  Increase call recursion depth as early as possible so that if we
     *  enter a recursive call for any reason there's a backstop to native
     *  recursion.  This can happen e.g. for almost any property read
     *  because it may cause a getter call or a Proxy trap (GC and finalizers
     *  are not an issue because they are not recursive).  If we end up
     *  doing an Ecma-to-Ecma call, revert the increase.  (See GH-2032.)
     *
     *  For similar reasons, ensure there is a known value stack spare
     *  even before we actually prepare the value stack for the target
     *  function.  If this isn't done, early recursion may consume the
     *  value stack space.
     *
     *  XXX: Should bump yield preventcount early, for the same reason.
     */

    duk__call_c_recursion_limit_check(thr);
    thr->heap->call_recursion_depth++;
    duk_require_stack(thr, DUK__CALL_HANDLING_REQUIRE_STACK);

    /*
     *  Resolve final target function; handle bound functions and special
     *  functions like .call() and .apply().  Also figure out the effective
     *  'this' binding, which replaces the current value at idx_func + 1.
     */

    if (DUK_LIKELY(duk__resolve_target_fastpath_check(thr, idx_func, &func, call_flags) != 0U)) {
        DUK_DDD(DUK_DDDPRINT("fast path target resolve"));
    } else {
        DUK_DDD(DUK_DDDPRINT("slow path target resolve"));
        func = duk__resolve_target_func_and_this_binding(thr, idx_func, &call_flags);
    }
    DUK_ASSERT(duk_get_top(thr) - idx_func >= 2);  /* at least func and this present */

    DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func));
    DUK_ASSERT(func == NULL || (DUK_HOBJECT_IS_COMPFUNC(func) ||
                                DUK_HOBJECT_IS_NATFUNC(func)));

    /* [ ... func this arg1 ... argN ] */

    /*
     *  Setup a preliminary activation and figure out nargs/nregs and
     *  value stack minimum size.
     *
     *  Don't touch valstack_bottom or valstack_top yet so that Duktape API
     *  calls work normally.
     *
     *  Because 'act' is not zeroed, all fields must be filled in.
     */

    /* Should not be necessary, but initialize to silence warnings. */
    act = NULL;
    nargs = 0;
    nregs = 0;
    vs_min_bytes = 0;

#if defined(DUK_USE_TAILCALL)
    use_tailcall = (call_flags & DUK_CALL_FLAG_TAILCALL);
    if (use_tailcall) {
        use_tailcall = duk__call_setup_act_attempt_tailcall(thr,
                                                            call_flags,
                                                            idx_func,
                                                            func,
                                                            entry_valstack_bottom_byteoff,
                                                            entry_valstack_end_byteoff,
                                                            &nargs,
                                                            &nregs,
                                                            &vs_min_bytes,
                                                            &act);
    }
#else
    DUK_ASSERT((call_flags & DUK_CALL_FLAG_TAILCALL) == 0);  /* compiler ensures this */
    use_tailcall = 0;
#endif

    if (use_tailcall) {
        idx_args = 0;
        DUK_STATS_INC(thr->heap, stats_call_tailcall);
    } else {
        duk__call_setup_act_not_tailcall(thr,
                                         call_flags,
                                         idx_func,
                                         func,
                                         entry_valstack_bottom_byteoff,
                                         entry_valstack_end_byteoff,
                                         &nargs,
                                         &nregs,
                                         &vs_min_bytes,
                                         &act);
        idx_args = idx_func + 2;
    }
    /* After this point idx_func is no longer valid for tailcalls. */

    DUK_ASSERT(act != NULL);

    /* [ ... func this arg1 ... argN ] */

    /*
     *  Environment record creation and 'arguments' object creation.
     *  Named function expression name binding is handled by the
     *  compiler; the compiled function's parent env will contain
     *  the (immutable) binding already.
     *
     *  This handling is now identical for C and ECMAScript functions.
     *  C functions always have the 'NEWENV' flag set, so their
     *  environment record initialization is delayed (which is good).
     *
     *  Delayed creation (on demand) is handled in duk_js_var.c.
     */

    duk__call_env_setup(thr, func, act, idx_args);

    /* [ ... func this arg1 ... argN ] */

    /*
     *  Setup value stack: clamp to 'nargs', fill up to 'nregs',
     *  ensure value stack size matches target requirements, and
     *  switch value stack bottom.  Valstack top is kept.
     *
     *  Value stack can only grow here.
     */

    duk_valstack_grow_check_throw(thr, vs_min_bytes);
    act->reserve_byteoff = (duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack);

    if (use_tailcall) {
        DUK_ASSERT(nregs >= 0);
        DUK_ASSERT(nregs >= nargs);
        duk_set_top_and_wipe(thr, nregs, nargs);
    } else {
        if (nregs >= 0) {
            DUK_ASSERT(nregs >= nargs);
            duk_set_top_and_wipe(thr, idx_func + 2 + nregs, idx_func + 2 + nargs);
        } else {
            ;
        }
        thr->valstack_bottom = thr->valstack_bottom + idx_func + 2;
    }
    DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
    DUK_ASSERT(thr->valstack_end >= thr->valstack_top);

    /*
     *  Make the actual call.  For Ecma-to-Ecma calls detect that
     *  setup is complete, then return with a status code that allows
     *  the caller to reuse the running executor.
     */

    if (func != NULL && DUK_HOBJECT_IS_COMPFUNC(func)) {
        /*
         *  ECMAScript call.
         */

        DUK_ASSERT(func != NULL);
        DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(func));
        act->curr_pc = DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, (duk_hcompfunc *) func);

        if (call_flags & DUK_CALL_FLAG_ALLOW_ECMATOECMA) {
            DUK_DD(DUK_DDPRINT("avoid native call, use existing executor"));
            DUK_STATS_INC(thr->heap, stats_call_ecmatoecma);
            DUK_ASSERT((act->flags & DUK_ACT_FLAG_PREVENT_YIELD) == 0);
            DUK_REFZERO_CHECK_FAST(thr);
            DUK_ASSERT(thr->ptr_curr_pc == NULL);
            thr->heap->call_recursion_depth--;  /* No recursion increase for this case. */
            return 1;  /* 1=reuse executor */
        }
        DUK_ASSERT(use_tailcall == 0);

        /* duk_hthread_activation_unwind_norz() will decrease this on unwind */
        DUK_ASSERT((act->flags & DUK_ACT_FLAG_PREVENT_YIELD) == 0);
        act->flags |= DUK_ACT_FLAG_PREVENT_YIELD;
        thr->callstack_preventcount++;

        /* [ ... func this | arg1 ... argN ] ('this' must precede new bottom) */

        /*
         *  Bytecode executor call.
         *
         *  Execute bytecode, handling any recursive function calls and
         *  thread resumptions.  Returns when execution would return from
         *  the entry level activation.  When the executor returns, a
         *  single return value is left on the stack top.
         *
         *  The only possible longjmp() is an error (DUK_LJ_TYPE_THROW),
         *  other types are handled internally by the executor.
         */

        /* thr->ptr_curr_pc is set by bytecode executor early on entry */
        DUK_ASSERT(thr->ptr_curr_pc == NULL);
        DUK_DDD(DUK_DDDPRINT("entering bytecode execution"));
        duk_js_execute_bytecode(thr);
        DUK_DDD(DUK_DDDPRINT("returned from bytecode execution"));
    } else {
        /*
         *  Native call.
         */

        DUK_ASSERT(func == NULL || ((duk_hnatfunc *) func)->func != NULL);
        DUK_ASSERT(use_tailcall == 0);

        /* [ ... func this | arg1 ... argN ] ('this' must precede new bottom) */

        /* duk_hthread_activation_unwind_norz() will decrease this on unwind */
        DUK_ASSERT((act->flags & DUK_ACT_FLAG_PREVENT_YIELD) == 0);
        act->flags |= DUK_ACT_FLAG_PREVENT_YIELD;
        thr->callstack_preventcount++;

        /* For native calls must be NULL so we don't sync back */
        DUK_ASSERT(thr->ptr_curr_pc == NULL);

        /* XXX: native funcptr could come out of call setup. */
        if (func) {
            rc = ((duk_hnatfunc *) func)->func(thr);
        } else {
            duk_tval *tv_func;
            duk_c_function funcptr;

            tv_func = &act->tv_func;
            DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv_func));
            funcptr = DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv_func);
            rc = funcptr(thr);
        }

        /* Automatic error throwing, retval check. */

        if (rc == 0) {
            DUK_ASSERT(thr->valstack < thr->valstack_end);
            DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top));
            thr->valstack_top++;
        } else if (rc == 1) {
            ;
        } else if (rc < 0) {
            duk_error_throw_from_negative_rc(thr, rc);
            DUK_WO_NORETURN(return 0;);
        } else {
            DUK_ERROR_TYPE(thr, DUK_STR_INVALID_CFUNC_RC);
            DUK_WO_NORETURN(return 0;);
        }
    }
    DUK_ASSERT(thr->ptr_curr_pc == NULL);
    DUK_ASSERT(use_tailcall == 0);

    /*
     *  Constructor call post processing.
     */

#if defined(DUK_USE_ES6_PROXY)
    if (call_flags & (DUK_CALL_FLAG_CONSTRUCT | DUK_CALL_FLAG_CONSTRUCT_PROXY)) {
        duk_call_construct_postprocess(thr, call_flags & DUK_CALL_FLAG_CONSTRUCT_PROXY);
    }
#else
    if (call_flags & DUK_CALL_FLAG_CONSTRUCT) {
        duk_call_construct_postprocess(thr, 0);
    }
#endif

    /*
     *  Unwind, restore valstack bottom and other book-keeping.
     */

    DUK_ASSERT(thr->callstack_curr != NULL);
    DUK_ASSERT(thr->callstack_curr->parent == entry_act);
    DUK_ASSERT(thr->callstack_top == entry_callstack_top + 1);
    duk_hthread_activation_unwind_norz(thr);
    DUK_ASSERT(thr->callstack_curr == entry_act);
    DUK_ASSERT(thr->callstack_top == entry_callstack_top);

    thr->valstack_bottom = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + entry_valstack_bottom_byteoff);
    /* keep current valstack_top */
    DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
    DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
    DUK_ASSERT(thr->valstack_top - thr->valstack_bottom >= idx_func + 1);

    /* Return value handling. */

    /* [ ... func this (crud) retval ] */

    {
        duk_tval *tv_ret;
        duk_tval *tv_funret;

        tv_ret = thr->valstack_bottom + idx_func;
        tv_funret = thr->valstack_top - 1;
#if defined(DUK_USE_FASTINT)
        /* Explicit check for fastint downgrade. */
        DUK_TVAL_CHKFAST_INPLACE_FAST(tv_funret);
#endif
        DUK_TVAL_SET_TVAL_UPDREF(thr, tv_ret, tv_funret);  /* side effects */
    }

    duk_set_top_unsafe(thr, idx_func + 1);

    /* [ ... retval ] */

    /* Restore caller's value stack reserve (cannot fail). */
    DUK_ASSERT((duk_uint8_t *) thr->valstack + entry_valstack_end_byteoff >= (duk_uint8_t *) thr->valstack_top);
    DUK_ASSERT((duk_uint8_t *) thr->valstack + entry_valstack_end_byteoff <= (duk_uint8_t *) thr->valstack_alloc_end);
    thr->valstack_end = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + entry_valstack_end_byteoff);

    /* XXX: Trial value stack shrink would be OK here, but we'd need
     * to prevent side effects of the potential realloc.
     */

    /* Restore entry thread executor curr_pc stack frame pointer. */
    thr->ptr_curr_pc = entry_ptr_curr_pc;

    DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread);  /* may be NULL */
    thr->state = (duk_uint8_t) entry_thread_state;

    /* Disabled assert: triggered with some torture tests. */
#if 0
    DUK_ASSERT((thr->state == DUK_HTHREAD_STATE_INACTIVE && thr->heap->curr_thread == NULL) ||  /* first call */
               (thr->state == DUK_HTHREAD_STATE_INACTIVE && thr->heap->curr_thread != NULL) ||  /* other call */
               (thr->state == DUK_HTHREAD_STATE_RUNNING && thr->heap->curr_thread == thr));     /* current thread */
#endif

    thr->heap->call_recursion_depth = entry_call_recursion_depth;

    /* If the debugger is active we need to force an interrupt so that
     * debugger breakpoints are rechecked.  This is important for function
     * calls caused by side effects (e.g. when doing a DUK_OP_GETPROP), see
     * GH-303.  Only needed for success path, error path always causes a
     * breakpoint recheck in the executor.  It would be enough to set this
     * only when returning to an ECMAScript activation, but setting the flag
     * on every return should have no ill effect.
     */
#if defined(DUK_USE_DEBUGGER_SUPPORT)
    if (duk_debug_is_attached(thr->heap)) {
        DUK_DD(DUK_DDPRINT("returning with debugger enabled, force interrupt"));
        DUK_ASSERT(thr->interrupt_counter <= thr->interrupt_init);
        thr->interrupt_init -= thr->interrupt_counter;
        thr->interrupt_counter = 0;
        thr->heap->dbg_force_restart = 1;
    }
#endif

#if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG)
    duk__interrupt_fixup(thr, entry_curr_thread);
#endif

    /* Restored by success path. */
    DUK_ASSERT(thr->heap->call_recursion_depth == entry_call_recursion_depth);
    DUK_ASSERT(thr->ptr_curr_pc == entry_ptr_curr_pc);
    DUK_ASSERT_LJSTATE_UNSET(thr->heap);

    DUK_REFZERO_CHECK_FAST(thr);

    return 0;  /* 0=call handled inline */
}

DUK_INTERNAL duk_int_t duk_handle_call_unprotected_nargs(duk_hthread *thr,
                                                         duk_idx_t nargs,
                                                         duk_small_uint_t call_flags) {
    duk_idx_t idx_func;
    DUK_ASSERT(duk_get_top(thr) >= nargs + 2);
    idx_func = duk_get_top(thr) - (nargs + 2);
    DUK_ASSERT(idx_func >= 0);
    return duk_handle_call_unprotected(thr, idx_func, call_flags);
}

DUK_INTERNAL duk_int_t duk_handle_call_unprotected(duk_hthread *thr,
                                                   duk_idx_t idx_func,
                                                   duk_small_uint_t call_flags) {
    DUK_ASSERT(duk_is_valid_index(thr, idx_func));
    DUK_ASSERT(idx_func >= 0);
    return duk__handle_call_raw(thr, idx_func, call_flags);
}

/*
 *  duk_handle_safe_call(): make a "C protected call" within the
 *  current activation.
 *
 *  The allowed thread states for making a call are the same as for
 *  duk_handle_call_protected().
 *
 *  Even though this call is protected, errors are thrown for insane arguments
 *  and may result in a fatal error unless there's another protected call which
 *  catches such errors.
 *
 *  The error handling path should be error free, even for out-of-memory
 *  errors, to ensure safe sandboxing.  (As of Duktape 2.2.0 this is not
 *  yet the case for environment closing which may run out of memory, see
 *  XXX notes below.)
 */

DUK_LOCAL void duk__handle_safe_call_inner(duk_hthread *thr,
                                           duk_safe_call_function func,
                                           void *udata,
#if defined(DUK_USE_ASSERTIONS)
                                           duk_size_t entry_valstack_bottom_byteoff,
                                           duk_size_t entry_callstack_top,
#endif
                                           duk_hthread *entry_curr_thread,
                                           duk_uint_fast8_t entry_thread_state,
                                           duk_idx_t idx_retbase,
                                           duk_idx_t num_stack_rets) {
    duk_ret_t rc;

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

    /*
     *  Thread state check and book-keeping.
     */

    duk__call_thread_state_update(thr);

    /*
     *  Recursion limit check.
     */

    duk__call_c_recursion_limit_check(thr);
    thr->heap->call_recursion_depth++;

    /*
     *  Make the C call.
     */

    rc = func(thr, udata);

    DUK_DDD(DUK_DDDPRINT("safe_call, func rc=%ld", (long) rc));

    /*
     *  Valstack manipulation for results.
     */

    /* we're running inside the caller's activation, so no change in call/catch stack or valstack bottom */
    DUK_ASSERT(thr->callstack_top == entry_callstack_top);
    DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
    DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack) == entry_valstack_bottom_byteoff);
    DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
    DUK_ASSERT(thr->valstack_end >= thr->valstack_top);

    if (DUK_UNLIKELY(rc < 0)) {
        duk_error_throw_from_negative_rc(thr, rc);
        DUK_WO_NORETURN(return;);
    }
    DUK_ASSERT(rc >= 0);

    duk__safe_call_adjust_valstack(thr, idx_retbase, num_stack_rets, rc);  /* throws for insane rc */

    DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread);  /* may be NULL */
    thr->state = (duk_uint8_t) entry_thread_state;
}

DUK_LOCAL void duk__handle_safe_call_error(duk_hthread *thr,
                                           duk_activation *entry_act,
#if defined(DUK_USE_ASSERTIONS)
                                           duk_size_t entry_callstack_top,
#endif
                                           duk_hthread *entry_curr_thread,
                                           duk_uint_fast8_t entry_thread_state,
                                           duk_idx_t idx_retbase,
                                           duk_idx_t num_stack_rets,
                                           duk_size_t entry_valstack_bottom_byteoff,
                                           duk_jmpbuf *old_jmpbuf_ptr) {
    DUK_ASSERT(thr != NULL);
    DUK_CTX_ASSERT_VALID(thr);

    /*
     *  Error during call.  The error value is at heap->lj.value1.
     *
     *  The very first thing we do is restore the previous setjmp catcher.
     *  This means that any error in error handling will propagate outwards
     *  instead of causing a setjmp() re-entry above.
     */

    DUK_DDD(DUK_DDDPRINT("error caught during protected duk_handle_safe_call()"));

    /* Other longjmp types are handled by executor before propagating
     * the error here.
     */
    DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_THROW);
    DUK_ASSERT_LJSTATE_SET(thr->heap);

    /* Either pointer may be NULL (at entry), so don't assert. */
    thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr;

    /* XXX: callstack unwind may now throw an error when closing
     * scopes; this is a sandboxing issue, described in:
     * https://github.com/svaarala/duktape/issues/476
     */
    /* XXX: "unwind to" primitive? */

    DUK_ASSERT(thr->callstack_top >= entry_callstack_top);
    while (thr->callstack_curr != entry_act) {
        DUK_ASSERT(thr->callstack_curr != NULL);
        duk_hthread_activation_unwind_norz(thr);
    }
    DUK_ASSERT(thr->callstack_top == entry_callstack_top);

    /* Switch active thread before any side effects to avoid a
     * dangling curr_thread pointer.
     */
    DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread);  /* may be NULL */
    thr->state = (duk_uint8_t) entry_thread_state;

    DUK_ASSERT(thr->heap->curr_thread == entry_curr_thread);
    DUK_ASSERT(thr->state == entry_thread_state);

    /* Restore valstack bottom. */
    thr->valstack_bottom = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + entry_valstack_bottom_byteoff);

    /* [ ... | (crud) ] */

    /* XXX: ensure space in valstack (now relies on internal reserve)? */
    duk_push_tval(thr, &thr->heap->lj.value1);

    /* [ ... | (crud) errobj ] */

    DUK_ASSERT(duk_get_top(thr) >= 1);  /* at least errobj must be on stack */

    duk__safe_call_adjust_valstack(thr, idx_retbase, num_stack_rets, 1);  /* 1 = num actual 'return values' */

    /* [ ... | ] or [ ... | errobj (M * undefined)] where M = num_stack_rets - 1 */

    /* Reset longjmp state. */
    thr->heap->lj.type = DUK_LJ_TYPE_UNKNOWN;
    thr->heap->lj.iserror = 0;
    DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ(thr, &thr->heap->lj.value1);
    DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ(thr, &thr->heap->lj.value2);

    /* Error handling complete, remove side effect protections.  Caller
     * will process pending finalizers.
     */
#if defined(DUK_USE_ASSERTIONS)
    DUK_ASSERT(thr->heap->error_not_allowed == 1);
    thr->heap->error_not_allowed = 0;
#endif
    DUK_ASSERT(thr->heap->pf_prevent_count > 0);
    thr->heap->pf_prevent_count--;
    DUK_DD(DUK_DDPRINT("safe call error handled, pf_prevent_count updated to %ld", (long) thr->heap->pf_prevent_count));

    /* thr->ptr_curr_pc is restored by
     * duk__handle_safe_call_shared_unwind() which is also used for
     * success path.
     */
}

DUK_LOCAL void duk__handle_safe_call_shared_unwind(duk_hthread *thr,
                                                   duk_idx_t idx_retbase,
                                                   duk_idx_t num_stack_rets,
#if defined(DUK_USE_ASSERTIONS)
                                                   duk_size_t entry_callstack_top,
#endif
                                                   duk_int_t entry_call_recursion_depth,
                                                   duk_hthread *entry_curr_thread,
                                                   duk_instr_t **entry_ptr_curr_pc) {
    DUK_ASSERT(thr != NULL);
    DUK_CTX_ASSERT_VALID(thr);
    DUK_UNREF(idx_retbase);
    DUK_UNREF(num_stack_rets);
    DUK_UNREF(entry_curr_thread);

    DUK_ASSERT(thr->callstack_top == entry_callstack_top);

    /* Restore entry thread executor curr_pc stack frame pointer.
     * XXX: would be enough to do in error path only, should nest
     * cleanly in success path.
     */
    thr->ptr_curr_pc = entry_ptr_curr_pc;

    thr->heap->call_recursion_depth = entry_call_recursion_depth;

    /* stack discipline consistency check */
    DUK_ASSERT(duk_get_top(thr) == idx_retbase + num_stack_rets);

    /* A debugger forced interrupt check is not needed here, as
     * problematic safe calls are not caused by side effects.
     */

#if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG)
    duk__interrupt_fixup(thr, entry_curr_thread);
#endif
}

DUK_INTERNAL duk_int_t duk_handle_safe_call(duk_hthread *thr,
                                            duk_safe_call_function func,
                                            void *udata,
                                            duk_idx_t num_stack_args,
                                            duk_idx_t num_stack_rets) {
    duk_activation *entry_act;
    duk_size_t entry_valstack_bottom_byteoff;
#if defined(DUK_USE_ASSERTIONS)
    duk_size_t entry_valstack_end_byteoff;
    duk_size_t entry_callstack_top;
    duk_size_t entry_callstack_preventcount;
#endif
    duk_int_t entry_call_recursion_depth;
    duk_hthread *entry_curr_thread;
    duk_uint_fast8_t entry_thread_state;
    duk_instr_t **entry_ptr_curr_pc;
    duk_jmpbuf *old_jmpbuf_ptr = NULL;
    duk_jmpbuf our_jmpbuf;
    duk_idx_t idx_retbase;
    duk_int_t retval;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(duk_get_top(thr) >= num_stack_args);  /* Caller ensures. */

    DUK_STATS_INC(thr->heap, stats_safecall_all);

    /* Value stack reserve handling: safe call assumes caller has reserved
     * space for nrets (assuming optimal unwind processing).  Value stack
     * reserve is not stored/restored as for normal calls because a safe
     * call conceptually happens in the same activation.
     */

    /* Careful with indices like '-x'; if 'x' is zero, it refers to bottom */
    entry_act = thr->callstack_curr;
    entry_valstack_bottom_byteoff = (duk_size_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack);
#if defined(DUK_USE_ASSERTIONS)
    entry_valstack_end_byteoff = (duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack);
    entry_callstack_top = thr->callstack_top;
    entry_callstack_preventcount = thr->callstack_preventcount;
#endif
    entry_call_recursion_depth = thr->heap->call_recursion_depth;
    entry_curr_thread = thr->heap->curr_thread;  /* may be NULL if first call */
    entry_thread_state = thr->state;
    entry_ptr_curr_pc = thr->ptr_curr_pc;  /* may be NULL */
    idx_retbase = duk_get_top(thr) - num_stack_args;  /* not a valid stack index if num_stack_args == 0 */
    DUK_ASSERT(idx_retbase >= 0);

    DUK_ASSERT((duk_idx_t) (thr->valstack_top - thr->valstack_bottom) >= num_stack_args);  /* Caller ensures. */
    DUK_ASSERT((duk_idx_t) (thr->valstack_end - (thr->valstack_bottom + idx_retbase)) >= num_stack_rets);  /* Caller ensures. */

    /* Cannot portably debug print a function pointer, hence 'func' not printed! */
    DUK_DD(DUK_DDPRINT("duk_handle_safe_call: thr=%p, num_stack_args=%ld, num_stack_rets=%ld, "
                       "valstack_top=%ld, idx_retbase=%ld, rec_depth=%ld/%ld, "
                       "entry_act=%p, entry_valstack_bottom_byteoff=%ld, entry_call_recursion_depth=%ld, "
                       "entry_curr_thread=%p, entry_thread_state=%ld",
                       (void *) thr,
                       (long) num_stack_args,
                       (long) num_stack_rets,
                       (long) duk_get_top(thr),
                       (long) idx_retbase,
                       (long) thr->heap->call_recursion_depth,
                       (long) thr->heap->call_recursion_limit,
                       (void *) entry_act,
                       (long) entry_valstack_bottom_byteoff,
                       (long) entry_call_recursion_depth,
                       (void *) entry_curr_thread,
                       (long) entry_thread_state));

    /* Setjmp catchpoint setup. */
    old_jmpbuf_ptr = thr->heap->lj.jmpbuf_ptr;
    thr->heap->lj.jmpbuf_ptr = &our_jmpbuf;

    /* Prevent yields for the duration of the safe call.  This only
     * matters if the executor makes safe calls to functions that
     * yield, this doesn't currently happen.
     */
    thr->callstack_preventcount++;

#if defined(DUK_USE_CPP_EXCEPTIONS)
    try {
#else
    DUK_ASSERT(thr->heap->lj.jmpbuf_ptr == &our_jmpbuf);
    if (DUK_SETJMP(our_jmpbuf.jb) == 0) {
        /* Success path. */
#endif
        DUK_DDD(DUK_DDDPRINT("safe_call setjmp catchpoint setup complete"));

        duk__handle_safe_call_inner(thr,
                                    func,
                                    udata,
#if defined(DUK_USE_ASSERTIONS)
                                    entry_valstack_bottom_byteoff,
                                    entry_callstack_top,
#endif
                                    entry_curr_thread,
                                    entry_thread_state,
                                    idx_retbase,
                                    num_stack_rets);

        DUK_STATS_INC(thr->heap, stats_safecall_nothrow);

        /* Either pointer may be NULL (at entry), so don't assert */
        thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr;

        /* If calls happen inside the safe call, these are restored by
         * whatever calls are made.  Reserve cannot decrease.
         */
        DUK_ASSERT(thr->callstack_curr == entry_act);
        DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack) >= entry_valstack_end_byteoff);

        retval = DUK_EXEC_SUCCESS;
#if defined(DUK_USE_CPP_EXCEPTIONS)
    } catch (duk_internal_exception &exc) {
        DUK_UNREF(exc);
#else
    } else {
        /* Error path. */
#endif
        DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack) >= entry_valstack_end_byteoff);

        DUK_STATS_INC(thr->heap, stats_safecall_throw);

        duk__handle_safe_call_error(thr,
                                    entry_act,
#if defined(DUK_USE_ASSERTIONS)
                                    entry_callstack_top,
#endif
                                    entry_curr_thread,
                                    entry_thread_state,
                                    idx_retbase,
                                    num_stack_rets,
                                    entry_valstack_bottom_byteoff,
                                    old_jmpbuf_ptr);

        retval = DUK_EXEC_ERROR;
    }
#if defined(DUK_USE_CPP_EXCEPTIONS)
    catch (duk_fatal_exception &exc) {
        DUK_D(DUK_DPRINT("rethrow duk_fatal_exception"));
        DUK_UNREF(exc);
        throw;
    } catch (std::exception &exc) {
        const char *what = exc.what();
        DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack) >= entry_valstack_end_byteoff);
        DUK_STATS_INC(thr->heap, stats_safecall_throw);
        if (!what) {
            what = "unknown";
        }
        DUK_D(DUK_DPRINT("unexpected c++ std::exception (perhaps thrown by user code)"));
        try {
            DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "caught invalid c++ std::exception '%s' (perhaps thrown by user code)", what);
            DUK_WO_NORETURN(return 0;);
        } catch (duk_internal_exception exc) {
            DUK_D(DUK_DPRINT("caught api error thrown from unexpected c++ std::exception"));
            DUK_UNREF(exc);
            duk__handle_safe_call_error(thr,
                                        entry_act,
#if defined(DUK_USE_ASSERTIONS)
                                        entry_callstack_top,
#endif
                                        entry_curr_thread,
                                        entry_thread_state,
                                        idx_retbase,
                                        num_stack_rets,
                                        entry_valstack_bottom_byteoff,
                                        old_jmpbuf_ptr);
            retval = DUK_EXEC_ERROR;
        }
    } catch (...) {
        DUK_D(DUK_DPRINT("unexpected c++ exception (perhaps thrown by user code)"));
        DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack) >= entry_valstack_end_byteoff);
        DUK_STATS_INC(thr->heap, stats_safecall_throw);
        try {
            DUK_ERROR_TYPE(thr, "caught invalid c++ exception (perhaps thrown by user code)");
            DUK_WO_NORETURN(return 0;);
        } catch (duk_internal_exception exc) {
            DUK_D(DUK_DPRINT("caught api error thrown from unexpected c++ exception"));
            DUK_UNREF(exc);
            duk__handle_safe_call_error(thr,
                                        entry_act,
#if defined(DUK_USE_ASSERTIONS)
                                        entry_callstack_top,
#endif
                                        entry_curr_thread,
                                        entry_thread_state,
                                        idx_retbase,
                                        num_stack_rets,
                                        entry_valstack_bottom_byteoff,
                                        old_jmpbuf_ptr);
            retval = DUK_EXEC_ERROR;
        }
    }
#endif

    DUK_ASSERT(thr->heap->lj.jmpbuf_ptr == old_jmpbuf_ptr);  /* success/error path both do this */

    DUK_ASSERT_LJSTATE_UNSET(thr->heap);

    DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack) >= entry_valstack_end_byteoff);
    duk__handle_safe_call_shared_unwind(thr,
                                        idx_retbase,
                                        num_stack_rets,
#if defined(DUK_USE_ASSERTIONS)
                                        entry_callstack_top,
#endif
                                        entry_call_recursion_depth,
                                        entry_curr_thread,
                                        entry_ptr_curr_pc);

    /* Restore preventcount. */
    thr->callstack_preventcount--;
    DUK_ASSERT(thr->callstack_preventcount == entry_callstack_preventcount);

    /* Final asserts. */
    DUK_ASSERT(thr->callstack_curr == entry_act);
    DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack) == entry_valstack_bottom_byteoff);
    DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack) >= entry_valstack_end_byteoff);
    DUK_ASSERT(thr->callstack_top == entry_callstack_top);
    DUK_ASSERT(thr->heap->call_recursion_depth == entry_call_recursion_depth);
    DUK_ASSERT(thr->heap->curr_thread == entry_curr_thread);
    DUK_ASSERT(thr->state == entry_thread_state);
    DUK_ASSERT(thr->ptr_curr_pc == entry_ptr_curr_pc);
    DUK_ASSERT(duk_get_top(thr) == idx_retbase + num_stack_rets);
    DUK_ASSERT_LJSTATE_UNSET(thr->heap);

    /* Pending side effects. */
    DUK_REFZERO_CHECK_FAST(thr);

    return retval;
}

/*
 *  Property-based call (foo.noSuch()) error setup: replace target function
 *  on stack top with a hidden Symbol tagged non-callable wrapper object
 *  holding the error.  The error gets thrown in call handling at the
 *  proper spot to follow ECMAScript semantics.
 */

#if defined(DUK_USE_VERBOSE_ERRORS)
DUK_INTERNAL DUK_NOINLINE DUK_COLD void duk_call_setup_propcall_error(duk_hthread *thr, duk_tval *tv_base, duk_tval *tv_key) {
    const char *str_targ, *str_key, *str_base;
    duk_idx_t entry_top;

    entry_top = duk_get_top(thr);

    /* [ <nargs> target ] */

    /* Must stabilize pointers first.  tv_targ is already on stack top. */
    duk_push_tval(thr, tv_base);
    duk_push_tval(thr, tv_key);

    DUK_GC_TORTURE(thr->heap);

    duk_push_bare_object(thr);

    /* [ <nargs> target base key {} ] */

    /* We only push a wrapped error, replacing the call target (at
     * idx_func) with the error to ensure side effects come out
     * correctly:
     * - Property read
     * - Call argument evaluation
     * - Callability check and error thrown
     *
     * A hidden Symbol on the wrapper object pushed above is used by
     * call handling to figure out the error is to be thrown as is.
     * It is CRITICAL that the hidden Symbol can never occur on a
     * user visible object that may get thrown.
     */

#if defined(DUK_USE_PARANOID_ERRORS)
    str_targ = duk_get_type_name(thr, -4);
    str_key = duk_get_type_name(thr, -2);
    str_base = duk_get_type_name(thr, -3);
    duk_push_error_object(thr,
                          DUK_ERR_TYPE_ERROR | DUK_ERRCODE_FLAG_NOBLAME_FILELINE,
                          "%s not callable (property %s of %s)", str_targ, str_key, str_base);
    duk_xdef_prop_stridx(thr, -2, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE);  /* Marker property, reuse _Target. */
    /* [ <nargs> target base key { _Target: error } ] */
    duk_replace(thr, entry_top - 1);
#else
    str_targ = duk_push_string_readable(thr, -4);
    str_key = duk_push_string_readable(thr, -3);
    str_base = duk_push_string_readable(thr, -5);
    duk_push_error_object(thr,
                          DUK_ERR_TYPE_ERROR | DUK_ERRCODE_FLAG_NOBLAME_FILELINE,
                          "%s not callable (property %s of %s)", str_targ, str_key, str_base);
    /* [ <nargs> target base key {} str_targ str_key str_base error ] */
    duk_xdef_prop_stridx(thr, -5, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE);  /* Marker property, reuse _Target. */
    /* [ <nargs> target base key { _Target: error } str_targ str_key str_base ] */
    duk_swap(thr, -4, entry_top - 1);
    /* [ <nargs> { _Target: error } base key target str_targ str_key str_base ] */
#endif

    /* [ <nregs> { _Target: error } <variable> */
    duk_set_top(thr, entry_top);

    /* [ <nregs> { _Target: error } */
    DUK_ASSERT(!duk_is_callable(thr, -1));  /* Critical so that call handling will throw the error. */
}
#endif  /* DUK_USE_VERBOSE_ERRORS */

/* automatic undefs */
#undef DUK__AUGMENT_CALL_RELAX_COUNT
#undef DUK__CALL_HANDLING_REQUIRE_STACK
//#line 1 "duk_js_compiler.c"
/*
 *  ECMAScript compiler.
 *
 *  Parses an input string and generates a function template result.
 *  Compilation may happen in multiple contexts (global code, eval
 *  code, function code).
 *
 *  The parser uses a traditional top-down recursive parsing for the
 *  statement level, and an operator precedence based top-down approach
 *  for the expression level.  The attempt is to minimize the C stack
 *  depth.  Bytecode is generated directly without an intermediate
 *  representation (tree), at the cost of needing two (and sometimes
 *  three) passes over each function.
 *
 *  The top-down recursive parser functions are named "duk__parse_XXX".
 *
 *  Recursion limits are in key functions to prevent arbitrary C recursion:
 *  function body parsing, statement parsing, and expression parsing.
 *
 *  See doc/compiler.rst for discussion on the design.
 *
 *  A few typing notes:
 *
 *    - duk_regconst_t: signed, highest bit set (< 0) means constant,
 *      some call sites use -1 for "none" (equivalent to constant 0x7fffffff)
 *    - PC values: duk_int_t, negative values used as markers
 */

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

/* If highest bit of a register number is set, it refers to a constant instead.
 * When interpreted as a signed value, this means const values are always
 * negative (when interpreted as two's complement).  For example DUK__ISREG_TEMP()
 * uses this approach to avoid an explicit DUK__ISREG() check (the condition is
 * logically "'x' is a register AND 'x' >= temp_first").
 */
#define DUK__CONST_MARKER                   DUK_REGCONST_CONST_MARKER
#define DUK__REMOVECONST(x)                 ((x) & ~DUK__CONST_MARKER)
#define DUK__ISREG(x)                       ((x) >= 0)
#define DUK__ISCONST(x)                     ((x) < 0)
#define DUK__ISREG_TEMP(comp_ctx,x)         ((duk_int32_t) (x) >= (duk_int32_t) ((comp_ctx)->curr_func.temp_first))   /* Check for x >= temp_first && x >= 0 by comparing as signed. */
#define DUK__ISREG_NOTTEMP(comp_ctx,x)      ((duk_uint32_t) (x) < (duk_uint32_t) ((comp_ctx)->curr_func.temp_first))  /* Check for x >= 0 && x < temp_first by interpreting as unsigned. */
#define DUK__GETTEMP(comp_ctx)              ((comp_ctx)->curr_func.temp_next)
#define DUK__SETTEMP(comp_ctx,x)            ((comp_ctx)->curr_func.temp_next = (x))  /* dangerous: must only lower (temp_max not updated) */
#define DUK__SETTEMP_CHECKMAX(comp_ctx,x)   duk__settemp_checkmax((comp_ctx),(x))
#define DUK__ALLOCTEMP(comp_ctx)            duk__alloctemp((comp_ctx))
#define DUK__ALLOCTEMPS(comp_ctx,count)     duk__alloctemps((comp_ctx),(count))

/* Init value set size for array and object literals. */
#define DUK__MAX_ARRAY_INIT_VALUES        20
#define DUK__MAX_OBJECT_INIT_PAIRS        10

/* XXX: hack, remove when const lookup is not O(n) */
#define DUK__GETCONST_MAX_CONSTS_CHECK    256

/* These limits are based on bytecode limits.  Max temps is limited
 * by duk_hcompfunc nargs/nregs fields being 16 bits.
 */
#define DUK__MAX_CONSTS                   DUK_BC_BC_MAX
#define DUK__MAX_FUNCS                    DUK_BC_BC_MAX
#define DUK__MAX_TEMPS                    0xffffL

/* Initial bytecode size allocation. */
#if defined(DUK_USE_PREFER_SIZE)
#define DUK__BC_INITIAL_INSTS             16
#else
#define DUK__BC_INITIAL_INSTS             256
#endif

#define DUK__RECURSION_INCREASE(comp_ctx,thr)  do { \
        DUK_DDD(DUK_DDDPRINT("RECURSION INCREASE: %s:%ld", (const char *) DUK_FILE_MACRO, (long) DUK_LINE_MACRO)); \
        duk__comp_recursion_increase((comp_ctx)); \
    } while (0)

#define DUK__RECURSION_DECREASE(comp_ctx,thr)  do { \
        DUK_DDD(DUK_DDDPRINT("RECURSION DECREASE: %s:%ld", (const char *) DUK_FILE_MACRO, (long) DUK_LINE_MACRO)); \
        duk__comp_recursion_decrease((comp_ctx)); \
    } while (0)

/* Value stack slot limits: these are quite approximate right now, and
 * because they overlap in control flow, some could be eliminated.
 */
#define DUK__COMPILE_ENTRY_SLOTS          8
#define DUK__FUNCTION_INIT_REQUIRE_SLOTS  16
#define DUK__FUNCTION_BODY_REQUIRE_SLOTS  16
#define DUK__PARSE_STATEMENTS_SLOTS       16
#define DUK__PARSE_EXPR_SLOTS             16

/* Temporary structure used to pass a stack allocated region through
 * duk_safe_call().
 */
typedef struct {
    duk_small_uint_t flags;
    duk_compiler_ctx comp_ctx_alloc;
    duk_lexer_point lex_pt_alloc;
} duk__compiler_stkstate;

/*
 *  Prototypes
 */

/* lexing */
DUK_LOCAL_DECL void duk__advance_helper(duk_compiler_ctx *comp_ctx, duk_small_int_t expect);
DUK_LOCAL_DECL void duk__advance_expect(duk_compiler_ctx *comp_ctx, duk_small_int_t expect);
DUK_LOCAL_DECL void duk__advance(duk_compiler_ctx *ctx);

/* function helpers */
DUK_LOCAL_DECL void duk__init_func_valstack_slots(duk_compiler_ctx *comp_ctx);
DUK_LOCAL_DECL void duk__reset_func_for_pass2(duk_compiler_ctx *comp_ctx);
DUK_LOCAL_DECL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ctx, duk_regconst_t *out_stmt_value_reg);
DUK_LOCAL_DECL void duk__convert_to_func_template(duk_compiler_ctx *comp_ctx);
DUK_LOCAL_DECL duk_int_t duk__cleanup_varmap(duk_compiler_ctx *comp_ctx);

/* code emission */
DUK_LOCAL_DECL duk_int_t duk__get_current_pc(duk_compiler_ctx *comp_ctx);
DUK_LOCAL_DECL duk_compiler_instr *duk__get_instr_ptr(duk_compiler_ctx *comp_ctx, duk_int_t pc);
DUK_LOCAL_DECL void duk__emit(duk_compiler_ctx *comp_ctx, duk_instr_t ins);
DUK_LOCAL_DECL void duk__emit_op_only(duk_compiler_ctx *comp_ctx, duk_small_uint_t op);
DUK_LOCAL_DECL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a, duk_regconst_t b, duk_regconst_t c);
DUK_LOCAL_DECL void duk__emit_a_b(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a, duk_regconst_t b);
DUK_LOCAL_DECL void duk__emit_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t b, duk_regconst_t c);
#if 0  /* unused */
DUK_LOCAL_DECL void duk__emit_a(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a);
DUK_LOCAL_DECL void duk__emit_b(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t b);
#endif
DUK_LOCAL_DECL void duk__emit_a_bc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a, duk_regconst_t bc);
DUK_LOCAL_DECL void duk__emit_bc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op, duk_regconst_t bc);
DUK_LOCAL_DECL void duk__emit_abc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op, duk_regconst_t abc);
DUK_LOCAL_DECL void duk__emit_load_int32(duk_compiler_ctx *comp_ctx, duk_regconst_t reg, duk_int32_t val);
DUK_LOCAL_DECL void duk__emit_load_int32_noshuffle(duk_compiler_ctx *comp_ctx, duk_regconst_t reg, duk_int32_t val);
DUK_LOCAL_DECL void duk__emit_jump(duk_compiler_ctx *comp_ctx, duk_int_t target_pc);
DUK_LOCAL_DECL duk_int_t duk__emit_jump_empty(duk_compiler_ctx *comp_ctx);
DUK_LOCAL_DECL void duk__insert_jump_entry(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc);
DUK_LOCAL_DECL void duk__patch_jump(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc, duk_int_t target_pc);
DUK_LOCAL_DECL void duk__patch_jump_here(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc);
DUK_LOCAL_DECL void duk__patch_trycatch(duk_compiler_ctx *comp_ctx, duk_int_t ldconst_pc, duk_int_t trycatch_pc, duk_regconst_t reg_catch, duk_regconst_t const_varname, duk_small_uint_t flags);
DUK_LOCAL_DECL void duk__emit_if_false_skip(duk_compiler_ctx *comp_ctx, duk_regconst_t regconst);
DUK_LOCAL_DECL void duk__emit_if_true_skip(duk_compiler_ctx *comp_ctx, duk_regconst_t regconst);
DUK_LOCAL_DECL void duk__emit_invalid(duk_compiler_ctx *comp_ctx);

/* ivalue/ispec helpers */
DUK_LOCAL_DECL void duk__ivalue_regconst(duk_ivalue *x, duk_regconst_t regconst);
DUK_LOCAL_DECL void duk__ivalue_plain_fromstack(duk_compiler_ctx *comp_ctx, duk_ivalue *x);
DUK_LOCAL_DECL void duk__ivalue_var_fromstack(duk_compiler_ctx *comp_ctx, duk_ivalue *x);
DUK_LOCAL_DECL void duk__ivalue_var_hstring(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_hstring *h);
DUK_LOCAL_DECL void duk__copy_ispec(duk_compiler_ctx *comp_ctx, duk_ispec *src, duk_ispec *dst);
DUK_LOCAL_DECL void duk__copy_ivalue(duk_compiler_ctx *comp_ctx, duk_ivalue *src, duk_ivalue *dst);
DUK_LOCAL_DECL duk_regconst_t duk__alloctemps(duk_compiler_ctx *comp_ctx, duk_small_int_t num);
DUK_LOCAL_DECL duk_regconst_t duk__alloctemp(duk_compiler_ctx *comp_ctx);
DUK_LOCAL_DECL void duk__settemp_checkmax(duk_compiler_ctx *comp_ctx, duk_regconst_t temp_next);
DUK_LOCAL_DECL duk_regconst_t duk__getconst(duk_compiler_ctx *comp_ctx);
DUK_LOCAL_DECL
duk_regconst_t duk__ispec_toregconst_raw(duk_compiler_ctx *comp_ctx,
                                         duk_ispec *x,
                                         duk_regconst_t forced_reg,
                                         duk_small_uint_t flags);
DUK_LOCAL_DECL void duk__ispec_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ispec *x, duk_regconst_t forced_reg);
DUK_LOCAL_DECL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_regconst_t forced_reg);
DUK_LOCAL_DECL void duk__ivalue_toplain(duk_compiler_ctx *comp_ctx, duk_ivalue *x);
DUK_LOCAL_DECL void duk__ivalue_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *x);
DUK_LOCAL_DECL
duk_regconst_t duk__ivalue_toregconst_raw(duk_compiler_ctx *comp_ctx,
                                          duk_ivalue *x,
                                          duk_regconst_t forced_reg,
                                          duk_small_uint_t flags);
DUK_LOCAL_DECL duk_regconst_t duk__ivalue_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *x);
#if 0  /* unused */
DUK_LOCAL_DECL duk_regconst_t duk__ivalue_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *x);
#endif
DUK_LOCAL_DECL void duk__ivalue_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_int_t forced_reg);
DUK_LOCAL_DECL duk_regconst_t duk__ivalue_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *x);
DUK_LOCAL_DECL duk_regconst_t duk__ivalue_totempconst(duk_compiler_ctx *comp_ctx, duk_ivalue *x);

/* identifier handling */
DUK_LOCAL_DECL duk_regconst_t duk__lookup_active_register_binding(duk_compiler_ctx *comp_ctx);
DUK_LOCAL_DECL duk_bool_t duk__lookup_lhs(duk_compiler_ctx *ctx, duk_regconst_t *out_reg_varbind, duk_regconst_t *out_rc_varname);

/* label handling */
DUK_LOCAL_DECL void duk__add_label(duk_compiler_ctx *comp_ctx, duk_hstring *h_label, duk_int_t pc_label, duk_int_t label_id);
DUK_LOCAL_DECL void duk__update_label_flags(duk_compiler_ctx *comp_ctx, duk_int_t label_id, duk_small_uint_t flags);
DUK_LOCAL_DECL void duk__lookup_active_label(duk_compiler_ctx *comp_ctx, duk_hstring *h_label, duk_bool_t is_break, duk_int_t *out_label_id, duk_int_t *out_label_catch_depth, duk_int_t *out_label_pc, duk_bool_t *out_is_closest);
DUK_LOCAL_DECL void duk__reset_labels_to_length(duk_compiler_ctx *comp_ctx, duk_size_t len);

/* top-down expression parser */
DUK_LOCAL_DECL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res);
DUK_LOCAL_DECL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_ivalue *res);
DUK_LOCAL_DECL duk_small_uint_t duk__expr_lbp(duk_compiler_ctx *comp_ctx);
DUK_LOCAL_DECL duk_bool_t duk__expr_is_empty(duk_compiler_ctx *comp_ctx);

/* exprtop is the top level variant which resets nud/led counts */
DUK_LOCAL_DECL void duk__expr(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
DUK_LOCAL_DECL void duk__exprtop(duk_compiler_ctx *ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);

/* convenience helpers */
#if 0  /* unused */
DUK_LOCAL_DECL duk_regconst_t duk__expr_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
#endif
#if 0  /* unused */
DUK_LOCAL_DECL duk_regconst_t duk__expr_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
#endif
DUK_LOCAL_DECL void duk__expr_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags, duk_regconst_t forced_reg);
DUK_LOCAL_DECL duk_regconst_t duk__expr_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
#if 0  /* unused */
DUK_LOCAL_DECL duk_regconst_t duk__expr_totempconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
#endif
DUK_LOCAL_DECL void duk__expr_toplain(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
DUK_LOCAL_DECL void duk__expr_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
DUK_LOCAL_DECL duk_regconst_t duk__exprtop_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
#if 0  /* unused */
DUK_LOCAL_DECL duk_regconst_t duk__exprtop_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
#endif
DUK_LOCAL_DECL void duk__exprtop_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags, duk_regconst_t forced_reg);
DUK_LOCAL_DECL duk_regconst_t duk__exprtop_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
#if 0  /* unused */
DUK_LOCAL_DECL void duk__exprtop_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
#endif

/* expression parsing helpers */
DUK_LOCAL_DECL duk_int_t duk__parse_arguments(duk_compiler_ctx *comp_ctx, duk_ivalue *res);
DUK_LOCAL_DECL void duk__nud_array_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *res);
DUK_LOCAL_DECL void duk__nud_object_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *res);

/* statement parsing */
DUK_LOCAL_DECL void duk__parse_var_decl(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t expr_flags, duk_regconst_t *out_reg_varbind, duk_regconst_t *out_rc_varname);
DUK_LOCAL_DECL void duk__parse_var_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t expr_flags);
DUK_LOCAL_DECL void duk__parse_for_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site);
DUK_LOCAL_DECL void duk__parse_switch_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site);
DUK_LOCAL_DECL void duk__parse_if_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res);
DUK_LOCAL_DECL void duk__parse_do_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site);
DUK_LOCAL_DECL void duk__parse_while_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site);
DUK_LOCAL_DECL void duk__parse_break_or_continue_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res);
DUK_LOCAL_DECL void duk__parse_return_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res);
DUK_LOCAL_DECL void duk__parse_throw_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res);
DUK_LOCAL_DECL void duk__parse_try_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res);
DUK_LOCAL_DECL void duk__parse_with_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res);
DUK_LOCAL_DECL void duk__parse_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_bool_t allow_source_elem);
DUK_LOCAL_DECL duk_int_t duk__stmt_label_site(duk_compiler_ctx *comp_ctx, duk_int_t label_id);
DUK_LOCAL_DECL void duk__parse_stmts(duk_compiler_ctx *comp_ctx, duk_bool_t allow_source_elem, duk_bool_t expect_eof, duk_bool_t regexp_after);

DUK_LOCAL_DECL void duk__parse_func_body(duk_compiler_ctx *comp_ctx, duk_bool_t expect_eof, duk_bool_t implicit_return_value, duk_bool_t regexp_after, duk_small_int_t expect_token);
DUK_LOCAL_DECL void duk__parse_func_formals(duk_compiler_ctx *comp_ctx);
DUK_LOCAL_DECL void duk__parse_func_like_raw(duk_compiler_ctx *comp_ctx, duk_small_uint_t flags);
DUK_LOCAL_DECL duk_int_t duk__parse_func_like_fnum(duk_compiler_ctx *comp_ctx, duk_small_uint_t flags);

#define DUK__FUNC_FLAG_DECL            (1 << 0)   /* Parsing a function declaration. */
#define DUK__FUNC_FLAG_GETSET          (1 << 1)   /* Parsing an object literal getter/setter. */
#define DUK__FUNC_FLAG_METDEF          (1 << 2)   /* Parsing an object literal method definition shorthand. */
#define DUK__FUNC_FLAG_PUSHNAME_PASS1  (1 << 3)   /* Push function name when creating template (first pass only). */
#define DUK__FUNC_FLAG_USE_PREVTOKEN   (1 << 4)   /* Use prev_token to start function parsing (workaround for object literal). */

/*
 *  Parser control values for tokens.  The token table is ordered by the
 *  DUK_TOK_XXX defines.
 *
 *  The binding powers are for lbp() use (i.e. for use in led() context).
 *  Binding powers are positive for typing convenience, and bits at the
 *  top should be reserved for flags.  Binding power step must be higher
 *  than 1 so that binding power "lbp - 1" can be used for right associative
 *  operators.  Currently a step of 2 is used (which frees one more bit for
 *  flags).
 */

/* XXX: actually single step levels would work just fine, clean up */

/* binding power "levels" (see doc/compiler.rst) */
#define DUK__BP_INVALID                0             /* always terminates led() */
#define DUK__BP_EOF                    2
#define DUK__BP_CLOSING                4             /* token closes expression, e.g. ')', ']' */
#define DUK__BP_FOR_EXPR               DUK__BP_CLOSING    /* bp to use when parsing a top level Expression */
#define DUK__BP_COMMA                  6
#define DUK__BP_ASSIGNMENT             8
#define DUK__BP_CONDITIONAL            10
#define DUK__BP_LOR                    12
#define DUK__BP_LAND                   14
#define DUK__BP_BOR                    16
#define DUK__BP_BXOR                   18
#define DUK__BP_BAND                   20
#define DUK__BP_EQUALITY               22
#define DUK__BP_RELATIONAL             24
#define DUK__BP_SHIFT                  26
#define DUK__BP_ADDITIVE               28
#define DUK__BP_MULTIPLICATIVE         30
#define DUK__BP_EXPONENTIATION         32
#define DUK__BP_POSTFIX                34
#define DUK__BP_CALL                   36
#define DUK__BP_MEMBER                 38

#define DUK__TOKEN_LBP_BP_MASK         0x1f
#define DUK__TOKEN_LBP_FLAG_NO_REGEXP  (1 << 5)   /* regexp literal must not follow this token */
#define DUK__TOKEN_LBP_FLAG_TERMINATES (1 << 6)   /* terminates expression; e.g. post-increment/-decrement */
#define DUK__TOKEN_LBP_FLAG_UNUSED     (1 << 7)   /* unused */

#define DUK__TOKEN_LBP_GET_BP(x)       ((duk_small_uint_t) (((x) & DUK__TOKEN_LBP_BP_MASK) * 2))

#define DUK__MK_LBP(bp)                ((bp) >> 1)    /* bp is assumed to be even */
#define DUK__MK_LBP_FLAGS(bp,flags)    (((bp) >> 1) | (flags))

DUK_LOCAL const duk_uint8_t duk__token_lbp[] = {
    DUK__MK_LBP(DUK__BP_EOF),                                 /* DUK_TOK_EOF */
    DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP),  /* DUK_TOK_IDENTIFIER */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_BREAK */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_CASE */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_CATCH */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_CONTINUE */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_DEBUGGER */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_DEFAULT */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_DELETE */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_DO */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_ELSE */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_FINALLY */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_FOR */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_FUNCTION */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_IF */
    DUK__MK_LBP(DUK__BP_RELATIONAL),                          /* DUK_TOK_IN */
    DUK__MK_LBP(DUK__BP_RELATIONAL),                          /* DUK_TOK_INSTANCEOF */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_NEW */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_RETURN */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_SWITCH */
    DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP),  /* DUK_TOK_THIS */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_THROW */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_TRY */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_TYPEOF */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_VAR */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_CONST */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_VOID */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_WHILE */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_WITH */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_CLASS */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_ENUM */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_EXPORT */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_EXTENDS */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_IMPORT */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_SUPER */
    DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP),  /* DUK_TOK_NULL */
    DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP),  /* DUK_TOK_TRUE */
    DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP),  /* DUK_TOK_FALSE */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_GET */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_SET */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_IMPLEMENTS */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_INTERFACE */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_LET */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_PACKAGE */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_PRIVATE */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_PROTECTED */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_PUBLIC */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_STATIC */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_YIELD */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_LCURLY */
    DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP),  /* DUK_TOK_RCURLY */
    DUK__MK_LBP(DUK__BP_MEMBER),                              /* DUK_TOK_LBRACKET */
    DUK__MK_LBP_FLAGS(DUK__BP_CLOSING, DUK__TOKEN_LBP_FLAG_NO_REGEXP),  /* DUK_TOK_RBRACKET */
    DUK__MK_LBP(DUK__BP_CALL),                                /* DUK_TOK_LPAREN */
    DUK__MK_LBP_FLAGS(DUK__BP_CLOSING, DUK__TOKEN_LBP_FLAG_NO_REGEXP),  /* DUK_TOK_RPAREN */
    DUK__MK_LBP(DUK__BP_MEMBER),                              /* DUK_TOK_PERIOD */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_SEMICOLON */
    DUK__MK_LBP(DUK__BP_COMMA),                               /* DUK_TOK_COMMA */
    DUK__MK_LBP(DUK__BP_RELATIONAL),                          /* DUK_TOK_LT */
    DUK__MK_LBP(DUK__BP_RELATIONAL),                          /* DUK_TOK_GT */
    DUK__MK_LBP(DUK__BP_RELATIONAL),                          /* DUK_TOK_LE */
    DUK__MK_LBP(DUK__BP_RELATIONAL),                          /* DUK_TOK_GE */
    DUK__MK_LBP(DUK__BP_EQUALITY),                            /* DUK_TOK_EQ */
    DUK__MK_LBP(DUK__BP_EQUALITY),                            /* DUK_TOK_NEQ */
    DUK__MK_LBP(DUK__BP_EQUALITY),                            /* DUK_TOK_SEQ */
    DUK__MK_LBP(DUK__BP_EQUALITY),                            /* DUK_TOK_SNEQ */
    DUK__MK_LBP(DUK__BP_ADDITIVE),                            /* DUK_TOK_ADD */
    DUK__MK_LBP(DUK__BP_ADDITIVE),                            /* DUK_TOK_SUB */
    DUK__MK_LBP(DUK__BP_MULTIPLICATIVE),                      /* DUK_TOK_MUL */
    DUK__MK_LBP(DUK__BP_MULTIPLICATIVE),                      /* DUK_TOK_DIV */
    DUK__MK_LBP(DUK__BP_MULTIPLICATIVE),                      /* DUK_TOK_MOD */
    DUK__MK_LBP(DUK__BP_EXPONENTIATION),                      /* DUK_TOK_EXP */
    DUK__MK_LBP_FLAGS(DUK__BP_POSTFIX, DUK__TOKEN_LBP_FLAG_NO_REGEXP),  /* DUK_TOK_INCREMENT */
    DUK__MK_LBP_FLAGS(DUK__BP_POSTFIX, DUK__TOKEN_LBP_FLAG_NO_REGEXP),  /* DUK_TOK_DECREMENT */
    DUK__MK_LBP(DUK__BP_SHIFT),                               /* DUK_TOK_ALSHIFT */
    DUK__MK_LBP(DUK__BP_SHIFT),                               /* DUK_TOK_ARSHIFT */
    DUK__MK_LBP(DUK__BP_SHIFT),                               /* DUK_TOK_RSHIFT */
    DUK__MK_LBP(DUK__BP_BAND),                                /* DUK_TOK_BAND */
    DUK__MK_LBP(DUK__BP_BOR),                                 /* DUK_TOK_BOR */
    DUK__MK_LBP(DUK__BP_BXOR),                                /* DUK_TOK_BXOR */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_LNOT */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_BNOT */
    DUK__MK_LBP(DUK__BP_LAND),                                /* DUK_TOK_LAND */
    DUK__MK_LBP(DUK__BP_LOR),                                 /* DUK_TOK_LOR */
    DUK__MK_LBP(DUK__BP_CONDITIONAL),                         /* DUK_TOK_QUESTION */
    DUK__MK_LBP(DUK__BP_INVALID),                             /* DUK_TOK_COLON */
    DUK__MK_LBP(DUK__BP_ASSIGNMENT),                          /* DUK_TOK_EQUALSIGN */
    DUK__MK_LBP(DUK__BP_ASSIGNMENT),                          /* DUK_TOK_ADD_EQ */
    DUK__MK_LBP(DUK__BP_ASSIGNMENT),                          /* DUK_TOK_SUB_EQ */
    DUK__MK_LBP(DUK__BP_ASSIGNMENT),                          /* DUK_TOK_MUL_EQ */
    DUK__MK_LBP(DUK__BP_ASSIGNMENT),                          /* DUK_TOK_DIV_EQ */
    DUK__MK_LBP(DUK__BP_ASSIGNMENT),                          /* DUK_TOK_MOD_EQ */
    DUK__MK_LBP(DUK__BP_ASSIGNMENT),                          /* DUK_TOK_EXP_EQ */
    DUK__MK_LBP(DUK__BP_ASSIGNMENT),                          /* DUK_TOK_ALSHIFT_EQ */
    DUK__MK_LBP(DUK__BP_ASSIGNMENT),                          /* DUK_TOK_ARSHIFT_EQ */
    DUK__MK_LBP(DUK__BP_ASSIGNMENT),                          /* DUK_TOK_RSHIFT_EQ */
    DUK__MK_LBP(DUK__BP_ASSIGNMENT),                          /* DUK_TOK_BAND_EQ */
    DUK__MK_LBP(DUK__BP_ASSIGNMENT),                          /* DUK_TOK_BOR_EQ */
    DUK__MK_LBP(DUK__BP_ASSIGNMENT),                          /* DUK_TOK_BXOR_EQ */
    DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP),  /* DUK_TOK_NUMBER */
    DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP),  /* DUK_TOK_STRING */
    DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP),  /* DUK_TOK_REGEXP */
};

/*
 *  Misc helpers
 */

DUK_LOCAL void duk__comp_recursion_increase(duk_compiler_ctx *comp_ctx) {
    DUK_ASSERT(comp_ctx != NULL);
    DUK_ASSERT(comp_ctx->recursion_depth >= 0);
    if (comp_ctx->recursion_depth >= comp_ctx->recursion_limit) {
        DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_COMPILER_RECURSION_LIMIT);
        DUK_WO_NORETURN(return;);
    }
    comp_ctx->recursion_depth++;
}

DUK_LOCAL void duk__comp_recursion_decrease(duk_compiler_ctx *comp_ctx) {
    DUK_ASSERT(comp_ctx != NULL);
    DUK_ASSERT(comp_ctx->recursion_depth > 0);
    comp_ctx->recursion_depth--;
}

DUK_LOCAL duk_bool_t duk__hstring_is_eval_or_arguments(duk_compiler_ctx *comp_ctx, duk_hstring *h) {
    DUK_UNREF(comp_ctx);
    DUK_ASSERT(h != NULL);
    return DUK_HSTRING_HAS_EVAL_OR_ARGUMENTS(h);
}

DUK_LOCAL duk_bool_t duk__hstring_is_eval_or_arguments_in_strict_mode(duk_compiler_ctx *comp_ctx, duk_hstring *h) {
    DUK_ASSERT(h != NULL);
    return (comp_ctx->curr_func.is_strict &&
            DUK_HSTRING_HAS_EVAL_OR_ARGUMENTS(h));
}

/*
 *  Parser duk__advance() token eating functions
 */

/* XXX: valstack handling is awkward.  Add a valstack helper which
 * avoids dup():ing; valstack_copy(src, dst)?
 */

DUK_LOCAL void duk__advance_helper(duk_compiler_ctx *comp_ctx, duk_small_int_t expect) {
    duk_hthread *thr = comp_ctx->thr;
    duk_bool_t regexp;

    DUK_ASSERT_DISABLE(comp_ctx->curr_token.t >= 0);  /* unsigned */
    DUK_ASSERT(comp_ctx->curr_token.t <= DUK_TOK_MAXVAL);  /* MAXVAL is inclusive */

    /*
     *  Use current token to decide whether a RegExp can follow.
     *
     *  We can use either 't' or 't_nores'; the latter would not
     *  recognize keywords.  Some keywords can be followed by a
     *  RegExp (e.g. "return"), so using 't' is better.  This is
     *  not trivial, see doc/compiler.rst.
     */

    regexp = 1;
    if (duk__token_lbp[comp_ctx->curr_token.t] & DUK__TOKEN_LBP_FLAG_NO_REGEXP) {
        regexp = 0;
    }
    if (comp_ctx->curr_func.reject_regexp_in_adv) {
        comp_ctx->curr_func.reject_regexp_in_adv = 0;
        regexp = 0;
    }
    if (comp_ctx->curr_func.allow_regexp_in_adv) {
        comp_ctx->curr_func.allow_regexp_in_adv = 0;
        regexp = 1;
    }

    if (expect >= 0 && comp_ctx->curr_token.t != (duk_small_uint_t) expect) {
        DUK_D(DUK_DPRINT("parse error: expect=%ld, got=%ld",
                         (long) expect, (long) comp_ctx->curr_token.t));
        DUK_ERROR_SYNTAX(thr, DUK_STR_PARSE_ERROR);
        DUK_WO_NORETURN(return;);
    }

    /* make current token the previous; need to fiddle with valstack "backing store" */
    duk_memcpy(&comp_ctx->prev_token, &comp_ctx->curr_token, sizeof(duk_token));
    duk_copy(thr, comp_ctx->tok11_idx, comp_ctx->tok21_idx);
    duk_copy(thr, comp_ctx->tok12_idx, comp_ctx->tok22_idx);

    /* parse new token */
    duk_lexer_parse_js_input_element(&comp_ctx->lex,
                                     &comp_ctx->curr_token,
                                     comp_ctx->curr_func.is_strict,
                                     regexp);

    DUK_DDD(DUK_DDDPRINT("advance: curr: tok=%ld/%ld,%ld,term=%ld,%!T,%!T "
                         "prev: tok=%ld/%ld,%ld,term=%ld,%!T,%!T",
                         (long) comp_ctx->curr_token.t,
                         (long) comp_ctx->curr_token.t_nores,
                         (long) comp_ctx->curr_token.start_line,
                         (long) comp_ctx->curr_token.lineterm,
                         (duk_tval *) duk_get_tval(thr, comp_ctx->tok11_idx),
                         (duk_tval *) duk_get_tval(thr, comp_ctx->tok12_idx),
                         (long) comp_ctx->prev_token.t,
                         (long) comp_ctx->prev_token.t_nores,
                         (long) comp_ctx->prev_token.start_line,
                         (long) comp_ctx->prev_token.lineterm,
                         (duk_tval *) duk_get_tval(thr, comp_ctx->tok21_idx),
                         (duk_tval *) duk_get_tval(thr, comp_ctx->tok22_idx)));
}

/* advance, expecting current token to be a specific token; parse next token in regexp context */
DUK_LOCAL void duk__advance_expect(duk_compiler_ctx *comp_ctx, duk_small_int_t expect) {
    duk__advance_helper(comp_ctx, expect);
}

/* advance, whatever the current token is; parse next token in regexp context */
DUK_LOCAL void duk__advance(duk_compiler_ctx *comp_ctx) {
    duk__advance_helper(comp_ctx, -1);
}

/*
 *  Helpers for duk_compiler_func.
 */

/* init function state: inits valstack allocations */
DUK_LOCAL void duk__init_func_valstack_slots(duk_compiler_ctx *comp_ctx) {
    duk_compiler_func *func = &comp_ctx->curr_func;
    duk_hthread *thr = comp_ctx->thr;
    duk_idx_t entry_top;

    entry_top = duk_get_top(thr);

    duk_memzero(func, sizeof(*func));  /* intentional overlap with earlier memzero */
#if defined(DUK_USE_EXPLICIT_NULL_INIT)
    func->h_name = NULL;
    func->h_consts = NULL;
    func->h_funcs = NULL;
    func->h_decls = NULL;
    func->h_labelnames = NULL;
    func->h_labelinfos = NULL;
    func->h_argnames = NULL;
    func->h_varmap = NULL;
#endif

    duk_require_stack(thr, DUK__FUNCTION_INIT_REQUIRE_SLOTS);

    DUK_BW_INIT_PUSHBUF(thr, &func->bw_code, DUK__BC_INITIAL_INSTS * sizeof(duk_compiler_instr));
    /* code_idx = entry_top + 0 */

    duk_push_bare_array(thr);
    func->consts_idx = entry_top + 1;
    func->h_consts = DUK_GET_HOBJECT_POSIDX(thr, entry_top + 1);
    DUK_ASSERT(func->h_consts != NULL);

    duk_push_bare_array(thr);
    func->funcs_idx = entry_top + 2;
    func->h_funcs = DUK_GET_HOBJECT_POSIDX(thr, entry_top + 2);
    DUK_ASSERT(func->h_funcs != NULL);
    DUK_ASSERT(func->fnum_next == 0);

    duk_push_bare_array(thr);
    func->decls_idx = entry_top + 3;
    func->h_decls = DUK_GET_HOBJECT_POSIDX(thr, entry_top + 3);
    DUK_ASSERT(func->h_decls != NULL);

    duk_push_bare_array(thr);
    func->labelnames_idx = entry_top + 4;
    func->h_labelnames = DUK_GET_HOBJECT_POSIDX(thr, entry_top + 4);
    DUK_ASSERT(func->h_labelnames != NULL);

    duk_push_dynamic_buffer(thr, 0);
    func->labelinfos_idx = entry_top + 5;
    func->h_labelinfos = (duk_hbuffer_dynamic *) duk_known_hbuffer(thr, entry_top + 5);
    DUK_ASSERT(func->h_labelinfos != NULL);
    DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(func->h_labelinfos) && !DUK_HBUFFER_HAS_EXTERNAL(func->h_labelinfos));

    duk_push_bare_array(thr);
    func->argnames_idx = entry_top + 6;
    func->h_argnames = DUK_GET_HOBJECT_POSIDX(thr, entry_top + 6);
    DUK_ASSERT(func->h_argnames != NULL);

    duk_push_bare_object(thr);
    func->varmap_idx = entry_top + 7;
    func->h_varmap = DUK_GET_HOBJECT_POSIDX(thr, entry_top + 7);
    DUK_ASSERT(func->h_varmap != NULL);
}

/* reset function state (prepare for pass 2) */
DUK_LOCAL void duk__reset_func_for_pass2(duk_compiler_ctx *comp_ctx) {
    duk_compiler_func *func = &comp_ctx->curr_func;
    duk_hthread *thr = comp_ctx->thr;

    /* reset bytecode buffer but keep current size; pass 2 will
     * require same amount or more.
     */
    DUK_BW_RESET_SIZE(thr, &func->bw_code);

    duk_set_length(thr, func->consts_idx, 0);
    /* keep func->h_funcs; inner functions are not reparsed to avoid O(depth^2) parsing */
    func->fnum_next = 0;
    /* duk_set_length(thr, func->funcs_idx, 0); */
    duk_set_length(thr, func->labelnames_idx, 0);
    duk_hbuffer_reset(thr, func->h_labelinfos);
    /* keep func->h_argnames; it is fixed for all passes */

    /* truncated in case pass 3 needed */
    duk_push_bare_object(thr);
    duk_replace(thr, func->varmap_idx);
    func->h_varmap = DUK_GET_HOBJECT_POSIDX(thr, func->varmap_idx);
    DUK_ASSERT(func->h_varmap != NULL);
}

/* cleanup varmap from any null entries, compact it, etc; returns number
 * of final entries after cleanup.
 */
DUK_LOCAL duk_int_t duk__cleanup_varmap(duk_compiler_ctx *comp_ctx) {
    duk_hthread *thr = comp_ctx->thr;
    duk_hobject *h_varmap;
    duk_hstring *h_key;
    duk_tval *tv;
    duk_uint32_t i, e_next;
    duk_int_t ret;

    /* [ ... varmap ] */

    h_varmap = DUK_GET_HOBJECT_NEGIDX(thr, -1);
    DUK_ASSERT(h_varmap != NULL);

    ret = 0;
    e_next = DUK_HOBJECT_GET_ENEXT(h_varmap);
    for (i = 0; i < e_next; i++) {
        h_key = DUK_HOBJECT_E_GET_KEY(thr->heap, h_varmap, i);
        if (!h_key) {
            continue;
        }

        DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, h_varmap, i));

        /* The entries can either be register numbers or 'null' values.
         * Thus, no need to DECREF them and get side effects.  DECREF'ing
         * the keys (strings) can cause memory to be freed but no side
         * effects as strings don't have finalizers.  This is why we can
         * rely on the object properties not changing from underneath us.
         */

        tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, h_varmap, i);
        if (!DUK_TVAL_IS_NUMBER(tv)) {
            DUK_ASSERT(!DUK_TVAL_IS_HEAP_ALLOCATED(tv));
            DUK_HOBJECT_E_SET_KEY(thr->heap, h_varmap, i, NULL);
            DUK_HSTRING_DECREF(thr, h_key);
            /* when key is NULL, value is garbage so no need to set */
        } else {
            ret++;
        }
    }

    duk_compact_m1(thr);

    return ret;
}

/* Convert duk_compiler_func into a function template, leaving the result
 * on top of stack.
 */
/* XXX: awkward and bloated asm -- use faster internal accesses */
DUK_LOCAL void duk__convert_to_func_template(duk_compiler_ctx *comp_ctx) {
    duk_compiler_func *func = &comp_ctx->curr_func;
    duk_hthread *thr = comp_ctx->thr;
    duk_hcompfunc *h_res;
    duk_hbuffer_fixed *h_data;
    duk_size_t consts_count;
    duk_size_t funcs_count;
    duk_size_t code_count;
    duk_size_t code_size;
    duk_size_t data_size;
    duk_size_t i;
    duk_tval *p_const;
    duk_hobject **p_func;
    duk_instr_t *p_instr;
    duk_compiler_instr *q_instr;
    duk_tval *tv;
    duk_bool_t keep_varmap;
    duk_bool_t keep_formals;
#if !defined(DUK_USE_DEBUGGER_SUPPORT)
    duk_size_t formals_length;
#endif

    DUK_DDD(DUK_DDDPRINT("converting duk_compiler_func to function/template"));

    /*
     *  Push result object and init its flags
     */

    /* Valstack should suffice here, required on function valstack init */

    h_res = duk_push_hcompfunc(thr);
    DUK_ASSERT(h_res != NULL);
    DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_res) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
    DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) h_res, NULL);  /* Function templates are "bare objects". */

    if (func->is_function) {
        DUK_DDD(DUK_DDDPRINT("function -> set NEWENV"));
        DUK_HOBJECT_SET_NEWENV((duk_hobject *) h_res);

        if (!func->is_arguments_shadowed) {
            /* arguments object would be accessible; note that shadowing
             * bindings are arguments or function declarations, neither
             * of which are deletable, so this is safe.
             */

            if (func->id_access_arguments || func->may_direct_eval) {
                DUK_DDD(DUK_DDDPRINT("function may access 'arguments' object directly or "
                                     "indirectly -> set CREATEARGS"));
                DUK_HOBJECT_SET_CREATEARGS((duk_hobject *) h_res);
            }
        }
    } else if (func->is_eval && func->is_strict) {
        DUK_DDD(DUK_DDDPRINT("strict eval code -> set NEWENV"));
        DUK_HOBJECT_SET_NEWENV((duk_hobject *) h_res);
    } else {
        /* non-strict eval: env is caller's env or global env (direct vs. indirect call)
         * global code: env is is global env
         */
        DUK_DDD(DUK_DDDPRINT("non-strict eval code or global code -> no NEWENV"));
        DUK_ASSERT(!DUK_HOBJECT_HAS_NEWENV((duk_hobject *) h_res));
    }

#if defined(DUK_USE_FUNC_NAME_PROPERTY)
    if (func->is_function && func->is_namebinding && func->h_name != NULL) {
        /* Object literal set/get functions have a name (property
         * name) but must not have a lexical name binding, see
         * test-bug-getset-func-name.js.
         */
        DUK_DDD(DUK_DDDPRINT("function expression with a name -> set NAMEBINDING"));
        DUK_HOBJECT_SET_NAMEBINDING((duk_hobject *) h_res);
    }
#endif

    if (func->is_strict) {
        DUK_DDD(DUK_DDDPRINT("function is strict -> set STRICT"));
        DUK_HOBJECT_SET_STRICT((duk_hobject *) h_res);
    }

    if (func->is_notail) {
        DUK_DDD(DUK_DDDPRINT("function is notail -> set NOTAIL"));
        DUK_HOBJECT_SET_NOTAIL((duk_hobject *) h_res);
    }

    if (func->is_constructable) {
        DUK_DDD(DUK_DDDPRINT("function is constructable -> set CONSTRUCTABLE"));
        DUK_HOBJECT_SET_CONSTRUCTABLE((duk_hobject *) h_res);
    }

    /*
     *  Build function fixed size 'data' buffer, which contains bytecode,
     *  constants, and inner function references.
     *
     *  During the building phase 'data' is reachable but incomplete.
     *  Only incref's occur during building (no refzero or GC happens),
     *  so the building process is atomic.
     */

    consts_count = duk_hobject_get_length(thr, func->h_consts);
    funcs_count = duk_hobject_get_length(thr, func->h_funcs) / 3;
    code_count = DUK_BW_GET_SIZE(thr, &func->bw_code) / sizeof(duk_compiler_instr);
    code_size = code_count * sizeof(duk_instr_t);

    data_size = consts_count * sizeof(duk_tval) +
                funcs_count * sizeof(duk_hobject *) +
                code_size;

    DUK_DDD(DUK_DDDPRINT("consts_count=%ld, funcs_count=%ld, code_size=%ld -> "
                         "data_size=%ld*%ld + %ld*%ld + %ld = %ld",
                         (long) consts_count, (long) funcs_count, (long) code_size,
                         (long) consts_count, (long) sizeof(duk_tval),
                         (long) funcs_count, (long) sizeof(duk_hobject *),
                         (long) code_size, (long) data_size));

    duk_push_fixed_buffer_nozero(thr, data_size);
    h_data = (duk_hbuffer_fixed *) (void *) duk_known_hbuffer(thr, -1);

    DUK_HCOMPFUNC_SET_DATA(thr->heap, h_res, (duk_hbuffer *) h_data);
    DUK_HEAPHDR_INCREF(thr, h_data);

    p_const = (duk_tval *) (void *) DUK_HBUFFER_FIXED_GET_DATA_PTR(thr->heap, h_data);
    for (i = 0; i < consts_count; i++) {
        DUK_ASSERT(i <= DUK_UARRIDX_MAX);  /* const limits */
        tv = duk_hobject_find_array_entry_tval_ptr(thr->heap, func->h_consts, (duk_uarridx_t) i);
        DUK_ASSERT(tv != NULL);
        DUK_TVAL_SET_TVAL(p_const, tv);
        p_const++;
        DUK_TVAL_INCREF(thr, tv);  /* may be a string constant */

        DUK_DDD(DUK_DDDPRINT("constant: %!T", (duk_tval *) tv));
    }

    p_func = (duk_hobject **) p_const;
    DUK_HCOMPFUNC_SET_FUNCS(thr->heap, h_res, p_func);
    for (i = 0; i < funcs_count; i++) {
        duk_hobject *h;
        DUK_ASSERT(i * 3 <= DUK_UARRIDX_MAX);  /* func limits */
        tv = duk_hobject_find_array_entry_tval_ptr(thr->heap, func->h_funcs, (duk_uarridx_t) (i * 3));
        DUK_ASSERT(tv != NULL);
        DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv));
        h = DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(h != NULL);
        DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(h));
        *p_func++ = h;
        DUK_HOBJECT_INCREF(thr, h);

        DUK_DDD(DUK_DDDPRINT("inner function: %p -> %!iO",
                             (void *) h, (duk_heaphdr *) h));
    }

    p_instr = (duk_instr_t *) p_func;
    DUK_HCOMPFUNC_SET_BYTECODE(thr->heap, h_res, p_instr);

    /* copy bytecode instructions one at a time */
    q_instr = (duk_compiler_instr *) (void *) DUK_BW_GET_BASEPTR(thr, &func->bw_code);
    for (i = 0; i < code_count; i++) {
        p_instr[i] = q_instr[i].ins;
    }
    /* Note: 'q_instr' is still used below */

    DUK_ASSERT((duk_uint8_t *) (p_instr + code_count) == DUK_HBUFFER_FIXED_GET_DATA_PTR(thr->heap, h_data) + data_size);

    duk_pop(thr);  /* 'data' (and everything in it) is reachable through h_res now */

    /*
     *  Init non-property result fields
     *
     *  'nregs' controls how large a register frame is allocated.
     *
     *  'nargs' controls how many formal arguments are written to registers:
     *  r0, ... r(nargs-1).  The remaining registers are initialized to
     *  undefined.
     */

    DUK_ASSERT(func->temp_max >= 0);
    h_res->nregs = (duk_uint16_t) func->temp_max;
    h_res->nargs = (duk_uint16_t) duk_hobject_get_length(thr, func->h_argnames);
    DUK_ASSERT(h_res->nregs >= h_res->nargs);  /* pass2 allocation handles this */
#if defined(DUK_USE_DEBUGGER_SUPPORT)
    h_res->start_line = (duk_uint32_t) func->min_line;
    h_res->end_line = (duk_uint32_t) func->max_line;
#endif

    /*
     *  Init object properties
     *
     *  Properties should be added in decreasing order of access frequency.
     *  (Not very critical for function templates.)
     */

    DUK_DDD(DUK_DDDPRINT("init function properties"));

    /* [ ... res ] */

    /* _Varmap: omitted if function is guaranteed not to do a slow path
     * identifier access that might be caught by locally declared variables.
     * The varmap can also be omitted if it turns out empty of actual
     * register mappings after a cleanup.  When debugging is enabled, we
     * always need the varmap to be able to lookup variables at any point.
     */

#if defined(DUK_USE_DEBUGGER_SUPPORT)
    DUK_DD(DUK_DDPRINT("keeping _Varmap because debugger support is enabled"));
    keep_varmap = 1;
#else
    if (func->id_access_slow_own ||   /* directly uses slow accesses that may match own variables */
        func->id_access_arguments ||  /* accesses 'arguments' directly */
        func->may_direct_eval ||      /* may indirectly slow access through a direct eval */
        funcs_count > 0) {            /* has inner functions which may slow access (XXX: this can be optimized by looking at the inner functions) */
        DUK_DD(DUK_DDPRINT("keeping _Varmap because of direct eval, slow path access that may match local variables, or presence of inner functions"));
        keep_varmap = 1;
    } else {
        DUK_DD(DUK_DDPRINT("dropping _Varmap"));
        keep_varmap = 0;
    }
#endif

    if (keep_varmap) {
        duk_int_t num_used;
        duk_dup(thr, func->varmap_idx);
        num_used = duk__cleanup_varmap(comp_ctx);
        DUK_DDD(DUK_DDDPRINT("cleaned up varmap: %!T (num_used=%ld)",
                             (duk_tval *) duk_get_tval(thr, -1), (long) num_used));

        if (num_used > 0) {
            duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VARMAP, DUK_PROPDESC_FLAGS_NONE);
        } else {
            DUK_DD(DUK_DDPRINT("varmap is empty after cleanup -> no need to add"));
            duk_pop(thr);
        }
    }

    /* _Formals: omitted if function is guaranteed not to need a (non-strict)
     * arguments object, and _Formals.length matches nargs exactly.
     *
     * Non-arrow functions can't see an outer function's 'argument' binding
     * (because they have their own), but arrow functions can.  When arrow
     * functions are added, this condition would need to be added:
     *     inner_arrow_funcs_count > 0   inner arrow functions may access 'arguments'
     */
#if defined(DUK_USE_DEBUGGER_SUPPORT)
    DUK_DD(DUK_DDPRINT("keeping _Formals because debugger support is enabled"));
    keep_formals = 1;
#else
    formals_length = duk_get_length(thr, func->argnames_idx);
    if (formals_length != (duk_size_t) h_res->nargs) {
        /* Nargs not enough for closure .length: keep _Formals regardless
         * of its length.  Shouldn't happen in practice at the moment.
         */
        DUK_DD(DUK_DDPRINT("keeping _Formals because _Formals.length != nargs"));
        keep_formals = 1;
    } else if ((func->id_access_arguments || func->may_direct_eval) &&
               (formals_length > 0)) {
        /* Direct eval (may access 'arguments') or accesses 'arguments'
         * explicitly: keep _Formals unless it is zero length.
         */
        DUK_DD(DUK_DDPRINT("keeping _Formals because of direct eval or explicit access to 'arguments', and _Formals.length != 0"));
        keep_formals = 1;
    } else {
        DUK_DD(DUK_DDPRINT("omitting _Formals, nargs matches _Formals.length, so no properties added"));
        keep_formals = 0;
    }
#endif

    if (keep_formals) {
        duk_dup(thr, func->argnames_idx);
        duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_FORMALS, DUK_PROPDESC_FLAGS_NONE);
    }

    /* name */
#if defined(DUK_USE_FUNC_NAME_PROPERTY)
    if (func->h_name) {
        duk_push_hstring(thr, func->h_name);
        DUK_DD(DUK_DDPRINT("setting function template .name to %!T", duk_get_tval(thr, -1)));
        duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE);
    }
#endif  /* DUK_USE_FUNC_NAME_PROPERTY */

    /* _Source */
#if defined(DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY)
    if (0) {
        /* XXX: Currently function source code is not stored, as it is not
         * required by the standard.  Source code should not be stored by
         * default (user should enable it explicitly), and the source should
         * probably be compressed with a trivial text compressor; average
         * compression of 20-30% is quite easy to achieve even with a trivial
         * compressor (RLE + backwards lookup).
         *
         * Debugging needs source code to be useful: sometimes input code is
         * not found in files as it may be generated and then eval()'d, given
         * by dynamic C code, etc.
         *
         * Other issues:
         *
         *   - Need tokenizer indices for start and end to substring
         *   - Always normalize function declaration part?
         *   - If we keep _Formals, only need to store body
         */

        /*
         *  For global or eval code this is straightforward.  For functions
         *  created with the Function constructor we only get the source for
         *  the body and must manufacture the "function ..." part.
         *
         *  For instance, for constructed functions (v8):
         *
         *    > a = new Function("foo", "bar", "print(foo)");
         *    [Function]
         *    > a.toString()
         *    'function anonymous(foo,bar) {\nprint(foo)\n}'
         *
         *  Similarly for e.g. getters (v8):
         *
         *    > x = { get a(foo,bar) { print(foo); } }
         *    { a: [Getter] }
         *    > Object.getOwnPropertyDescriptor(x, 'a').get.toString()
         *    'function a(foo,bar) { print(foo); }'
         */

#if 0
        duk_push_literal(thr, "XXX");
        duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_SOURCE, DUK_PROPDESC_FLAGS_NONE);
#endif
    }
#endif  /* DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY */

    /* _Pc2line */
#if defined(DUK_USE_PC2LINE)
    if (1) {
        /*
         *  Size-optimized pc->line mapping.
         */

        DUK_ASSERT(code_count <= DUK_COMPILER_MAX_BYTECODE_LENGTH);
        duk_hobject_pc2line_pack(thr, q_instr, (duk_uint_fast32_t) code_count);  /* -> pushes fixed buffer */
        duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_PC2LINE, DUK_PROPDESC_FLAGS_NONE);

        /* XXX: if assertions enabled, walk through all valid PCs
         * and check line mapping.
         */
    }
#endif  /* DUK_USE_PC2LINE */

    /* fileName */
#if defined(DUK_USE_FUNC_FILENAME_PROPERTY)
    if (comp_ctx->h_filename) {
        /*
         *  Source filename (or equivalent), for identifying thrown errors.
         */

        duk_push_hstring(thr, comp_ctx->h_filename);
        duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_NONE);
    }
#endif

    DUK_DD(DUK_DDPRINT("converted function: %!ixT",
                       (duk_tval *) duk_get_tval(thr, -1)));

    /*
     *  Compact the function template.
     */

    duk_compact_m1(thr);

    /*
     *  Debug dumping
     */

#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)
    {
        duk_hcompfunc *h;
        duk_instr_t *p, *p_start, *p_end;

        h = (duk_hcompfunc *) duk_get_hobject(thr, -1);
        p_start = (duk_instr_t *) DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, h);
        p_end = (duk_instr_t *) DUK_HCOMPFUNC_GET_CODE_END(thr->heap, h);

        p = p_start;
        while (p < p_end) {
            DUK_DDD(DUK_DDDPRINT("BC %04ld: %!I        ; 0x%08lx op=%ld (%!X) a=%ld b=%ld c=%ld",
                                 (long) (p - p_start),
                                 (duk_instr_t) (*p),
                                 (unsigned long) (*p),
                                 (long) DUK_DEC_OP(*p),
                                 (long) DUK_DEC_OP(*p),
                                 (long) DUK_DEC_A(*p),
                                 (long) DUK_DEC_B(*p),
                                 (long) DUK_DEC_C(*p)));
            p++;
        }
    }
#endif
}

/*
 *  Code emission helpers
 *
 *  Some emission helpers understand the range of target and source reg/const
 *  values and automatically emit shuffling code if necessary.  This is the
 *  case when the slot in question (A, B, C) is used in the standard way and
 *  for opcodes the emission helpers explicitly understand (like DUK_OP_MPUTOBJ).
 *
 *  The standard way is that:
 *    - slot A is a target register
 *    - slot B is a source register/constant
 *    - slot C is a source register/constant
 *
 *  If a slot is used in a non-standard way the caller must indicate this
 *  somehow.  If a slot is used as a target instead of a source (or vice
 *  versa), this can be indicated with a flag to trigger proper shuffling
 *  (e.g. DUK__EMIT_FLAG_B_IS_TARGET).  If the value in the slot is not
 *  register/const related at all, the caller must ensure that the raw value
 *  fits into the corresponding slot so as to not trigger shuffling.  The
 *  caller must set a "no shuffle" flag to ensure compilation fails if
 *  shuffling were to be triggered because of an internal error.
 *
 *  For slots B and C the raw slot size is 9 bits but one bit is reserved for
 *  the reg/const indicator.  To use the full 9-bit range for a raw value,
 *  shuffling must be disabled with the DUK__EMIT_FLAG_NO_SHUFFLE_{B,C} flag.
 *  Shuffling is only done for A, B, and C slots, not the larger BC or ABC slots.
 *
 *  There is call handling specific understanding in the A-B-C emitter to
 *  convert call setup and call instructions into indirect ones if necessary.
 */

/* Code emission flags, passed in the 'opcode' field.  Opcode + flags
 * fit into 16 bits for now, so use duk_small_uint_t.
 */
#define DUK__EMIT_FLAG_NO_SHUFFLE_A      (1 << 8)
#define DUK__EMIT_FLAG_NO_SHUFFLE_B      (1 << 9)
#define DUK__EMIT_FLAG_NO_SHUFFLE_C      (1 << 10)
#define DUK__EMIT_FLAG_A_IS_SOURCE       (1 << 11)  /* slot A is a source (default: target) */
#define DUK__EMIT_FLAG_B_IS_TARGET       (1 << 12)  /* slot B is a target (default: source) */
#define DUK__EMIT_FLAG_C_IS_TARGET       (1 << 13)  /* slot C is a target (default: source) */
#define DUK__EMIT_FLAG_BC_REGCONST       (1 << 14)  /* slots B and C are reg/const */
#define DUK__EMIT_FLAG_RESERVE_JUMPSLOT  (1 << 15)  /* reserve a jumpslot after instr before target spilling, used for NEXTENUM */

/* XXX: macro smaller than call? */
DUK_LOCAL duk_int_t duk__get_current_pc(duk_compiler_ctx *comp_ctx) {
    duk_compiler_func *func;
    func = &comp_ctx->curr_func;
    return (duk_int_t) (DUK_BW_GET_SIZE(comp_ctx->thr, &func->bw_code) / sizeof(duk_compiler_instr));
}

DUK_LOCAL duk_compiler_instr *duk__get_instr_ptr(duk_compiler_ctx *comp_ctx, duk_int_t pc) {
    DUK_ASSERT(pc >= 0);
    DUK_ASSERT((duk_size_t) pc < (duk_size_t) (DUK_BW_GET_SIZE(comp_ctx->thr, &comp_ctx->curr_func.bw_code) / sizeof(duk_compiler_instr)));
    return ((duk_compiler_instr *) (void *) DUK_BW_GET_BASEPTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code)) + pc;
}

/* emit instruction; could return PC but that's not needed in the majority
 * of cases.
 */
DUK_LOCAL void duk__emit(duk_compiler_ctx *comp_ctx, duk_instr_t ins) {
#if defined(DUK_USE_PC2LINE)
    duk_int_t line;
#endif
    duk_compiler_instr *instr;

    DUK_DDD(DUK_DDDPRINT("duk__emit: 0x%08lx curr_token.start_line=%ld prev_token.start_line=%ld pc=%ld --> %!I",
                         (unsigned long) ins,
                         (long) comp_ctx->curr_token.start_line,
                         (long) comp_ctx->prev_token.start_line,
                         (long) duk__get_current_pc(comp_ctx),
                         (duk_instr_t) ins));

    instr = (duk_compiler_instr *) (void *) DUK_BW_ENSURE_GETPTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code, sizeof(duk_compiler_instr));
    DUK_BW_ADD_PTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code, sizeof(duk_compiler_instr));

#if defined(DUK_USE_PC2LINE)
    /* The line number tracking is a bit inconsistent right now, which
     * affects debugger accuracy.  Mostly call sites emit opcodes when
     * they have parsed a token (say a terminating semicolon) and called
     * duk__advance().  In this case the line number of the previous
     * token is the most accurate one (except in prologue where
     * prev_token.start_line is 0).  This is probably not 100% correct
     * right now.
     */
    /* approximation, close enough */
    line = comp_ctx->prev_token.start_line;
    if (line == 0) {
        line = comp_ctx->curr_token.start_line;
    }
#endif

    instr->ins = ins;
#if defined(DUK_USE_PC2LINE)
    instr->line = (duk_uint32_t) line;
#endif
#if defined(DUK_USE_DEBUGGER_SUPPORT)
    if (line < comp_ctx->curr_func.min_line) {
        comp_ctx->curr_func.min_line = line;
    }
    if (line > comp_ctx->curr_func.max_line) {
        comp_ctx->curr_func.max_line = line;
    }
#endif

    /* Limit checks for bytecode byte size and line number. */
    if (DUK_UNLIKELY(DUK_BW_GET_SIZE(comp_ctx->thr, &comp_ctx->curr_func.bw_code) > DUK_USE_ESBC_MAX_BYTES)) {
        goto fail_bc_limit;
    }
#if defined(DUK_USE_PC2LINE) && defined(DUK_USE_ESBC_LIMITS)
#if defined(DUK_USE_BUFLEN16)
    /* Buffer length is bounded to 0xffff automatically, avoid compile warning. */
    if (DUK_UNLIKELY(line > DUK_USE_ESBC_MAX_LINENUMBER)) {
        goto fail_bc_limit;
    }
#else
    if (DUK_UNLIKELY(line > DUK_USE_ESBC_MAX_LINENUMBER)) {
        goto fail_bc_limit;
    }
#endif
#endif

    return;

  fail_bc_limit:
    DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_BYTECODE_LIMIT);
    DUK_WO_NORETURN(return;);
}

/* Update function min/max line from current token.  Needed to improve
 * function line range information for debugging, so that e.g. opening
 * curly brace is covered by line range even when no opcodes are emitted
 * for the line containing the brace.
 */
DUK_LOCAL void duk__update_lineinfo_currtoken(duk_compiler_ctx *comp_ctx) {
#if defined(DUK_USE_DEBUGGER_SUPPORT)
    duk_int_t line;

    line = comp_ctx->curr_token.start_line;
    if (line == 0) {
        return;
    }
    if (line < comp_ctx->curr_func.min_line) {
        comp_ctx->curr_func.min_line = line;
    }
    if (line > comp_ctx->curr_func.max_line) {
        comp_ctx->curr_func.max_line = line;
    }
#else
    DUK_UNREF(comp_ctx);
#endif
}

DUK_LOCAL void duk__emit_op_only(duk_compiler_ctx *comp_ctx, duk_small_uint_t op) {
    duk__emit(comp_ctx, DUK_ENC_OP_ABC(op, 0));
}

/* Important main primitive. */
DUK_LOCAL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a, duk_regconst_t b, duk_regconst_t c) {
    duk_instr_t ins = 0;
    duk_int_t a_out = -1;
    duk_int_t b_out = -1;
    duk_int_t c_out = -1;
    duk_int_t tmp;
    duk_small_uint_t op = op_flags & 0xffU;

    DUK_DDD(DUK_DDDPRINT("emit: op_flags=%04lx, a=%ld, b=%ld, c=%ld",
                         (unsigned long) op_flags, (long) a, (long) b, (long) c));

    /* We could rely on max temp/const checks: if they don't exceed BC
     * limit, nothing here can either (just asserts would be enough).
     * Currently we check for the limits, which provides additional
     * protection against creating invalid bytecode due to compiler
     * bugs.
     */

    DUK_ASSERT_DISABLE((op_flags & 0xff) >= DUK_BC_OP_MIN);  /* unsigned */
    DUK_ASSERT((op_flags & 0xff) <= DUK_BC_OP_MAX);
    DUK_ASSERT(DUK__ISREG(a));
    DUK_ASSERT(b != -1);  /* Not 'none'. */
    DUK_ASSERT(c != -1);  /* Not 'none'. */

    /* Input shuffling happens before the actual operation, while output
     * shuffling happens afterwards.  Output shuffling decisions are still
     * made at the same time to reduce branch clutter; output shuffle decisions
     * are recorded into X_out variables.
     */

    /* Slot A: currently no support for reg/const. */

#if defined(DUK_USE_SHUFFLE_TORTURE)
    if (a <= DUK_BC_A_MAX && (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_A)) {
#else
    if (a <= DUK_BC_A_MAX) {
#endif
        ;
    } else if (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_A) {
        DUK_D(DUK_DPRINT("out of regs: 'a' (reg) needs shuffling but shuffle prohibited, a: %ld", (long) a));
        goto error_outofregs;
    } else if (a <= DUK_BC_BC_MAX) {
        comp_ctx->curr_func.needs_shuffle = 1;
        tmp = comp_ctx->curr_func.shuffle1;
        if (op_flags & DUK__EMIT_FLAG_A_IS_SOURCE) {
            duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDREG, tmp, a));
        } else {
            /* Output shuffle needed after main operation */
            a_out = a;

            /* The DUK_OP_CSVAR output shuffle assumes shuffle registers are
             * consecutive.
             */
            DUK_ASSERT((comp_ctx->curr_func.shuffle1 == 0 && comp_ctx->curr_func.shuffle2 == 0) ||
                       (comp_ctx->curr_func.shuffle2 == comp_ctx->curr_func.shuffle1 + 1));
            if (op == DUK_OP_CSVAR) {
                /* For CSVAR the limit is one smaller because output shuffle
                 * must be able to express 'a + 1' in BC.
                 */
                if (a + 1 > DUK_BC_BC_MAX) {
                    goto error_outofregs;
                }
            }
        }
        a = tmp;
    } else {
        DUK_D(DUK_DPRINT("out of regs: 'a' (reg) needs shuffling but does not fit into BC, a: %ld", (long) a));
        goto error_outofregs;
    }

    /* Slot B: reg/const support, mapped to bit 0 of opcode. */

    if ((b & DUK__CONST_MARKER) != 0) {
        DUK_ASSERT((op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_B) == 0);
        DUK_ASSERT((op_flags & DUK__EMIT_FLAG_B_IS_TARGET) == 0);
        b = b & ~DUK__CONST_MARKER;
#if defined(DUK_USE_SHUFFLE_TORTURE)
        if (0) {
#else
        if (b <= 0xff) {
#endif
            if (op_flags & DUK__EMIT_FLAG_BC_REGCONST) {
                /* Opcode follows B/C reg/const convention. */
                DUK_ASSERT((op & 0x01) == 0);
                ins |= DUK_ENC_OP_A_B_C(0x01, 0, 0, 0);  /* const flag for B */
            } else {
                DUK_D(DUK_DPRINT("B is const, opcode is not B/C reg/const: %x", op_flags));
            }
        } else if (b <= DUK_BC_BC_MAX) {
            comp_ctx->curr_func.needs_shuffle = 1;
            tmp = comp_ctx->curr_func.shuffle2;
            duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDCONST, tmp, b));
            b = tmp;
        } else {
            DUK_D(DUK_DPRINT("out of regs: 'b' (const) needs shuffling but does not fit into BC, b: %ld", (long) b));
            goto error_outofregs;
        }
    } else {
#if defined(DUK_USE_SHUFFLE_TORTURE)
        if (b <= 0xff && (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_B)) {
#else
        if (b <= 0xff) {
#endif
            ;
        } else if (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_B) {
            if (b > DUK_BC_B_MAX) {
                /* Note: 0xff != DUK_BC_B_MAX */
                DUK_D(DUK_DPRINT("out of regs: 'b' (reg) needs shuffling but shuffle prohibited, b: %ld", (long) b));
                goto error_outofregs;
            }
        } else if (b <= DUK_BC_BC_MAX) {
            comp_ctx->curr_func.needs_shuffle = 1;
            tmp = comp_ctx->curr_func.shuffle2;
            if (op_flags & DUK__EMIT_FLAG_B_IS_TARGET) {
                /* Output shuffle needed after main operation */
                b_out = b;
            }
            if (!(op_flags & DUK__EMIT_FLAG_B_IS_TARGET)) {
                if (op == DUK_OP_MPUTOBJ || op == DUK_OP_MPUTARR) {
                    /* Special handling for MPUTOBJ/MPUTARR shuffling.
                     * For each, slot B identifies the first register of a range
                     * of registers, so normal shuffling won't work.  Instead,
                     * an indirect version of the opcode is used.
                     */
                    DUK_ASSERT((op_flags & DUK__EMIT_FLAG_B_IS_TARGET) == 0);
                    duk__emit_load_int32_noshuffle(comp_ctx, tmp, b);
                    DUK_ASSERT(DUK_OP_MPUTOBJI == DUK_OP_MPUTOBJ + 1);
                    DUK_ASSERT(DUK_OP_MPUTARRI == DUK_OP_MPUTARR + 1);
                    op_flags++;  /* indirect opcode follows direct */
                } else {
                    duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDREG, tmp, b));
                }
            }
            b = tmp;
        } else {
            DUK_D(DUK_DPRINT("out of regs: 'b' (reg) needs shuffling but does not fit into BC, b: %ld", (long) b));
            goto error_outofregs;
        }
    }

    /* Slot C: reg/const support, mapped to bit 1 of opcode. */

    if ((c & DUK__CONST_MARKER) != 0) {
        DUK_ASSERT((op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_C) == 0);
        DUK_ASSERT((op_flags & DUK__EMIT_FLAG_C_IS_TARGET) == 0);
        c = c & ~DUK__CONST_MARKER;
#if defined(DUK_USE_SHUFFLE_TORTURE)
        if (0) {
#else
        if (c <= 0xff) {
#endif
            if (op_flags & DUK__EMIT_FLAG_BC_REGCONST) {
                /* Opcode follows B/C reg/const convention. */
                DUK_ASSERT((op & 0x02) == 0);
                ins |= DUK_ENC_OP_A_B_C(0x02, 0, 0, 0);  /* const flag for C */
            } else {
                DUK_D(DUK_DPRINT("C is const, opcode is not B/C reg/const: %x", op_flags));
            }
        } else if (c <= DUK_BC_BC_MAX) {
            comp_ctx->curr_func.needs_shuffle = 1;
            tmp = comp_ctx->curr_func.shuffle3;
            duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDCONST, tmp, c));
            c = tmp;
        } else {
            DUK_D(DUK_DPRINT("out of regs: 'c' (const) needs shuffling but does not fit into BC, c: %ld", (long) c));
            goto error_outofregs;
        }
    } else {
#if defined(DUK_USE_SHUFFLE_TORTURE)
        if (c <= 0xff && (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_C)) {
#else
        if (c <= 0xff) {
#endif
            ;
        } else if (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_C) {
            if (c > DUK_BC_C_MAX) {
                /* Note: 0xff != DUK_BC_C_MAX */
                DUK_D(DUK_DPRINT("out of regs: 'c' (reg) needs shuffling but shuffle prohibited, c: %ld", (long) c));
                goto error_outofregs;
            }
        } else if (c <= DUK_BC_BC_MAX) {
            comp_ctx->curr_func.needs_shuffle = 1;
            tmp = comp_ctx->curr_func.shuffle3;
            if (op_flags & DUK__EMIT_FLAG_C_IS_TARGET) {
                /* Output shuffle needed after main operation */
                c_out = c;
            } else {
                duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDREG, tmp, c));
            }
            c = tmp;
        } else {
            DUK_D(DUK_DPRINT("out of regs: 'c' (reg) needs shuffling but does not fit into BC, c: %ld", (long) c));
            goto error_outofregs;
        }
    }

    /* Main operation */

    DUK_ASSERT(a >= DUK_BC_A_MIN);
    DUK_ASSERT(a <= DUK_BC_A_MAX);
    DUK_ASSERT(b >= DUK_BC_B_MIN);
    DUK_ASSERT(b <= DUK_BC_B_MAX);
    DUK_ASSERT(c >= DUK_BC_C_MIN);
    DUK_ASSERT(c <= DUK_BC_C_MAX);

    ins |= DUK_ENC_OP_A_B_C(op_flags & 0xff, a, b, c);
    duk__emit(comp_ctx, ins);

    /* NEXTENUM needs a jump slot right after the main instruction.
     * When the JUMP is taken, output spilling is not needed so this
     * workaround is possible.  The jump slot PC is exceptionally
     * plumbed through comp_ctx to minimize call sites.
     */
    if (op_flags & DUK__EMIT_FLAG_RESERVE_JUMPSLOT) {
        comp_ctx->emit_jumpslot_pc = duk__get_current_pc(comp_ctx);
        duk__emit_abc(comp_ctx, DUK_OP_JUMP, 0);
    }

    /* Output shuffling: only one output register is realistically possible.
     *
     * (Zero would normally be an OK marker value: if the target register
     * was zero, it would never be shuffled.  But with DUK_USE_SHUFFLE_TORTURE
     * this is no longer true, so use -1 as a marker instead.)
     */

    if (a_out >= 0) {
        DUK_ASSERT(b_out < 0);
        DUK_ASSERT(c_out < 0);
        duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_STREG, a, a_out));

        if (op == DUK_OP_CSVAR) {
            /* Special handling for CSVAR shuffling.  The variable lookup
             * results in a <value, this binding> pair in successive
             * registers so use two shuffle registers and two output
             * loads.  (In practice this is dead code because temp/const
             * limit is reached first.)
             */
            duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_STREG, a + 1, a_out + 1));
        }
    } else if (b_out >= 0) {
        DUK_ASSERT(a_out < 0);
        DUK_ASSERT(c_out < 0);
        duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_STREG, b, b_out));
    } else if (c_out >= 0) {
        DUK_ASSERT(b_out < 0);
        DUK_ASSERT(c_out < 0);
        duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_STREG, c, c_out));
    }

    return;

 error_outofregs:
    DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_REG_LIMIT);
    DUK_WO_NORETURN(return;);
}

/* For many of the helpers below it'd be technically correct to add
 * "no shuffle" flags for parameters passed in as zero.  For example,
 * duk__emit_a_b() should call duk__emit_a_b_c() with C set to 0, and
 * DUK__EMIT_FLAG_NO_SHUFFLE_C added to op_flags.  However, since the
 * C value is 0, it'll never get shuffled so adding the flag is just
 * unnecessary additional code.  This is unfortunately not true for
 * "shuffle torture" mode which needs special handling.
 */

DUK_LOCAL void duk__emit_a_b(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a, duk_regconst_t b) {
#if defined(DUK_USE_SHUFFLE_TORTURE)
    op_flags |= DUK__EMIT_FLAG_NO_SHUFFLE_C;
#endif
    duk__emit_a_b_c(comp_ctx, op_flags, a, b, 0);
}

DUK_LOCAL void duk__emit_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t b, duk_regconst_t c) {
#if defined(DUK_USE_SHUFFLE_TORTURE)
    op_flags |= DUK__EMIT_FLAG_NO_SHUFFLE_A;
#endif
    duk__emit_a_b_c(comp_ctx, op_flags, 0, b, c);
}

#if 0  /* unused */
DUK_LOCAL void duk__emit_a(duk_compiler_ctx *comp_ctx, int op_flags, int a) {
#if defined(DUK_USE_SHUFFLE_TORTURE)
    op_flags |= DUK__EMIT_FLAG_NO_SHUFFLE_B | DUK__EMIT_FLAG_NO_SHUFFLE_C;
#endif
    duk__emit_a_b_c(comp_ctx, op_flags, a, 0, 0);
}
#endif

#if 0  /* unused */
DUK_LOCAL void duk__emit_b(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t b) {
#if defined(DUK_USE_SHUFFLE_TORTURE)
    op_flags |= DUK__EMIT_FLAG_NO_SHUFFLE_A | DUK__EMIT_FLAG_NO_SHUFFLE_C;
#endif
    duk__emit_a_b_c(comp_ctx, op_flags, 0, b, 0);
}
#endif

DUK_LOCAL void duk__emit_a_bc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a, duk_regconst_t bc) {
    duk_instr_t ins;
    duk_int_t tmp;

    /* allow caller to give a const number with the DUK__CONST_MARKER */
    DUK_ASSERT(bc != -1);  /* Not 'none'. */
    bc = bc & (~DUK__CONST_MARKER);

    DUK_ASSERT_DISABLE((op_flags & 0xff) >= DUK_BC_OP_MIN);  /* unsigned */
    DUK_ASSERT((op_flags & 0xff) <= DUK_BC_OP_MAX);
    DUK_ASSERT(bc >= DUK_BC_BC_MIN);
    DUK_ASSERT(bc <= DUK_BC_BC_MAX);
    DUK_ASSERT((bc & DUK__CONST_MARKER) == 0);

    if (bc <= DUK_BC_BC_MAX) {
        ;
    } else {
        /* No BC shuffling now. */
        goto error_outofregs;
    }

#if defined(DUK_USE_SHUFFLE_TORTURE)
    if (a <= DUK_BC_A_MAX && (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_A)) {
#else
    if (a <= DUK_BC_A_MAX) {
#endif
        ins = DUK_ENC_OP_A_BC(op_flags & 0xff, a, bc);
        duk__emit(comp_ctx, ins);
    } else if (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_A) {
        goto error_outofregs;
    } else if ((op_flags & 0xf0U) == DUK_OP_CALL0) {
        comp_ctx->curr_func.needs_shuffle = 1;
        tmp = comp_ctx->curr_func.shuffle1;
        duk__emit_load_int32_noshuffle(comp_ctx, tmp, a);
        op_flags |= DUK_BC_CALL_FLAG_INDIRECT;
        ins = DUK_ENC_OP_A_BC(op_flags & 0xff, tmp, bc);
        duk__emit(comp_ctx, ins);
    } else if (a <= DUK_BC_BC_MAX) {
        comp_ctx->curr_func.needs_shuffle = 1;
        tmp = comp_ctx->curr_func.shuffle1;
        ins = DUK_ENC_OP_A_BC(op_flags & 0xff, tmp, bc);
        if (op_flags & DUK__EMIT_FLAG_A_IS_SOURCE) {
            duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDREG, tmp, a));
            duk__emit(comp_ctx, ins);
        } else {
            duk__emit(comp_ctx, ins);
            duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_STREG, tmp, a));
        }
    } else {
        goto error_outofregs;
    }
    return;

 error_outofregs:
    DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_REG_LIMIT);
    DUK_WO_NORETURN(return;);
}

DUK_LOCAL void duk__emit_bc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op, duk_regconst_t bc) {
#if defined(DUK_USE_SHUFFLE_TORTURE)
    op |= DUK__EMIT_FLAG_NO_SHUFFLE_A;
#endif
    duk__emit_a_bc(comp_ctx, op, 0, bc);
}

DUK_LOCAL void duk__emit_abc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op, duk_regconst_t abc) {
    duk_instr_t ins;

    DUK_ASSERT_DISABLE(op >= DUK_BC_OP_MIN);  /* unsigned */
    DUK_ASSERT(op <= DUK_BC_OP_MAX);
    DUK_ASSERT_DISABLE(abc >= DUK_BC_ABC_MIN);  /* unsigned */
    DUK_ASSERT(abc <= DUK_BC_ABC_MAX);
    DUK_ASSERT((abc & DUK__CONST_MARKER) == 0);
    DUK_ASSERT(abc != -1);  /* Not 'none'. */

    if (abc <= DUK_BC_ABC_MAX) {
        ;
    } else {
        goto error_outofregs;
    }
    ins = DUK_ENC_OP_ABC(op, abc);
    DUK_DDD(DUK_DDDPRINT("duk__emit_abc: 0x%08lx line=%ld pc=%ld op=%ld (%!X) abc=%ld (%!I)",
                         (unsigned long) ins, (long) comp_ctx->curr_token.start_line,
                         (long) duk__get_current_pc(comp_ctx), (long) op, (long) op,
                         (long) abc, (duk_instr_t) ins));
    duk__emit(comp_ctx, ins);
    return;

 error_outofregs:
    DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_REG_LIMIT);
    DUK_WO_NORETURN(return;);
}

DUK_LOCAL void duk__emit_load_int32_raw(duk_compiler_ctx *comp_ctx, duk_regconst_t reg, duk_int32_t val, duk_small_uint_t op_flags) {
    /* XXX: Shuffling support could be implemented here so that LDINT+LDINTX
     * would only shuffle once (instead of twice).  The current code works
     * though, and has a smaller compiler footprint.
     */

    if ((val >= (duk_int32_t) DUK_BC_BC_MIN - (duk_int32_t) DUK_BC_LDINT_BIAS) &&
        (val <= (duk_int32_t) DUK_BC_BC_MAX - (duk_int32_t) DUK_BC_LDINT_BIAS)) {
        DUK_DDD(DUK_DDDPRINT("emit LDINT to reg %ld for %ld", (long) reg, (long) val));
        duk__emit_a_bc(comp_ctx, DUK_OP_LDINT | op_flags, reg, (duk_regconst_t) (val + (duk_int32_t) DUK_BC_LDINT_BIAS));
    } else {
        duk_int32_t hi = val >> DUK_BC_LDINTX_SHIFT;
        duk_int32_t lo = val & ((((duk_int32_t) 1) << DUK_BC_LDINTX_SHIFT) - 1);
        DUK_ASSERT(lo >= 0);
        DUK_DDD(DUK_DDDPRINT("emit LDINT+LDINTX to reg %ld for %ld -> hi %ld, lo %ld",
                             (long) reg, (long) val, (long) hi, (long) lo));
        duk__emit_a_bc(comp_ctx, DUK_OP_LDINT | op_flags, reg, (duk_regconst_t) (hi + (duk_int32_t) DUK_BC_LDINT_BIAS));
        duk__emit_a_bc(comp_ctx, DUK_OP_LDINTX | op_flags, reg, (duk_regconst_t) lo);
    }
}

DUK_LOCAL void duk__emit_load_int32(duk_compiler_ctx *comp_ctx, duk_regconst_t reg, duk_int32_t val) {
    duk__emit_load_int32_raw(comp_ctx, reg, val, 0 /*op_flags*/);
}

#if defined(DUK_USE_SHUFFLE_TORTURE)
/* Used by duk__emit*() calls so that we don't shuffle the loadints that
 * are needed to handle indirect opcodes.
 */
DUK_LOCAL void duk__emit_load_int32_noshuffle(duk_compiler_ctx *comp_ctx, duk_regconst_t reg, duk_int32_t val) {
    duk__emit_load_int32_raw(comp_ctx, reg, val, DUK__EMIT_FLAG_NO_SHUFFLE_A /*op_flags*/);
}
#else
DUK_LOCAL void duk__emit_load_int32_noshuffle(duk_compiler_ctx *comp_ctx, duk_regconst_t reg, duk_int32_t val) {
    /* When torture not enabled, can just use the same helper because
     * 'reg' won't get spilled.
     */
    DUK_ASSERT(reg <= DUK_BC_A_MAX);
    duk__emit_load_int32(comp_ctx, reg, val);
}
#endif

DUK_LOCAL void duk__emit_jump(duk_compiler_ctx *comp_ctx, duk_int_t target_pc) {
    duk_int_t curr_pc;
    duk_int_t offset;

    curr_pc = (duk_int_t) (DUK_BW_GET_SIZE(comp_ctx->thr, &comp_ctx->curr_func.bw_code) / sizeof(duk_compiler_instr));
    offset = (duk_int_t) target_pc - (duk_int_t) curr_pc - 1;
    DUK_ASSERT(offset + DUK_BC_JUMP_BIAS >= DUK_BC_ABC_MIN);
    DUK_ASSERT(offset + DUK_BC_JUMP_BIAS <= DUK_BC_ABC_MAX);
    duk__emit_abc(comp_ctx, DUK_OP_JUMP, (duk_regconst_t) (offset + DUK_BC_JUMP_BIAS));
}

DUK_LOCAL duk_int_t duk__emit_jump_empty(duk_compiler_ctx *comp_ctx) {
    duk_int_t ret;

    ret = duk__get_current_pc(comp_ctx);  /* useful for patching jumps later */
    duk__emit_op_only(comp_ctx, DUK_OP_JUMP);
    return ret;
}

/* Insert an empty jump in the middle of code emitted earlier.  This is
 * currently needed for compiling for-in.
 */
DUK_LOCAL void duk__insert_jump_entry(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc) {
#if defined(DUK_USE_PC2LINE)
    duk_int_t line;
#endif
    duk_compiler_instr *instr;
    duk_size_t offset;

    DUK_ASSERT(jump_pc >= 0);
    offset = (duk_size_t) jump_pc * sizeof(duk_compiler_instr);
    instr = (duk_compiler_instr *) (void *)
            DUK_BW_INSERT_ENSURE_AREA(comp_ctx->thr,
                                      &comp_ctx->curr_func.bw_code,
                                      offset,
                                      sizeof(duk_compiler_instr));

#if defined(DUK_USE_PC2LINE)
    line = comp_ctx->curr_token.start_line;  /* approximation, close enough */
#endif
    instr->ins = DUK_ENC_OP_ABC(DUK_OP_JUMP, 0);
#if defined(DUK_USE_PC2LINE)
    instr->line = (duk_uint32_t) line;
#endif

    DUK_BW_ADD_PTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code, sizeof(duk_compiler_instr));
    if (DUK_UNLIKELY(DUK_BW_GET_SIZE(comp_ctx->thr, &comp_ctx->curr_func.bw_code) > DUK_USE_ESBC_MAX_BYTES)) {
        goto fail_bc_limit;
    }
    return;

  fail_bc_limit:
    DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_BYTECODE_LIMIT);
    DUK_WO_NORETURN(return;);
}

/* Does not assume that jump_pc contains a DUK_OP_JUMP previously; this is intentional
 * to allow e.g. an INVALID opcode be overwritten with a JUMP (label management uses this).
 */
DUK_LOCAL void duk__patch_jump(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc, duk_int_t target_pc) {
    duk_compiler_instr *instr;
    duk_int_t offset;

    /* allow negative PCs, behave as a no-op */
    if (jump_pc < 0) {
        DUK_DDD(DUK_DDDPRINT("duk__patch_jump(): nop call, jump_pc=%ld (<0), target_pc=%ld",
                             (long) jump_pc, (long) target_pc));
        return;
    }
    DUK_ASSERT(jump_pc >= 0);

    /* XXX: range assert */
    instr = duk__get_instr_ptr(comp_ctx, jump_pc);
    DUK_ASSERT(instr != NULL);

    /* XXX: range assert */
    offset = target_pc - jump_pc - 1;

    instr->ins = DUK_ENC_OP_ABC(DUK_OP_JUMP, offset + DUK_BC_JUMP_BIAS);
    DUK_DDD(DUK_DDDPRINT("duk__patch_jump(): jump_pc=%ld, target_pc=%ld, offset=%ld",
                         (long) jump_pc, (long) target_pc, (long) offset));
}

DUK_LOCAL void duk__patch_jump_here(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc) {
    duk__patch_jump(comp_ctx, jump_pc, duk__get_current_pc(comp_ctx));
}

DUK_LOCAL void duk__patch_trycatch(duk_compiler_ctx *comp_ctx, duk_int_t ldconst_pc, duk_int_t trycatch_pc, duk_regconst_t reg_catch, duk_regconst_t const_varname, duk_small_uint_t flags) {
    duk_compiler_instr *instr;

    DUK_ASSERT(DUK__ISREG(reg_catch));

    instr = duk__get_instr_ptr(comp_ctx, ldconst_pc);
    DUK_ASSERT(DUK_DEC_OP(instr->ins) == DUK_OP_LDCONST);
    DUK_ASSERT(instr != NULL);
    if (const_varname & DUK__CONST_MARKER) {
        /* Have a catch variable. */
        const_varname = const_varname & (~DUK__CONST_MARKER);
        if (reg_catch > DUK_BC_BC_MAX || const_varname > DUK_BC_BC_MAX) {
            /* Catch attempts to use out-of-range reg/const.  Without this
             * check Duktape 0.12.0 could generate invalid code which caused
             * an assert failure on execution.  This error is triggered e.g.
             * for functions with a lot of constants and a try-catch statement.
             * Shuffling or opcode semantics change is needed to fix the issue.
             * See: test-bug-trycatch-many-constants.js.
             */
            DUK_D(DUK_DPRINT("failed to patch trycatch: flags=%ld, reg_catch=%ld, const_varname=%ld (0x%08lx)",
                             (long) flags, (long) reg_catch, (long) const_varname, (long) const_varname));
            DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_REG_LIMIT);
            DUK_WO_NORETURN(return;);
        }
        instr->ins |= DUK_ENC_OP_A_BC(0, 0, const_varname);
    } else {
        /* No catch variable, e.g. a try-finally; replace LDCONST with
         * NOP to avoid a bogus LDCONST.
         */
        instr->ins = DUK_ENC_OP(DUK_OP_NOP);
    }

    instr = duk__get_instr_ptr(comp_ctx, trycatch_pc);
    DUK_ASSERT(instr != NULL);
    DUK_ASSERT_DISABLE(flags >= DUK_BC_A_MIN);
    DUK_ASSERT(flags <= DUK_BC_A_MAX);
    instr->ins = DUK_ENC_OP_A_BC(DUK_OP_TRYCATCH, flags, reg_catch);
}

DUK_LOCAL void duk__emit_if_false_skip(duk_compiler_ctx *comp_ctx, duk_regconst_t regconst) {
    duk_small_uint_t op;

    op = DUK__ISREG(regconst) ? DUK_OP_IFFALSE_R : DUK_OP_IFFALSE_C;
    duk__emit_bc(comp_ctx, op, regconst);  /* helper will remove const flag */
}

DUK_LOCAL void duk__emit_if_true_skip(duk_compiler_ctx *comp_ctx, duk_regconst_t regconst) {
    duk_small_uint_t op;

    op = DUK__ISREG(regconst) ? DUK_OP_IFTRUE_R : DUK_OP_IFTRUE_C;
    duk__emit_bc(comp_ctx, op, regconst);  /* helper will remove const flag */
}

DUK_LOCAL void duk__emit_invalid(duk_compiler_ctx *comp_ctx) {
    duk__emit_op_only(comp_ctx, DUK_OP_INVALID);
}

/*
 *  Peephole optimizer for finished bytecode.
 *
 *  Does not remove opcodes; currently only straightens out unconditional
 *  jump chains which are generated by several control structures.
 */

DUK_LOCAL void duk__peephole_optimize_bytecode(duk_compiler_ctx *comp_ctx) {
    duk_compiler_instr *bc;
    duk_small_uint_t iter;
    duk_int_t i, n;
    duk_int_t count_opt;

    bc = (duk_compiler_instr *) (void *) DUK_BW_GET_BASEPTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code);
#if defined(DUK_USE_BUFLEN16)
    /* No need to assert, buffer size maximum is 0xffff. */
#else
    DUK_ASSERT((duk_size_t) DUK_BW_GET_SIZE(comp_ctx->thr, &comp_ctx->curr_func.bw_code) / sizeof(duk_compiler_instr) <= (duk_size_t) DUK_INT_MAX);  /* bytecode limits */
#endif
    n = (duk_int_t) (DUK_BW_GET_SIZE(comp_ctx->thr, &comp_ctx->curr_func.bw_code) / sizeof(duk_compiler_instr));

    for (iter = 0; iter < DUK_COMPILER_PEEPHOLE_MAXITER; iter++) {
        count_opt = 0;

        for (i = 0; i < n; i++) {
            duk_instr_t ins;
            duk_int_t target_pc1;
            duk_int_t target_pc2;

            ins = bc[i].ins;
            if (DUK_DEC_OP(ins) != DUK_OP_JUMP) {
                continue;
            }

            target_pc1 = i + 1 + (duk_int_t) DUK_DEC_ABC(ins) - (duk_int_t) DUK_BC_JUMP_BIAS;
            DUK_DDD(DUK_DDDPRINT("consider jump at pc %ld; target_pc=%ld", (long) i, (long) target_pc1));
            DUK_ASSERT(target_pc1 >= 0);
            DUK_ASSERT(target_pc1 < n);

            /* Note: if target_pc1 == i, we'll optimize a jump to itself.
             * This does not need to be checked for explicitly; the case
             * is rare and max iter breaks us out.
             */

            ins = bc[target_pc1].ins;
            if (DUK_DEC_OP(ins) != DUK_OP_JUMP) {
                continue;
            }

            target_pc2 = target_pc1 + 1 + (duk_int_t) DUK_DEC_ABC(ins) - (duk_int_t) DUK_BC_JUMP_BIAS;

            DUK_DDD(DUK_DDDPRINT("optimizing jump at pc %ld; old target is %ld -> new target is %ld",
                                 (long) i, (long) target_pc1, (long) target_pc2));

            bc[i].ins = DUK_ENC_OP_ABC(DUK_OP_JUMP, target_pc2 - (i + 1) + DUK_BC_JUMP_BIAS);

            count_opt++;
        }

        DUK_DD(DUK_DDPRINT("optimized %ld jumps on peephole round %ld", (long) count_opt, (long) (iter + 1)));

        if (count_opt == 0) {
            break;
        }
    }
}

/*
 *  Intermediate value helpers
 */

/* Flags for intermediate value coercions.  A flag for using a forced reg
 * is not needed, the forced_reg argument suffices and generates better
 * code (it is checked as it is used).
 */
/* XXX: DUK__IVAL_FLAG_REQUIRE_SHORT is passed but not currently implemented
 * by ispec/ivalue operations.
 */
#define DUK__IVAL_FLAG_ALLOW_CONST          (1 << 0)  /* allow a constant to be returned */
#define DUK__IVAL_FLAG_REQUIRE_TEMP         (1 << 1)  /* require a (mutable) temporary as a result (or a const if allowed) */
#define DUK__IVAL_FLAG_REQUIRE_SHORT        (1 << 2)  /* require a short (8-bit) reg/const which fits into bytecode B/C slot */

/* XXX: some code might benefit from DUK__SETTEMP_IFTEMP(thr,x) */

#if 0  /* enable manually for dumping */
#define DUK__DUMP_ISPEC(compctx,ispec) do { duk__dump_ispec((compctx), (ispec)); } while (0)
#define DUK__DUMP_IVALUE(compctx,ivalue) do { duk__dump_ivalue((compctx), (ivalue)); } while (0)

DUK_LOCAL void duk__dump_ispec(duk_compiler_ctx *comp_ctx, duk_ispec *x) {
    DUK_D(DUK_DPRINT("ispec dump: t=%ld regconst=0x%08lx, valstack_idx=%ld, value=%!T",
                     (long) x->t, (unsigned long) x->regconst, (long) x->valstack_idx,
                     duk_get_tval(comp_ctx->thr, x->valstack_idx)));
}
DUK_LOCAL void duk__dump_ivalue(duk_compiler_ctx *comp_ctx, duk_ivalue *x) {
    DUK_D(DUK_DPRINT("ivalue dump: t=%ld op=%ld "
                     "x1={t=%ld regconst=0x%08lx valstack_idx=%ld value=%!T} "
                     "x2={t=%ld regconst=0x%08lx valstack_idx=%ld value=%!T}",
                 (long) x->t, (long) x->op,
                     (long) x->x1.t, (unsigned long) x->x1.regconst, (long) x->x1.valstack_idx,
                     duk_get_tval(comp_ctx->thr, x->x1.valstack_idx),
                     (long) x->x2.t, (unsigned long) x->x2.regconst, (long) x->x2.valstack_idx,
                     duk_get_tval(comp_ctx->thr, x->x2.valstack_idx)));
}
#else
#define DUK__DUMP_ISPEC(comp_ctx,x) do {} while (0)
#define DUK__DUMP_IVALUE(comp_ctx,x) do {} while (0)
#endif

DUK_LOCAL void duk__ivalue_regconst(duk_ivalue *x, duk_regconst_t regconst) {
    x->t = DUK_IVAL_PLAIN;
    x->x1.t = DUK_ISPEC_REGCONST;
    x->x1.regconst = regconst;
}

DUK_LOCAL void duk__ivalue_plain_fromstack(duk_compiler_ctx *comp_ctx, duk_ivalue *x) {
    x->t = DUK_IVAL_PLAIN;
    x->x1.t = DUK_ISPEC_VALUE;
    duk_replace(comp_ctx->thr, x->x1.valstack_idx);
}

DUK_LOCAL void duk__ivalue_var_fromstack(duk_compiler_ctx *comp_ctx, duk_ivalue *x) {
    x->t = DUK_IVAL_VAR;
    x->x1.t = DUK_ISPEC_VALUE;
    duk_replace(comp_ctx->thr, x->x1.valstack_idx);
}

DUK_LOCAL_DECL void duk__ivalue_var_hstring(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_hstring *h) {
    DUK_ASSERT(h != NULL);
    duk_push_hstring(comp_ctx->thr, h);
    duk__ivalue_var_fromstack(comp_ctx, x);
}

DUK_LOCAL void duk__copy_ispec(duk_compiler_ctx *comp_ctx, duk_ispec *src, duk_ispec *dst) {
    dst->t = src->t;
    dst->regconst = src->regconst;
    duk_copy(comp_ctx->thr, src->valstack_idx, dst->valstack_idx);
}

DUK_LOCAL void duk__copy_ivalue(duk_compiler_ctx *comp_ctx, duk_ivalue *src, duk_ivalue *dst) {
    dst->t = src->t;
    dst->op = src->op;
    dst->x1.t = src->x1.t;
    dst->x1.regconst = src->x1.regconst;
    dst->x2.t = src->x2.t;
    dst->x2.regconst = src->x2.regconst;
    duk_copy(comp_ctx->thr, src->x1.valstack_idx, dst->x1.valstack_idx);
    duk_copy(comp_ctx->thr, src->x2.valstack_idx, dst->x2.valstack_idx);
}

DUK_LOCAL duk_regconst_t duk__alloctemps(duk_compiler_ctx *comp_ctx, duk_small_int_t num) {
    duk_regconst_t res;

    res = comp_ctx->curr_func.temp_next;
    comp_ctx->curr_func.temp_next += num;

    if (comp_ctx->curr_func.temp_next > DUK__MAX_TEMPS) {  /* == DUK__MAX_TEMPS is OK */
        DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_TEMP_LIMIT);
        DUK_WO_NORETURN(return 0;);
    }

    /* maintain highest 'used' temporary, needed to figure out nregs of function */
    if (comp_ctx->curr_func.temp_next > comp_ctx->curr_func.temp_max) {
        comp_ctx->curr_func.temp_max = comp_ctx->curr_func.temp_next;
    }

    return res;
}

DUK_LOCAL duk_regconst_t duk__alloctemp(duk_compiler_ctx *comp_ctx) {
    return duk__alloctemps(comp_ctx, 1);
}

DUK_LOCAL void duk__settemp_checkmax(duk_compiler_ctx *comp_ctx, duk_regconst_t temp_next) {
    comp_ctx->curr_func.temp_next = temp_next;
    if (temp_next > comp_ctx->curr_func.temp_max) {
        comp_ctx->curr_func.temp_max = temp_next;
    }
}

/* get const for value at valstack top */
DUK_LOCAL duk_regconst_t duk__getconst(duk_compiler_ctx *comp_ctx) {
    duk_hthread *thr = comp_ctx->thr;
    duk_compiler_func *f = &comp_ctx->curr_func;
    duk_tval *tv1;
    duk_int_t i, n, n_check;

    n = (duk_int_t) duk_get_length(thr, f->consts_idx);

    tv1 = DUK_GET_TVAL_NEGIDX(thr, -1);
    DUK_ASSERT(tv1 != NULL);

#if defined(DUK_USE_FASTINT)
    /* Explicit check for fastint downgrade. */
    DUK_TVAL_CHKFAST_INPLACE_SLOW(tv1);
#endif

    /* Sanity workaround for handling functions with a large number of
     * constants at least somewhat reasonably.  Otherwise checking whether
     * we already have the constant would grow very slow (as it is O(N^2)).
     */
    n_check = (n > DUK__GETCONST_MAX_CONSTS_CHECK ? DUK__GETCONST_MAX_CONSTS_CHECK : n);
    for (i = 0; i < n_check; i++) {
        duk_tval *tv2 = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, f->h_consts, i);

        /* Strict equality is NOT enough, because we cannot use the same
         * constant for e.g. +0 and -0.
         */
        if (duk_js_samevalue(tv1, tv2)) {
            DUK_DDD(DUK_DDDPRINT("reused existing constant for %!T -> const index %ld",
                                 (duk_tval *) tv1, (long) i));
            duk_pop(thr);
            return (duk_regconst_t) i | (duk_regconst_t) DUK__CONST_MARKER;
        }
    }

    if (n > DUK__MAX_CONSTS) {
        DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_CONST_LIMIT);
        DUK_WO_NORETURN(return 0;);
    }

    DUK_DDD(DUK_DDDPRINT("allocating new constant for %!T -> const index %ld",
                         (duk_tval *) tv1, (long) n));
    (void) duk_put_prop_index(thr, f->consts_idx, (duk_uarridx_t) n);  /* invalidates tv1, tv2 */
    return (duk_regconst_t) n | (duk_regconst_t) DUK__CONST_MARKER;
}

DUK_LOCAL duk_bool_t duk__const_needs_refcount(duk_compiler_ctx *comp_ctx, duk_regconst_t rc) {
#if defined(DUK_USE_REFERENCE_COUNTING)
    duk_compiler_func *f = &comp_ctx->curr_func;
    duk_bool_t ret;

    DUK_ASSERT((rc & DUK__CONST_MARKER) == 0);  /* caller removes const marker */
    (void) duk_get_prop_index(comp_ctx->thr, f->consts_idx, (duk_uarridx_t) rc);
    ret = !duk_is_number(comp_ctx->thr, -1);  /* now only number/string, so conservative check */
    duk_pop(comp_ctx->thr);
    return ret;
#else
    DUK_UNREF(comp_ctx);
    DUK_UNREF(rc);
    DUK_ASSERT((rc & DUK__CONST_MARKER) == 0);  /* caller removes const marker */
    return 0;
#endif
}

/* Get the value represented by an duk_ispec to a register or constant.
 * The caller can control the result by indicating whether or not:
 *
 *   (1) a constant is allowed (sometimes the caller needs the result to
 *       be in a register)
 *
 *   (2) a temporary register is required (usually when caller requires
 *       the register to be safely mutable; normally either a bound
 *       register or a temporary register are both OK)
 *
 *   (3) a forced register target needs to be used
 *
 * Bytecode may be emitted to generate the necessary value.  The return
 * value is either a register or a constant.
 */

DUK_LOCAL
duk_regconst_t duk__ispec_toregconst_raw(duk_compiler_ctx *comp_ctx,
                                         duk_ispec *x,
                                         duk_regconst_t forced_reg,
                                         duk_small_uint_t flags) {
    duk_hthread *thr = comp_ctx->thr;

    DUK_DDD(DUK_DDDPRINT("duk__ispec_toregconst_raw(): x={%ld:%ld:%!T}, "
                         "forced_reg=%ld, flags 0x%08lx: allow_const=%ld require_temp=%ld require_short=%ld",
                         (long) x->t,
                         (long) x->regconst,
                         (duk_tval *) duk_get_tval(thr, x->valstack_idx),
                         (long) forced_reg,
                         (unsigned long) flags,
                         (long) ((flags & DUK__IVAL_FLAG_ALLOW_CONST) ? 1 : 0),
                         (long) ((flags & DUK__IVAL_FLAG_REQUIRE_TEMP) ? 1 : 0),
                         (long) ((flags & DUK__IVAL_FLAG_REQUIRE_SHORT) ? 1 : 0)));

    switch (x->t) {
    case DUK_ISPEC_VALUE: {
        duk_tval *tv;

        tv = DUK_GET_TVAL_POSIDX(thr, x->valstack_idx);
        DUK_ASSERT(tv != NULL);

        switch (DUK_TVAL_GET_TAG(tv)) {
        case DUK_TAG_UNDEFINED: {
            /* Note: although there is no 'undefined' literal, undefined
             * values can occur during compilation as a result of e.g.
             * the 'void' operator.
             */
            duk_regconst_t dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx));
            duk__emit_bc(comp_ctx, DUK_OP_LDUNDEF, dest);
            return dest;
        }
        case DUK_TAG_NULL: {
            duk_regconst_t dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx));
            duk__emit_bc(comp_ctx, DUK_OP_LDNULL, dest);
            return dest;
        }
        case DUK_TAG_BOOLEAN: {
            duk_regconst_t dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx));
            duk__emit_bc(comp_ctx,
                         (DUK_TVAL_GET_BOOLEAN(tv) ? DUK_OP_LDTRUE : DUK_OP_LDFALSE),
                         dest);
            return dest;
        }
        case DUK_TAG_POINTER: {
            DUK_UNREACHABLE();
            break;
        }
        case DUK_TAG_STRING: {
            duk_hstring *h;
            duk_regconst_t dest;
            duk_regconst_t constidx;

            h = DUK_TVAL_GET_STRING(tv);
            DUK_UNREF(h);
            DUK_ASSERT(h != NULL);

#if 0  /* XXX: to be implemented? */
            /* Use special opcodes to load short strings */
            if (DUK_HSTRING_GET_BYTELEN(h) <= 2) {
                /* Encode into a single opcode (18 bits can encode 1-2 bytes + length indicator) */
            } else if (DUK_HSTRING_GET_BYTELEN(h) <= 6) {
                /* Encode into a double constant (53 bits can encode 6*8 = 48 bits + 3-bit length */
            }
#endif
            duk_dup(thr, x->valstack_idx);
            constidx = duk__getconst(comp_ctx);

            if (flags & DUK__IVAL_FLAG_ALLOW_CONST) {
                return constidx;
            }

            dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx));
            duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, dest, constidx);
            return dest;
        }
        case DUK_TAG_OBJECT: {
            DUK_UNREACHABLE();
            break;
        }
        case DUK_TAG_BUFFER: {
            DUK_UNREACHABLE();
            break;
        }
        case DUK_TAG_LIGHTFUNC: {
            DUK_UNREACHABLE();
            break;
        }
#if defined(DUK_USE_FASTINT)
        case DUK_TAG_FASTINT:
#endif
        default: {
            /* number */
            duk_regconst_t dest;
            duk_regconst_t constidx;
            duk_double_t dval;
            duk_int32_t ival;

            DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
            DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
            dval = DUK_TVAL_GET_NUMBER(tv);

            if (!(flags & DUK__IVAL_FLAG_ALLOW_CONST)) {
                /* A number can be loaded either through a constant, using
                 * LDINT, or using LDINT+LDINTX.  LDINT is always a size win,
                 * LDINT+LDINTX is not if the constant is used multiple times.
                 * Currently always prefer LDINT+LDINTX over a double constant.
                 */

                if (duk_is_whole_get_int32_nonegzero(dval, &ival)) {
                    dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx));
                    duk__emit_load_int32(comp_ctx, dest, ival);
                    return dest;
                }
            }

            duk_dup(thr, x->valstack_idx);
            constidx = duk__getconst(comp_ctx);

            if (flags & DUK__IVAL_FLAG_ALLOW_CONST) {
                return constidx;
            } else {
                dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx));
                duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, dest, constidx);
                return dest;
            }
        }
        }  /* end switch */
        goto fail_internal;  /* never here */
    }
    case DUK_ISPEC_REGCONST: {
        if (forced_reg >= 0) {
            if (DUK__ISCONST(x->regconst)) {
                duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, forced_reg, x->regconst);
            } else if (x->regconst != forced_reg) {
                duk__emit_a_bc(comp_ctx, DUK_OP_LDREG, forced_reg, x->regconst);
            } else {
                ; /* already in correct reg */
            }
            return forced_reg;
        }

        DUK_ASSERT(forced_reg < 0);
        if (DUK__ISCONST(x->regconst)) {
            if (!(flags & DUK__IVAL_FLAG_ALLOW_CONST)) {
                duk_regconst_t dest = DUK__ALLOCTEMP(comp_ctx);
                duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, dest, x->regconst);
                return dest;
            }
            return x->regconst;
        }

        DUK_ASSERT(forced_reg < 0 && !DUK__ISCONST(x->regconst));
        if ((flags & DUK__IVAL_FLAG_REQUIRE_TEMP) && !DUK__ISREG_TEMP(comp_ctx, x->regconst)) {
            duk_regconst_t dest = DUK__ALLOCTEMP(comp_ctx);
            duk__emit_a_bc(comp_ctx, DUK_OP_LDREG, dest, x->regconst);
            return dest;
        }
        return x->regconst;
    }
    default: {
        break;  /* never here */
    }
    }

 fail_internal:
    DUK_ERROR_INTERNAL(thr);
    DUK_WO_NORETURN(return 0;);
}

DUK_LOCAL void duk__ispec_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ispec *x, duk_regconst_t forced_reg) {
    DUK_ASSERT(forced_reg >= 0);
    (void) duk__ispec_toregconst_raw(comp_ctx, x, forced_reg, 0 /*flags*/);
}

/* Coerce an duk_ivalue to a 'plain' value by generating the necessary
 * arithmetic operations, property access, or variable access bytecode.
 * The duk_ivalue argument ('x') is converted into a plain value as a
 * side effect.
 */
DUK_LOCAL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_regconst_t forced_reg) {
    duk_hthread *thr = comp_ctx->thr;

    DUK_DDD(DUK_DDDPRINT("duk__ivalue_toplain_raw(): x={t=%ld,op=%ld,x1={%ld:%ld:%!T},x2={%ld:%ld:%!T}}, "
                         "forced_reg=%ld",
                         (long) x->t, (long) x->op,
                         (long) x->x1.t, (long) x->x1.regconst,
                         (duk_tval *) duk_get_tval(thr, x->x1.valstack_idx),
                         (long) x->x2.t, (long) x->x2.regconst,
                         (duk_tval *) duk_get_tval(thr, x->x2.valstack_idx),
                         (long) forced_reg));

    switch (x->t) {
    case DUK_IVAL_PLAIN: {
        return;
    }
    /* XXX: support unary arithmetic ivalues (useful?) */
    case DUK_IVAL_ARITH: {
        duk_regconst_t arg1;
        duk_regconst_t arg2;
        duk_regconst_t dest;
        duk_tval *tv1;
        duk_tval *tv2;

        DUK_DDD(DUK_DDDPRINT("arith to plain conversion"));

        /* inline arithmetic check for constant values */
        /* XXX: use the exactly same arithmetic function here as in executor */
        if (x->x1.t == DUK_ISPEC_VALUE && x->x2.t == DUK_ISPEC_VALUE && x->t == DUK_IVAL_ARITH) {
            tv1 = DUK_GET_TVAL_POSIDX(thr, x->x1.valstack_idx);
            tv2 = DUK_GET_TVAL_POSIDX(thr, x->x2.valstack_idx);
            DUK_ASSERT(tv1 != NULL);
            DUK_ASSERT(tv2 != NULL);

            DUK_DDD(DUK_DDDPRINT("arith: tv1=%!T, tv2=%!T",
                                 (duk_tval *) tv1,
                                 (duk_tval *) tv2));

            if (DUK_TVAL_IS_NUMBER(tv1) && DUK_TVAL_IS_NUMBER(tv2)) {
                duk_double_t d1 = DUK_TVAL_GET_NUMBER(tv1);
                duk_double_t d2 = DUK_TVAL_GET_NUMBER(tv2);
                duk_double_t d3;
                duk_bool_t accept_fold = 1;

                DUK_DDD(DUK_DDDPRINT("arith inline check: d1=%lf, d2=%lf, op=%ld",
                                     (double) d1, (double) d2, (long) x->op));
                switch (x->op) {
                case DUK_OP_ADD: {
                    d3 = d1 + d2;
                    break;
                }
                case DUK_OP_SUB: {
                    d3 = d1 - d2;
                    break;
                }
                case DUK_OP_MUL: {
                    d3 = d1 * d2;
                    break;
                }
                case DUK_OP_DIV: {
                    /* Division-by-zero is undefined
                     * behavior, so rely on a helper.
                     */
                    d3 = duk_double_div(d1, d2);
                    break;
                }
                case DUK_OP_EXP: {
                    d3 = (duk_double_t) duk_js_arith_pow((double) d1, (double) d2);
                    break;
                }
                default: {
                    d3 = 0.0;  /* Won't be used, but silence MSVC /W4 warning. */
                    accept_fold = 0;
                    break;
                }
                }

                if (accept_fold) {
                    duk_double_union du;
                    du.d = d3;
                    DUK_DBLUNION_NORMALIZE_NAN_CHECK(&du);
                    d3 = du.d;

                    x->t = DUK_IVAL_PLAIN;
                    DUK_ASSERT(x->x1.t == DUK_ISPEC_VALUE);
                    DUK_TVAL_SET_NUMBER(tv1, d3);  /* old value is number: no refcount */
                    return;
                }
            } else if (x->op == DUK_OP_ADD && DUK_TVAL_IS_STRING(tv1) && DUK_TVAL_IS_STRING(tv2)) {
                /* Inline string concatenation.  No need to check for
                 * symbols, as all inputs are valid ECMAScript strings.
                 */
                duk_dup(thr, x->x1.valstack_idx);
                duk_dup(thr, x->x2.valstack_idx);
                duk_concat(thr, 2);
                duk_replace(thr, x->x1.valstack_idx);
                x->t = DUK_IVAL_PLAIN;
                DUK_ASSERT(x->x1.t == DUK_ISPEC_VALUE);
                return;
            }
        }

        arg1 = duk__ispec_toregconst_raw(comp_ctx, &x->x1, -1, DUK__IVAL_FLAG_ALLOW_CONST | DUK__IVAL_FLAG_REQUIRE_SHORT /*flags*/);
        arg2 = duk__ispec_toregconst_raw(comp_ctx, &x->x2, -1, DUK__IVAL_FLAG_ALLOW_CONST | DUK__IVAL_FLAG_REQUIRE_SHORT /*flags*/);

        /* If forced reg, use it as destination.  Otherwise try to
         * use either coerced ispec if it is a temporary.
         */
        if (forced_reg >= 0) {
            dest = forced_reg;
        } else if (DUK__ISREG_TEMP(comp_ctx, arg1)) {
            dest = arg1;
        } else if (DUK__ISREG_TEMP(comp_ctx, arg2)) {
            dest = arg2;
        } else {
            dest = DUK__ALLOCTEMP(comp_ctx);
        }

        DUK_ASSERT(DUK__ISREG(dest));
        duk__emit_a_b_c(comp_ctx, x->op | DUK__EMIT_FLAG_BC_REGCONST, dest, arg1, arg2);

        duk__ivalue_regconst(x, dest);
        return;
    }
    case DUK_IVAL_PROP: {
        /* XXX: very similar to DUK_IVAL_ARITH - merge? */
        duk_regconst_t arg1;
        duk_regconst_t arg2;
        duk_regconst_t dest;

        /* Need a short reg/const, does not have to be a mutable temp. */
        arg1 = duk__ispec_toregconst_raw(comp_ctx, &x->x1, -1, DUK__IVAL_FLAG_ALLOW_CONST | DUK__IVAL_FLAG_REQUIRE_SHORT /*flags*/);
        arg2 = duk__ispec_toregconst_raw(comp_ctx, &x->x2, -1, DUK__IVAL_FLAG_ALLOW_CONST | DUK__IVAL_FLAG_REQUIRE_SHORT /*flags*/);

        /* Pick a destination register.  If either base value or key
         * happens to be a temp value, reuse it as the destination.
         *
         * XXX: The temp must be a "mutable" one, i.e. such that no
         * other expression is using it anymore.  Here this should be
         * the case because the value of a property access expression
         * is neither the base nor the key, but the lookup result.
         */

        if (forced_reg >= 0) {
            dest = forced_reg;
        } else if (DUK__ISREG_TEMP(comp_ctx, arg1)) {
            dest = arg1;
        } else if (DUK__ISREG_TEMP(comp_ctx, arg2)) {
            dest = arg2;
        } else {
            dest = DUK__ALLOCTEMP(comp_ctx);
        }

        duk__emit_a_b_c(comp_ctx,
                        DUK_OP_GETPROP | DUK__EMIT_FLAG_BC_REGCONST,
                        dest,
                        arg1,
                        arg2);

        duk__ivalue_regconst(x, dest);
        return;
    }
    case DUK_IVAL_VAR: {
        /* x1 must be a string */
        duk_regconst_t dest;
        duk_regconst_t reg_varbind;
        duk_regconst_t rc_varname;

        DUK_ASSERT(x->x1.t == DUK_ISPEC_VALUE);

        duk_dup(thr, x->x1.valstack_idx);
        if (duk__lookup_lhs(comp_ctx, &reg_varbind, &rc_varname)) {
            duk__ivalue_regconst(x, reg_varbind);
        } else {
            dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx));
            duk__emit_a_bc(comp_ctx, DUK_OP_GETVAR, dest, rc_varname);
            duk__ivalue_regconst(x, dest);
        }
        return;
    }
    case DUK_IVAL_NONE:
    default: {
        DUK_D(DUK_DPRINT("invalid ivalue type: %ld", (long) x->t));
        break;
    }
    }

    DUK_ERROR_INTERNAL(thr);
    DUK_WO_NORETURN(return;);
}

/* evaluate to plain value, no forced register (temp/bound reg both ok) */
DUK_LOCAL void duk__ivalue_toplain(duk_compiler_ctx *comp_ctx, duk_ivalue *x) {
    duk__ivalue_toplain_raw(comp_ctx, x, -1 /*forced_reg*/);
}

/* evaluate to final form (e.g. coerce GETPROP to code), throw away temp */
DUK_LOCAL void duk__ivalue_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *x) {
    duk_regconst_t temp;

    /* If duk__ivalue_toplain_raw() allocates a temp, forget it and
     * restore next temp state.
     */
    temp = DUK__GETTEMP(comp_ctx);
    duk__ivalue_toplain_raw(comp_ctx, x, -1 /*forced_reg*/);
    DUK__SETTEMP(comp_ctx, temp);
}

/* Coerce an duk_ivalue to a register or constant; result register may
 * be a temp or a bound register.
 *
 * The duk_ivalue argument ('x') is converted into a regconst as a
 * side effect.
 */
DUK_LOCAL
duk_regconst_t duk__ivalue_toregconst_raw(duk_compiler_ctx *comp_ctx,
                                          duk_ivalue *x,
                                          duk_regconst_t forced_reg,
                                          duk_small_uint_t flags) {
    duk_hthread *thr = comp_ctx->thr;
    duk_regconst_t reg;
    DUK_UNREF(thr);

    DUK_DDD(DUK_DDDPRINT("duk__ivalue_toregconst_raw(): x={t=%ld,op=%ld,x1={%ld:%ld:%!T},x2={%ld:%ld:%!T}}, "
                         "forced_reg=%ld, flags 0x%08lx: allow_const=%ld require_temp=%ld require_short=%ld",
                         (long) x->t, (long) x->op,
                         (long) x->x1.t, (long) x->x1.regconst,
                         (duk_tval *) duk_get_tval(thr, x->x1.valstack_idx),
                         (long) x->x2.t, (long) x->x2.regconst,
                         (duk_tval *) duk_get_tval(thr, x->x2.valstack_idx),
                         (long) forced_reg,
                         (unsigned long) flags,
                         (long) ((flags & DUK__IVAL_FLAG_ALLOW_CONST) ? 1 : 0),
                         (long) ((flags & DUK__IVAL_FLAG_REQUIRE_TEMP) ? 1 : 0),
                         (long) ((flags & DUK__IVAL_FLAG_REQUIRE_SHORT) ? 1 : 0)));

    /* first coerce to a plain value */
    duk__ivalue_toplain_raw(comp_ctx, x, forced_reg);
    DUK_ASSERT(x->t == DUK_IVAL_PLAIN);

    /* then to a register */
    reg = duk__ispec_toregconst_raw(comp_ctx, &x->x1, forced_reg, flags);
    duk__ivalue_regconst(x, reg);

    return reg;
}

DUK_LOCAL duk_regconst_t duk__ivalue_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *x) {
    return duk__ivalue_toregconst_raw(comp_ctx, x, -1, 0 /*flags*/);
}

#if 0  /* unused */
DUK_LOCAL duk_regconst_t duk__ivalue_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *x) {
    return duk__ivalue_toregconst_raw(comp_ctx, x, -1, DUK__IVAL_FLAG_REQUIRE_TEMP /*flags*/);
}
#endif

DUK_LOCAL void duk__ivalue_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_int_t forced_reg) {
    DUK_ASSERT(forced_reg >= 0);
    (void) duk__ivalue_toregconst_raw(comp_ctx, x, forced_reg, 0 /*flags*/);
}

DUK_LOCAL duk_regconst_t duk__ivalue_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *x) {
    return duk__ivalue_toregconst_raw(comp_ctx, x, -1, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/);
}

DUK_LOCAL duk_regconst_t duk__ivalue_totempconst(duk_compiler_ctx *comp_ctx, duk_ivalue *x) {
    return duk__ivalue_toregconst_raw(comp_ctx, x, -1, DUK__IVAL_FLAG_ALLOW_CONST | DUK__IVAL_FLAG_REQUIRE_TEMP /*flags*/);
}

/* The issues below can be solved with better flags */

/* XXX: many operations actually want toforcedtemp() -- brand new temp? */
/* XXX: need a toplain_ignore() which will only coerce a value to a temp
 * register if it might have a side effect.  Side-effect free values do not
 * need to be coerced.
 */

/*
 *  Identifier handling
 */

DUK_LOCAL duk_regconst_t duk__lookup_active_register_binding(duk_compiler_ctx *comp_ctx) {
    duk_hthread *thr = comp_ctx->thr;
    duk_hstring *h_varname;
    duk_regconst_t ret;

    DUK_DDD(DUK_DDDPRINT("resolving identifier reference to '%!T'",
                         (duk_tval *) duk_get_tval(thr, -1)));

    /*
     *  Special name handling
     */

    h_varname = duk_known_hstring(thr, -1);

    if (h_varname == DUK_HTHREAD_STRING_LC_ARGUMENTS(thr)) {
        DUK_DDD(DUK_DDDPRINT("flagging function as accessing 'arguments'"));
        comp_ctx->curr_func.id_access_arguments = 1;
    }

    /*
     *  Inside one or more 'with' statements fall back to slow path always.
     *  (See e.g. test-stmt-with.js.)
     */

    if (comp_ctx->curr_func.with_depth > 0) {
        DUK_DDD(DUK_DDDPRINT("identifier lookup inside a 'with' -> fall back to slow path"));
        goto slow_path_own;
    }

    /*
     *  Any catch bindings ("catch (e)") also affect identifier binding.
     *
     *  Currently, the varmap is modified for the duration of the catch
     *  clause to ensure any identifier accesses with the catch variable
     *  name will use slow path.
     */

    duk_get_prop(thr, comp_ctx->curr_func.varmap_idx);
    if (duk_is_number(thr, -1)) {
        ret = duk_to_int(thr, -1);
        duk_pop(thr);
    } else {
        duk_pop(thr);
        if (comp_ctx->curr_func.catch_depth > 0 || comp_ctx->curr_func.with_depth > 0) {
            DUK_DDD(DUK_DDDPRINT("slow path access from inside a try-catch or with needs _Varmap"));
            goto slow_path_own;
        } else {
            /* In this case we're doing a variable lookup that doesn't
             * match our own variables, so _Varmap won't be needed at
             * run time.
             */
            DUK_DDD(DUK_DDDPRINT("slow path access outside of try-catch and with, no need for _Varmap"));
            goto slow_path_notown;
        }
    }

    DUK_DDD(DUK_DDDPRINT("identifier lookup -> reg %ld", (long) ret));
    return ret;

 slow_path_notown:
    DUK_DDD(DUK_DDDPRINT("identifier lookup -> slow path, not own variable"));

    comp_ctx->curr_func.id_access_slow = 1;
    return (duk_regconst_t) -1;

 slow_path_own:
    DUK_DDD(DUK_DDDPRINT("identifier lookup -> slow path, may be own variable"));

    comp_ctx->curr_func.id_access_slow = 1;
    comp_ctx->curr_func.id_access_slow_own = 1;
    return (duk_regconst_t) -1;
}

/* Lookup an identifier name in the current varmap, indicating whether the
 * identifier is register-bound and if not, allocating a constant for the
 * identifier name.  Returns 1 if register-bound, 0 otherwise.  Caller can
 * also check (out_reg_varbind >= 0) to check whether or not identifier is
 * register bound.  The caller must NOT use out_rc_varname at all unless
 * return code is 0 or out_reg_varbind is < 0; this is becuase out_rc_varname
 * is unsigned and doesn't have a "unused" / none value.
 */
DUK_LOCAL duk_bool_t duk__lookup_lhs(duk_compiler_ctx *comp_ctx, duk_regconst_t *out_reg_varbind, duk_regconst_t *out_rc_varname) {
    duk_hthread *thr = comp_ctx->thr;
    duk_regconst_t reg_varbind;
    duk_regconst_t rc_varname;

    /* [ ... varname ] */

    duk_dup_top(thr);
    reg_varbind = duk__lookup_active_register_binding(comp_ctx);

    if (reg_varbind >= 0) {
        *out_reg_varbind = reg_varbind;
        *out_rc_varname = 0;  /* duk_regconst_t is unsigned, so use 0 as dummy value (ignored by caller) */
        duk_pop(thr);
        return 1;
    } else {
        rc_varname = duk__getconst(comp_ctx);
        *out_reg_varbind = -1;
        *out_rc_varname = rc_varname;
        return 0;
    }
}

/*
 *  Label handling
 *
 *  Labels are initially added with flags prohibiting both break and continue.
 *  When the statement type is finally uncovered (after potentially multiple
 *  labels), all the labels are updated to allow/prohibit break and continue.
 */

DUK_LOCAL void duk__add_label(duk_compiler_ctx *comp_ctx, duk_hstring *h_label, duk_int_t pc_label, duk_int_t label_id) {
    duk_hthread *thr = comp_ctx->thr;
    duk_size_t n;
    duk_size_t new_size;
    duk_uint8_t *p;
    duk_labelinfo *li_start, *li;

    /* Duplicate (shadowing) labels are not allowed, except for the empty
     * labels (which are used as default labels for switch and iteration
     * statements).
     *
     * We could also allow shadowing of non-empty pending labels without any
     * other issues than breaking the required label shadowing requirements
     * of the E5 specification, see Section 12.12.
     */

    p = (duk_uint8_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, comp_ctx->curr_func.h_labelinfos);
    li_start = (duk_labelinfo *) (void *) p;
    li = (duk_labelinfo *) (void *) (p + DUK_HBUFFER_GET_SIZE(comp_ctx->curr_func.h_labelinfos));
    n = (duk_size_t) (li - li_start);

    while (li > li_start) {
        li--;

        if (li->h_label == h_label && h_label != DUK_HTHREAD_STRING_EMPTY_STRING(thr)) {
            DUK_ERROR_SYNTAX(thr, DUK_STR_DUPLICATE_LABEL);
            DUK_WO_NORETURN(return;);
        }
    }

    duk_push_hstring(thr, h_label);
    DUK_ASSERT(n <= DUK_UARRIDX_MAX);  /* label limits */
    (void) duk_put_prop_index(thr, comp_ctx->curr_func.labelnames_idx, (duk_uarridx_t) n);

    new_size = (n + 1) * sizeof(duk_labelinfo);
    duk_hbuffer_resize(thr, comp_ctx->curr_func.h_labelinfos, new_size);
    /* XXX: slack handling, slow now */

    /* relookup after possible realloc */
    p = (duk_uint8_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, comp_ctx->curr_func.h_labelinfos);
    li_start = (duk_labelinfo *) (void *) p;
    DUK_UNREF(li_start);  /* silence scan-build warning */
    li = (duk_labelinfo *) (void *) (p + DUK_HBUFFER_GET_SIZE(comp_ctx->curr_func.h_labelinfos));
    li--;

    /* Labels can be used for iteration statements but also for other statements,
     * in particular a label can be used for a block statement.  All cases of a
     * named label accept a 'break' so that flag is set here.  Iteration statements
     * also allow 'continue', so that flag is updated when we figure out the
     * statement type.
     */

    li->flags = DUK_LABEL_FLAG_ALLOW_BREAK;
    li->label_id = label_id;
    li->h_label = h_label;
    li->catch_depth = comp_ctx->curr_func.catch_depth;   /* catch depth from current func */
    li->pc_label = pc_label;

    DUK_DDD(DUK_DDDPRINT("registered label: flags=0x%08lx, id=%ld, name=%!O, catch_depth=%ld, pc_label=%ld",
                         (unsigned long) li->flags, (long) li->label_id, (duk_heaphdr *) li->h_label,
                         (long) li->catch_depth, (long) li->pc_label));
}

/* Update all labels with matching label_id. */
DUK_LOCAL void duk__update_label_flags(duk_compiler_ctx *comp_ctx, duk_int_t label_id, duk_small_uint_t flags) {
    duk_uint8_t *p;
    duk_labelinfo *li_start, *li;

    p = (duk_uint8_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(comp_ctx->thr->heap, comp_ctx->curr_func.h_labelinfos);
    li_start = (duk_labelinfo *) (void *) p;
    li = (duk_labelinfo *) (void *) (p + DUK_HBUFFER_GET_SIZE(comp_ctx->curr_func.h_labelinfos));

    /* Match labels starting from latest; once label_id no longer matches, we can
     * safely exit without checking the rest of the labels (only the topmost labels
     * are ever updated).
     */
    while (li > li_start) {
        li--;

        if (li->label_id != label_id) {
            break;
        }

        DUK_DDD(DUK_DDDPRINT("updating (overwriting) label flags for li=%p, label_id=%ld, flags=%ld",
                             (void *) li, (long) label_id, (long) flags));

        li->flags = flags;
    }
}

/* Lookup active label information.  Break/continue distinction is necessary to handle switch
 * statement related labels correctly: a switch will only catch a 'break', not a 'continue'.
 *
 * An explicit label cannot appear multiple times in the active set, but empty labels (unlabelled
 * iteration and switch statements) can.  A break will match the closest unlabelled or labelled
 * statement.  A continue will match the closest unlabelled or labelled iteration statement.  It is
 * a syntax error if a continue matches a labelled switch statement; because an explicit label cannot
 * be duplicated, the continue cannot match any valid label outside the switch.
 *
 * A side effect of these rules is that a LABEL statement related to a switch should never actually
 * catch a continue abrupt completion at run-time.  Hence an INVALID opcode can be placed in the
 * continue slot of the switch's LABEL statement.
 */

/* XXX: awkward, especially the bunch of separate output values -> output struct? */
DUK_LOCAL void duk__lookup_active_label(duk_compiler_ctx *comp_ctx, duk_hstring *h_label, duk_bool_t is_break, duk_int_t *out_label_id, duk_int_t *out_label_catch_depth, duk_int_t *out_label_pc, duk_bool_t *out_is_closest) {
    duk_hthread *thr = comp_ctx->thr;
    duk_uint8_t *p;
    duk_labelinfo *li_start, *li_end, *li;
    duk_bool_t match = 0;

    DUK_DDD(DUK_DDDPRINT("looking up active label: label='%!O', is_break=%ld",
                         (duk_heaphdr *) h_label, (long) is_break));

    DUK_UNREF(thr);

    p = (duk_uint8_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, comp_ctx->curr_func.h_labelinfos);
    li_start = (duk_labelinfo *) (void *) p;
    li_end = (duk_labelinfo *) (void *) (p + DUK_HBUFFER_GET_SIZE(comp_ctx->curr_func.h_labelinfos));
    li = li_end;

    /* Match labels starting from latest label because there can be duplicate empty
     * labels in the label set.
     */
    while (li > li_start) {
        li--;

        if (li->h_label != h_label) {
            DUK_DDD(DUK_DDDPRINT("labelinfo[%ld] ->'%!O' != %!O",
                                 (long) (li - li_start),
                                 (duk_heaphdr *) li->h_label,
                                 (duk_heaphdr *) h_label));
            continue;
        }

        DUK_DDD(DUK_DDDPRINT("labelinfo[%ld] -> '%!O' label name matches (still need to check type)",
                             (long) (li - li_start), (duk_heaphdr *) h_label));

        /* currently all labels accept a break, so no explicit check for it now */
        DUK_ASSERT(li->flags & DUK_LABEL_FLAG_ALLOW_BREAK);

        if (is_break) {
            /* break matches always */
            match = 1;
            break;
        } else if (li->flags & DUK_LABEL_FLAG_ALLOW_CONTINUE) {
            /* iteration statements allow continue */
            match = 1;
            break;
        } else {
            /* continue matched this label -- we can only continue if this is the empty
             * label, for which duplication is allowed, and thus there is hope of
             * finding a match deeper in the label stack.
             */
            if (h_label != DUK_HTHREAD_STRING_EMPTY_STRING(thr)) {
                DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_LABEL);
                DUK_WO_NORETURN(return;);
            } else {
                DUK_DDD(DUK_DDDPRINT("continue matched an empty label which does not "
                                     "allow a continue -> continue lookup deeper in label stack"));
            }
        }
    }
    /* XXX: match flag is awkward, rework */
    if (!match) {
        DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_LABEL);
        DUK_WO_NORETURN(return;);
    }

    DUK_DDD(DUK_DDDPRINT("label match: %!O -> label_id %ld, catch_depth=%ld, pc_label=%ld",
                         (duk_heaphdr *) h_label, (long) li->label_id,
                         (long) li->catch_depth, (long) li->pc_label));

    *out_label_id = li->label_id;
    *out_label_catch_depth = li->catch_depth;
    *out_label_pc = li->pc_label;
    *out_is_closest = (li == li_end - 1);
}

DUK_LOCAL void duk__reset_labels_to_length(duk_compiler_ctx *comp_ctx, duk_size_t len) {
    duk_hthread *thr = comp_ctx->thr;

    duk_set_length(thr, comp_ctx->curr_func.labelnames_idx, len);
    duk_hbuffer_resize(thr, comp_ctx->curr_func.h_labelinfos, sizeof(duk_labelinfo) * len);
}

/*
 *  Expression parsing: duk__expr_nud(), duk__expr_led(), duk__expr_lbp(), and helpers.
 *
 *  - duk__expr_nud(): ("null denotation"): process prev_token as a "start" of an expression (e.g. literal)
 *  - duk__expr_led(): ("left denotation"): process prev_token in the "middle" of an expression (e.g. operator)
 *  - duk__expr_lbp(): ("left-binding power"): return left-binding power of curr_token
 */

/* object literal key tracking flags */
#define DUK__OBJ_LIT_KEY_PLAIN  (1 << 0)  /* key encountered as a plain property */
#define DUK__OBJ_LIT_KEY_GET    (1 << 1)  /* key encountered as a getter */
#define DUK__OBJ_LIT_KEY_SET    (1 << 2)  /* key encountered as a setter */

DUK_LOCAL void duk__nud_array_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
    duk_hthread *thr = comp_ctx->thr;
    duk_regconst_t reg_obj;                 /* result reg */
    duk_regconst_t reg_temp;                /* temp reg */
    duk_regconst_t temp_start;              /* temp reg value for start of loop */
    duk_small_uint_t max_init_values;  /* max # of values initialized in one MPUTARR set */
    duk_small_uint_t num_values;       /* number of values in current MPUTARR set */
    duk_uarridx_t curr_idx;            /* current (next) array index */
    duk_uarridx_t start_idx;           /* start array index of current MPUTARR set */
    duk_uarridx_t init_idx;            /* last array index explicitly initialized, +1 */
    duk_bool_t require_comma;          /* next loop requires a comma */
#if !defined(DUK_USE_PREFER_SIZE)
    duk_int_t pc_newarr;
    duk_compiler_instr *instr;
#endif

    /* DUK_TOK_LBRACKET already eaten, current token is right after that */
    DUK_ASSERT(comp_ctx->prev_token.t == DUK_TOK_LBRACKET);

    max_init_values = DUK__MAX_ARRAY_INIT_VALUES;  /* XXX: depend on available temps? */

    reg_obj = DUK__ALLOCTEMP(comp_ctx);
#if !defined(DUK_USE_PREFER_SIZE)
    pc_newarr = duk__get_current_pc(comp_ctx);
#endif
    duk__emit_bc(comp_ctx, DUK_OP_NEWARR, reg_obj);  /* XXX: patch initial size hint afterwards? */
    temp_start = DUK__GETTEMP(comp_ctx);

    /*
     *  Emit initializers in sets of maximum max_init_values.
     *  Corner cases such as single value initializers do not have
     *  special handling now.
     *
     *  Elided elements must not be emitted as 'undefined' values,
     *  because such values would be enumerable (which is incorrect).
     *  Also note that trailing elisions must be reflected in the
     *  length of the final array but cause no elements to be actually
     *  inserted.
     */

    curr_idx = 0;
    init_idx = 0;         /* tracks maximum initialized index + 1 */
    start_idx = 0;
    require_comma = 0;

    for (;;) {
        num_values = 0;
        DUK__SETTEMP(comp_ctx, temp_start);

        if (comp_ctx->curr_token.t == DUK_TOK_RBRACKET) {
            break;
        }

        for (;;) {
            if (comp_ctx->curr_token.t == DUK_TOK_RBRACKET) {
                /* the outer loop will recheck and exit */
                break;
            }

            /* comma check */
            if (require_comma) {
                if (comp_ctx->curr_token.t == DUK_TOK_COMMA) {
                    /* comma after a value, expected */
                    duk__advance(comp_ctx);
                    require_comma = 0;
                    continue;
                } else {
                    goto syntax_error;
                }
            } else {
                if (comp_ctx->curr_token.t == DUK_TOK_COMMA) {
                    /* elision - flush */
                    curr_idx++;
                    duk__advance(comp_ctx);
                    /* if num_values > 0, MPUTARR emitted by outer loop after break */
                    break;
                }
            }
            /* else an array initializer element */

            /* initial index */
            if (num_values == 0) {
                start_idx = curr_idx;
                reg_temp = DUK__ALLOCTEMP(comp_ctx);
                duk__emit_load_int32(comp_ctx, reg_temp, (duk_int32_t) start_idx);
            }

            reg_temp = DUK__ALLOCTEMP(comp_ctx);   /* alloc temp just in case, to update max temp */
            DUK__SETTEMP(comp_ctx, reg_temp);
            duk__expr_toforcedreg(comp_ctx, res, DUK__BP_COMMA /*rbp_flags*/, reg_temp /*forced_reg*/);
            DUK__SETTEMP(comp_ctx, reg_temp + 1);

            num_values++;
            curr_idx++;
            require_comma = 1;

            if (num_values >= max_init_values) {
                /* MPUTARR emitted by outer loop */
                break;
            }
        }

        if (num_values > 0) {
            /* - A is a source register (it's not a write target, but used
             *   to identify the target object) but can be shuffled.
             * - B cannot be shuffled normally because it identifies a range
             *   of registers, the emitter has special handling for this
             *   (the "no shuffle" flag must not be set).
             * - C is a non-register number and cannot be shuffled, but
             *   never needs to be.
             */
            duk__emit_a_b_c(comp_ctx,
                            DUK_OP_MPUTARR |
                                DUK__EMIT_FLAG_NO_SHUFFLE_C |
                                DUK__EMIT_FLAG_A_IS_SOURCE,
                            reg_obj,
                            temp_start,
                            (duk_regconst_t) (num_values + 1));
            init_idx = start_idx + num_values;

            /* num_values and temp_start reset at top of outer loop */
        }
    }

    /* Update initil size for NEWARR, doesn't need to be exact and is
     * capped at A field limit.
     */
#if !defined(DUK_USE_PREFER_SIZE)
    instr = duk__get_instr_ptr(comp_ctx, pc_newarr);
    instr->ins |= DUK_ENC_OP_A(0, curr_idx > DUK_BC_A_MAX ? DUK_BC_A_MAX : curr_idx);
#endif

    DUK_ASSERT(comp_ctx->curr_token.t == DUK_TOK_RBRACKET);
    duk__advance(comp_ctx);

    DUK_DDD(DUK_DDDPRINT("array literal done, curridx=%ld, initidx=%ld",
                         (long) curr_idx, (long) init_idx));

    /* trailing elisions? */
    if (curr_idx > init_idx) {
        /* yes, must set array length explicitly */
        DUK_DDD(DUK_DDDPRINT("array literal has trailing elisions which affect its length"));
        reg_temp = DUK__ALLOCTEMP(comp_ctx);
        duk__emit_load_int32(comp_ctx, reg_temp, (duk_int_t) curr_idx);
        duk__emit_a_bc(comp_ctx,
                       DUK_OP_SETALEN | DUK__EMIT_FLAG_A_IS_SOURCE,
                       reg_obj,
                       reg_temp);
    }

    DUK__SETTEMP(comp_ctx, temp_start);

    duk__ivalue_regconst(res, reg_obj);
    return;

 syntax_error:
    DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_ARRAY_LITERAL);
    DUK_WO_NORETURN(return;);
}

typedef struct {
    duk_regconst_t reg_obj;
    duk_regconst_t temp_start;
    duk_small_uint_t num_pairs;
    duk_small_uint_t num_total_pairs;
} duk__objlit_state;

DUK_LOCAL void duk__objlit_flush_keys(duk_compiler_ctx *comp_ctx, duk__objlit_state *st) {
    if (st->num_pairs > 0) {
        /* - A is a source register (it's not a write target, but used
         *   to identify the target object) but can be shuffled.
         * - B cannot be shuffled normally because it identifies a range
         *   of registers, the emitter has special handling for this
         *   (the "no shuffle" flag must not be set).
         * - C is a non-register number and cannot be shuffled, but
         *   never needs to be.
         */
        DUK_ASSERT(st->num_pairs > 0);
        duk__emit_a_b_c(comp_ctx,
                        DUK_OP_MPUTOBJ |
                            DUK__EMIT_FLAG_NO_SHUFFLE_C |
                            DUK__EMIT_FLAG_A_IS_SOURCE,
                        st->reg_obj,
                        st->temp_start,
                        (duk_regconst_t) (st->num_pairs * 2));
        st->num_total_pairs += st->num_pairs;
        st->num_pairs = 0;
    }
    DUK__SETTEMP(comp_ctx, st->temp_start);
}

DUK_LOCAL duk_bool_t duk__objlit_load_key(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_token *tok, duk_regconst_t reg_temp) {
    if (tok->t_nores == DUK_TOK_IDENTIFIER || tok->t_nores == DUK_TOK_STRING) {
        /* same handling for identifiers and strings */
        DUK_ASSERT(tok->str1 != NULL);
        duk_push_hstring(comp_ctx->thr, tok->str1);
    } else if (tok->t == DUK_TOK_NUMBER) {
        /* numbers can be loaded as numbers and coerced on the fly */
        duk_push_number(comp_ctx->thr, tok->num);
    } else {
        return 1;  /* error */
    }

    duk__ivalue_plain_fromstack(comp_ctx, res);
    DUK__SETTEMP(comp_ctx, reg_temp + 1);
    duk__ivalue_toforcedreg(comp_ctx, res, reg_temp);
    DUK__SETTEMP(comp_ctx, reg_temp + 1);
    return 0;
}

DUK_LOCAL void duk__nud_object_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
    duk_hthread *thr = comp_ctx->thr;
    duk__objlit_state st;
    duk_regconst_t reg_temp;          /* temp reg */
    duk_small_uint_t max_init_pairs;  /* max # of key-value pairs initialized in one MPUTOBJ set */
    duk_bool_t first;                 /* first value: comma must not precede the value */
    duk_bool_t is_set, is_get;        /* temps */
#if !defined(DUK_USE_PREFER_SIZE)
    duk_int_t pc_newobj;
    duk_compiler_instr *instr;
#endif

    DUK_ASSERT(comp_ctx->prev_token.t == DUK_TOK_LCURLY);

    max_init_pairs = DUK__MAX_OBJECT_INIT_PAIRS;  /* XXX: depend on available temps? */

    st.reg_obj = DUK__ALLOCTEMP(comp_ctx);    /* target object */
    st.temp_start = DUK__GETTEMP(comp_ctx);   /* start of MPUTOBJ argument list */
    st.num_pairs = 0;                         /* number of key/value pairs emitted for current MPUTOBJ set */
    st.num_total_pairs = 0;                   /* number of key/value pairs emitted overall */

#if !defined(DUK_USE_PREFER_SIZE)
    pc_newobj = duk__get_current_pc(comp_ctx);
#endif
    duk__emit_bc(comp_ctx, DUK_OP_NEWOBJ, st.reg_obj);

    /*
     *  Emit initializers in sets of maximum max_init_pairs keys.
     *  Setter/getter is handled separately and terminates the
     *  current set of initializer values.  Corner cases such as
     *  single value initializers do not have special handling now.
     */

    first = 1;
    for (;;) {
        /*
         *  ES5 and ES2015+ provide a lot of different PropertyDefinition
         *  formats, see http://www.ecma-international.org/ecma-262/6.0/#sec-object-initializer.
         *
         *  PropertyName can be IdentifierName (includes reserved words), a string
         *  literal, or a number literal.  Note that IdentifierName allows 'get' and
         *  'set' too, so we need to look ahead to the next token to distinguish:
         *
         *     { get : 1 }
         *
         *  and
         *
         *     { get foo() { return 1 } }
         *     { get get() { return 1 } }    // 'get' as getter propertyname
         *
         *  Finally, a trailing comma is allowed.
         *
         *  Key name is coerced to string at compile time (and ends up as a
         *  a string constant) even for numeric keys (e.g. "{1:'foo'}").
         *  These could be emitted using e.g. LDINT, but that seems hardly
         *  worth the effort and would increase code size.
         */

        DUK_DDD(DUK_DDDPRINT("object literal loop, curr_token->t = %ld",
                             (long) comp_ctx->curr_token.t));

        if (comp_ctx->curr_token.t == DUK_TOK_RCURLY) {
            break;
        }

        if (first) {
            first = 0;
        } else {
            if (comp_ctx->curr_token.t != DUK_TOK_COMMA) {
                goto syntax_error;
            }
            duk__advance(comp_ctx);
            if (comp_ctx->curr_token.t == DUK_TOK_RCURLY) {
                /* trailing comma followed by rcurly */
                break;
            }
        }

        /* Advance to get one step of lookup. */
        duk__advance(comp_ctx);

        /* Flush current MPUTOBJ if enough many pairs gathered. */
        if (st.num_pairs >= max_init_pairs) {
            duk__objlit_flush_keys(comp_ctx, &st);
            DUK_ASSERT(st.num_pairs == 0);
        }

        /* Reset temp register state and reserve reg_temp and
         * reg_temp + 1 for handling the current property.
         */
        DUK__SETTEMP(comp_ctx, st.temp_start + 2 * (duk_regconst_t) st.num_pairs);
        reg_temp = DUK__ALLOCTEMPS(comp_ctx, 2);

        /* NOTE: "get" and "set" are not officially ReservedWords and the lexer
         * currently treats them always like ordinary identifiers (DUK_TOK_GET
         * and DUK_TOK_SET are unused).  They need to be detected based on the
         * identifier string content.
         */

        is_get = (comp_ctx->prev_token.t == DUK_TOK_IDENTIFIER &&
                  comp_ctx->prev_token.str1 == DUK_HTHREAD_STRING_GET(thr));
        is_set = (comp_ctx->prev_token.t == DUK_TOK_IDENTIFIER &&
                  comp_ctx->prev_token.str1 == DUK_HTHREAD_STRING_SET(thr));
        if ((is_get || is_set) && comp_ctx->curr_token.t != DUK_TOK_COLON) {
            /* getter/setter */
            duk_int_t fnum;

            duk__objlit_flush_keys(comp_ctx, &st);
            DUK_ASSERT(DUK__GETTEMP(comp_ctx) == st.temp_start);  /* 2 regs are guaranteed to be allocated w.r.t. temp_max */
            reg_temp = DUK__ALLOCTEMPS(comp_ctx, 2);

            if (duk__objlit_load_key(comp_ctx, res, &comp_ctx->curr_token, reg_temp) != 0) {
                goto syntax_error;
            }

            /* curr_token = get/set name */
            fnum = duk__parse_func_like_fnum(comp_ctx, DUK__FUNC_FLAG_GETSET);

            duk__emit_a_bc(comp_ctx,
                           DUK_OP_CLOSURE,
                           st.temp_start + 1,
                           (duk_regconst_t) fnum);

            /* Slot C is used in a non-standard fashion (range of regs),
             * emitter code has special handling for it (must not set the
             * "no shuffle" flag).
             */
            duk__emit_a_bc(comp_ctx,
                          (is_get ? DUK_OP_INITGET : DUK_OP_INITSET) | DUK__EMIT_FLAG_A_IS_SOURCE,
                          st.reg_obj,
                          st.temp_start);   /* temp_start+0 = key, temp_start+1 = closure */

            DUK_ASSERT(st.num_pairs == 0);  /* temp state is reset on next loop */
#if defined(DUK_USE_ES6)
        } else if (comp_ctx->prev_token.t == DUK_TOK_IDENTIFIER &&
                   (comp_ctx->curr_token.t == DUK_TOK_COMMA || comp_ctx->curr_token.t == DUK_TOK_RCURLY)) {
            duk_bool_t load_rc;

            load_rc = duk__objlit_load_key(comp_ctx, res, &comp_ctx->prev_token, reg_temp);
            DUK_UNREF(load_rc);
            DUK_ASSERT(load_rc == 0);  /* always succeeds because token is identifier */

            duk__ivalue_var_hstring(comp_ctx, res, comp_ctx->prev_token.str1);
            DUK_ASSERT(DUK__GETTEMP(comp_ctx) == reg_temp + 1);
            duk__ivalue_toforcedreg(comp_ctx, res, reg_temp + 1);

            st.num_pairs++;
        } else if ((comp_ctx->prev_token.t == DUK_TOK_IDENTIFIER ||
                    comp_ctx->prev_token.t == DUK_TOK_STRING ||
                    comp_ctx->prev_token.t == DUK_TOK_NUMBER) &&
                   comp_ctx->curr_token.t == DUK_TOK_LPAREN) {
            duk_int_t fnum;

            /* Parsing-wise there's a small hickup here: the token parsing
             * state is one step too advanced for the function parse helper
             * compared to other cases.  The current solution is an extra
             * flag to indicate whether function parsing should use the
             * current or the previous token to starting parsing from.
             */

            if (duk__objlit_load_key(comp_ctx, res, &comp_ctx->prev_token, reg_temp) != 0) {
                goto syntax_error;
            }

            fnum = duk__parse_func_like_fnum(comp_ctx, DUK__FUNC_FLAG_USE_PREVTOKEN | DUK__FUNC_FLAG_METDEF);

            duk__emit_a_bc(comp_ctx,
                           DUK_OP_CLOSURE,
                           reg_temp + 1,
                           (duk_regconst_t) fnum);

            st.num_pairs++;
#endif  /* DUK_USE_ES6 */
        } else {
#if defined(DUK_USE_ES6)
            if (comp_ctx->prev_token.t == DUK_TOK_LBRACKET) {
                /* ES2015 computed property name.  Executor ToPropertyKey()
                 * coerces the key at runtime.
                 */
                DUK__SETTEMP(comp_ctx, reg_temp);
                duk__expr_toforcedreg(comp_ctx, res, DUK__BP_FOR_EXPR, reg_temp);
                duk__advance_expect(comp_ctx, DUK_TOK_RBRACKET);

                /* XXX: If next token is '(' we're dealing with
                 * the method shorthand with a computed name,
                 * e.g. { [Symbol.for('foo')](a,b) {} }.  This
                 * form is not yet supported and causes a
                 * SyntaxError on the DUK_TOK_COLON check below.
                 */
            }
            else
#endif  /* DUK_USE_ES6 */
            {
                if (duk__objlit_load_key(comp_ctx, res, &comp_ctx->prev_token, reg_temp) != 0) {
                    goto syntax_error;
                }
            }

            duk__advance_expect(comp_ctx, DUK_TOK_COLON);

            DUK__SETTEMP(comp_ctx, reg_temp + 1);
            duk__expr_toforcedreg(comp_ctx, res, DUK__BP_COMMA /*rbp_flags*/, reg_temp + 1 /*forced_reg*/);

            st.num_pairs++;
        }
    }  /* property loop */

    /* Flush remaining properties. */
    duk__objlit_flush_keys(comp_ctx, &st);
    DUK_ASSERT(st.num_pairs == 0);
    DUK_ASSERT(DUK__GETTEMP(comp_ctx) == st.temp_start);

    /* Update initial size for NEWOBJ.  The init size doesn't need to be
     * exact as the purpose is just to avoid object resizes in common
     * cases.  The size is capped to field A limit, and will be too high
     * if the object literal contains duplicate keys (this is harmless but
     * increases memory traffic if the object is compacted later on).
     */
#if !defined(DUK_USE_PREFER_SIZE)
    instr = duk__get_instr_ptr(comp_ctx, pc_newobj);
    instr->ins |= DUK_ENC_OP_A(0, st.num_total_pairs > DUK_BC_A_MAX ? DUK_BC_A_MAX : st.num_total_pairs);
#endif

    DUK_ASSERT(comp_ctx->curr_token.t == DUK_TOK_RCURLY);
    duk__advance(comp_ctx);  /* No RegExp after object literal. */

    duk__ivalue_regconst(res, st.reg_obj);
    return;

 syntax_error:
    DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_OBJECT_LITERAL);
    DUK_WO_NORETURN(return;);
}

/* Parse argument list.  Arguments are written to temps starting from
 * "next temp".  Returns number of arguments parsed.  Expects left paren
 * to be already eaten, and eats the right paren before returning.
 */
DUK_LOCAL duk_int_t duk__parse_arguments(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
    duk_int_t nargs = 0;
    duk_regconst_t reg_temp;

    /* Note: expect that caller has already eaten the left paren */

    DUK_DDD(DUK_DDDPRINT("start parsing arguments, prev_token.t=%ld, curr_token.t=%ld",
                         (long) comp_ctx->prev_token.t, (long) comp_ctx->curr_token.t));

    for (;;) {
        if (comp_ctx->curr_token.t == DUK_TOK_RPAREN) {
            break;
        }
        if (nargs > 0) {
            duk__advance_expect(comp_ctx, DUK_TOK_COMMA);
        }

        /* We want the argument expression value to go to "next temp"
         * without additional moves.  That should almost always be the
         * case, but we double check after expression parsing.
         *
         * This is not the cleanest possible approach.
         */

        reg_temp = DUK__ALLOCTEMP(comp_ctx);  /* bump up "allocated" reg count, just in case */
        DUK__SETTEMP(comp_ctx, reg_temp);

        /* binding power must be high enough to NOT allow comma expressions directly */
        duk__expr_toforcedreg(comp_ctx, res, DUK__BP_COMMA /*rbp_flags*/, reg_temp);  /* always allow 'in', coerce to 'tr' just in case */

        DUK__SETTEMP(comp_ctx, reg_temp + 1);
        nargs++;

        DUK_DDD(DUK_DDDPRINT("argument #%ld written into reg %ld", (long) nargs, (long) reg_temp));
    }

    /* eat the right paren */
    duk__advance_expect(comp_ctx, DUK_TOK_RPAREN);  /* RegExp mode does not matter. */

    DUK_DDD(DUK_DDDPRINT("end parsing arguments"));

    return nargs;
}

DUK_LOCAL duk_bool_t duk__expr_is_empty(duk_compiler_ctx *comp_ctx) {
    /* empty expressions can be detected conveniently with nud/led counts */
    return (comp_ctx->curr_func.nud_count == 0) &&
           (comp_ctx->curr_func.led_count == 0);
}

DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
    duk_hthread *thr = comp_ctx->thr;
    duk_token *tk;
    duk_regconst_t temp_at_entry;
    duk_small_uint_t tok;
    duk_uint32_t args;  /* temp variable to pass constants and flags to shared code */

    /*
     *  ctx->prev_token     token to process with duk__expr_nud()
     *  ctx->curr_token     updated by caller
     *
     *  Note: the token in the switch below has already been eaten.
     */

    temp_at_entry = DUK__GETTEMP(comp_ctx);

    comp_ctx->curr_func.nud_count++;

    tk = &comp_ctx->prev_token;
    tok = tk->t;
    res->t = DUK_IVAL_NONE;

    DUK_DDD(DUK_DDDPRINT("duk__expr_nud(), prev_token.t=%ld, allow_in=%ld, paren_level=%ld",
                         (long) tk->t, (long) comp_ctx->curr_func.allow_in, (long) comp_ctx->curr_func.paren_level));

    switch (tok) {

    /* PRIMARY EXPRESSIONS */

    case DUK_TOK_THIS: {
        duk_regconst_t reg_temp;
        reg_temp = DUK__ALLOCTEMP(comp_ctx);
        duk__emit_bc(comp_ctx,
                     DUK_OP_LDTHIS,
                     reg_temp);
        duk__ivalue_regconst(res, reg_temp);
        return;
    }
    case DUK_TOK_IDENTIFIER: {
        duk__ivalue_var_hstring(comp_ctx, res, tk->str1);
        return;
    }
    case DUK_TOK_NULL: {
        duk_push_null(thr);
        goto plain_value;
    }
    case DUK_TOK_TRUE: {
        duk_push_true(thr);
        goto plain_value;
    }
    case DUK_TOK_FALSE: {
        duk_push_false(thr);
        goto plain_value;
    }
    case DUK_TOK_NUMBER: {
        duk_push_number(thr, tk->num);
        goto plain_value;
    }
    case DUK_TOK_STRING: {
        DUK_ASSERT(tk->str1 != NULL);
        duk_push_hstring(thr, tk->str1);
        goto plain_value;
    }
    case DUK_TOK_REGEXP: {
#if defined(DUK_USE_REGEXP_SUPPORT)
        duk_regconst_t reg_temp;
        duk_regconst_t rc_re_bytecode;  /* const */
        duk_regconst_t rc_re_source;    /* const */

        DUK_ASSERT(tk->str1 != NULL);
        DUK_ASSERT(tk->str2 != NULL);

        DUK_DDD(DUK_DDDPRINT("emitting regexp op, str1=%!O, str2=%!O",
                             (duk_heaphdr *) tk->str1,
                             (duk_heaphdr *) tk->str2));

        reg_temp = DUK__ALLOCTEMP(comp_ctx);
        duk_push_hstring(thr, tk->str1);
        duk_push_hstring(thr, tk->str2);

        /* [ ... pattern flags ] */

        duk_regexp_compile(thr);

        /* [ ... escaped_source bytecode ] */

        rc_re_bytecode = duk__getconst(comp_ctx);
        rc_re_source = duk__getconst(comp_ctx);

        duk__emit_a_b_c(comp_ctx,
                        DUK_OP_REGEXP | DUK__EMIT_FLAG_BC_REGCONST,
                        reg_temp /*a*/,
                        rc_re_bytecode /*b*/,
                        rc_re_source /*c*/);

        duk__ivalue_regconst(res, reg_temp);
        return;
#else  /* DUK_USE_REGEXP_SUPPORT */
        goto syntax_error;
#endif  /* DUK_USE_REGEXP_SUPPORT */
    }
    case DUK_TOK_LBRACKET: {
        DUK_DDD(DUK_DDDPRINT("parsing array literal"));
        duk__nud_array_literal(comp_ctx, res);
        return;
    }
    case DUK_TOK_LCURLY: {
        DUK_DDD(DUK_DDDPRINT("parsing object literal"));
        duk__nud_object_literal(comp_ctx, res);
        return;
    }
    case DUK_TOK_LPAREN: {
        duk_bool_t prev_allow_in;

        comp_ctx->curr_func.paren_level++;
        prev_allow_in = comp_ctx->curr_func.allow_in;
        comp_ctx->curr_func.allow_in = 1; /* reset 'allow_in' for parenthesized expression */

        duk__expr(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/);  /* Expression, terminates at a ')' */

        duk__advance_expect(comp_ctx, DUK_TOK_RPAREN);  /* No RegExp after parenthesized expression. */
        comp_ctx->curr_func.allow_in = prev_allow_in;
        comp_ctx->curr_func.paren_level--;
        return;
    }

    /* MEMBER/NEW/CALL EXPRESSIONS */

    case DUK_TOK_NEW: {
        /*
         *  Parsing an expression starting with 'new' is tricky because
         *  there are multiple possible productions deriving from
         *  LeftHandSideExpression which begin with 'new'.
         *
         *  We currently resort to one-token lookahead to distinguish the
         *  cases.  Hopefully this is correct.  The binding power must be
         *  such that parsing ends at an LPAREN (CallExpression) but not at
         *  a PERIOD or LBRACKET (MemberExpression).
         *
         *  See doc/compiler.rst for discussion on the parsing approach,
         *  and testcases/test-dev-new.js for a bunch of documented tests.
         */

        duk_regconst_t reg_target;
        duk_int_t nargs;

        DUK_DDD(DUK_DDDPRINT("begin parsing new expression"));

        reg_target = DUK__ALLOCTEMPS(comp_ctx, 2);

#if defined(DUK_USE_ES6)
        if (comp_ctx->curr_token.t == DUK_TOK_PERIOD) {
            /* new.target */
            DUK_DDD(DUK_DDDPRINT("new.target"));
            duk__advance(comp_ctx);
            if (comp_ctx->curr_token.t_nores != DUK_TOK_IDENTIFIER ||
                !duk_hstring_equals_ascii_cstring(comp_ctx->curr_token.str1, "target")) {
                goto syntax_error_newtarget;
            }
            if (comp_ctx->curr_func.is_global) {
                goto syntax_error_newtarget;
            }
            duk__advance(comp_ctx);
            duk__emit_bc(comp_ctx,
                         DUK_OP_NEWTARGET,
                         reg_target);
            duk__ivalue_regconst(res, reg_target);
            return;
        }
#endif  /* DUK_USE_ES6 */

        duk__expr_toforcedreg(comp_ctx, res, DUK__BP_CALL /*rbp_flags*/, reg_target /*forced_reg*/);
        duk__emit_bc(comp_ctx, DUK_OP_NEWOBJ, reg_target + 1);  /* default instance */
        DUK__SETTEMP(comp_ctx, reg_target + 2);

        /* XXX: 'new obj.noSuch()' doesn't use GETPROPC now which
         * makes the error message worse than for obj.noSuch().
         */

        if (comp_ctx->curr_token.t == DUK_TOK_LPAREN) {
            /* 'new' MemberExpression Arguments */
            DUK_DDD(DUK_DDDPRINT("new expression has argument list"));
            duk__advance(comp_ctx);
            nargs = duk__parse_arguments(comp_ctx, res);  /* parse args starting from "next temp", reg_target + 1 */
            /* right paren eaten */
        } else {
            /* 'new' MemberExpression */
            DUK_DDD(DUK_DDDPRINT("new expression has no argument list"));
            nargs = 0;
        }

        duk__emit_a_bc(comp_ctx,
                      DUK_OP_CALL0 | DUK_BC_CALL_FLAG_CONSTRUCT,
                      nargs /*num_args*/,
                      reg_target /*target*/);

        DUK_DDD(DUK_DDDPRINT("end parsing new expression"));

        duk__ivalue_regconst(res, reg_target);
        return;
    }

    /* FUNCTION EXPRESSIONS */

    case DUK_TOK_FUNCTION: {
        /* Function expression.  Note that any statement beginning with 'function'
         * is handled by the statement parser as a function declaration, or a
         * non-standard function expression/statement (or a SyntaxError).  We only
         * handle actual function expressions (occurring inside an expression) here.
         *
         * O(depth^2) parse count for inner functions is handled by recording a
         * lexer offset on the first compilation pass, so that the function can
         * be efficiently skipped on the second pass.  This is encapsulated into
         * duk__parse_func_like_fnum().
         */

        duk_regconst_t reg_temp;
        duk_int_t fnum;

        reg_temp = DUK__ALLOCTEMP(comp_ctx);

        /* curr_token follows 'function' */
        fnum = duk__parse_func_like_fnum(comp_ctx, 0 /*flags*/);
        DUK_DDD(DUK_DDDPRINT("parsed inner function -> fnum %ld", (long) fnum));

        duk__emit_a_bc(comp_ctx,
                       DUK_OP_CLOSURE,
                       reg_temp /*a*/,
                       (duk_regconst_t) fnum /*bc*/);

        duk__ivalue_regconst(res, reg_temp);
        return;
    }

    /* UNARY EXPRESSIONS */

    case DUK_TOK_DELETE: {
        /* Delete semantics are a bit tricky.  The description in E5 specification
         * is kind of confusing, because it distinguishes between resolvability of
         * a reference (which is only known at runtime) seemingly at compile time
         * (= SyntaxError throwing).
         */
        duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/);  /* UnaryExpression */
        if (res->t == DUK_IVAL_VAR) {
            /* not allowed in strict mode, regardless of whether resolves;
             * in non-strict mode DELVAR handles both non-resolving and
             * resolving cases (the specification description is a bit confusing).
             */

            duk_regconst_t reg_temp;
            duk_regconst_t reg_varbind;
            duk_regconst_t rc_varname;

            if (comp_ctx->curr_func.is_strict) {
                DUK_ERROR_SYNTAX(thr, DUK_STR_CANNOT_DELETE_IDENTIFIER);
                DUK_WO_NORETURN(return;);
            }

            DUK__SETTEMP(comp_ctx, temp_at_entry);
            reg_temp = DUK__ALLOCTEMP(comp_ctx);

            duk_dup(thr, res->x1.valstack_idx);
            if (duk__lookup_lhs(comp_ctx, &reg_varbind, &rc_varname)) {
                /* register bound variables are non-configurable -> always false */
                duk__emit_bc(comp_ctx,
                             DUK_OP_LDFALSE,
                             reg_temp);
            } else {
                duk_dup(thr, res->x1.valstack_idx);
                rc_varname = duk__getconst(comp_ctx);
                duk__emit_a_bc(comp_ctx,
                               DUK_OP_DELVAR,
                               reg_temp,
                               rc_varname);
            }
            duk__ivalue_regconst(res, reg_temp);
        } else if (res->t == DUK_IVAL_PROP) {
            duk_regconst_t reg_temp;
            duk_regconst_t reg_obj;
            duk_regconst_t rc_key;

            DUK__SETTEMP(comp_ctx, temp_at_entry);
            reg_temp = DUK__ALLOCTEMP(comp_ctx);
            reg_obj = duk__ispec_toregconst_raw(comp_ctx, &res->x1, -1 /*forced_reg*/, 0 /*flags*/);  /* don't allow const */
            rc_key = duk__ispec_toregconst_raw(comp_ctx, &res->x2, -1 /*forced_reg*/, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/);
            duk__emit_a_b_c(comp_ctx,
                            DUK_OP_DELPROP | DUK__EMIT_FLAG_BC_REGCONST,
                            reg_temp,
                            reg_obj,
                            rc_key);

            duk__ivalue_regconst(res, reg_temp);
        } else {
            /* non-Reference deletion is always 'true', even in strict mode */
            duk_push_true(thr);
            goto plain_value;
        }
        return;
    }
    case DUK_TOK_VOID: {
        duk__expr_toplain_ignore(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/);  /* UnaryExpression */
        duk_push_undefined(thr);
        goto plain_value;
    }
    case DUK_TOK_TYPEOF: {
        /* 'typeof' must handle unresolvable references without throwing
         * a ReferenceError (E5 Section 11.4.3).  Register mapped values
         * will never be unresolvable so special handling is only required
         * when an identifier is a "slow path" one.
         */
        duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/);  /* UnaryExpression */

        if (res->t == DUK_IVAL_VAR) {
            duk_regconst_t reg_varbind;
            duk_regconst_t rc_varname;
            duk_regconst_t reg_temp;

            duk_dup(thr, res->x1.valstack_idx);
            if (!duk__lookup_lhs(comp_ctx, &reg_varbind, &rc_varname)) {
                DUK_DDD(DUK_DDDPRINT("typeof for an identifier name which could not be resolved "
                                     "at compile time, need to use special run-time handling"));
                reg_temp = DUK__ALLOCTEMP(comp_ctx);
                duk__emit_a_bc(comp_ctx,
                               DUK_OP_TYPEOFID,
                               reg_temp,
                               rc_varname);
                duk__ivalue_regconst(res, reg_temp);
                return;
            }
        }

        args = DUK_OP_TYPEOF;
        goto unary;
    }
    case DUK_TOK_INCREMENT: {
        args = (DUK_OP_PREINCP << 8) + DUK_OP_PREINCR;
        goto preincdec;
    }
    case DUK_TOK_DECREMENT: {
        args = (DUK_OP_PREDECP << 8) + DUK_OP_PREDECR;
        goto preincdec;
    }
    case DUK_TOK_ADD: {
        /* unary plus */
        duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/);  /* UnaryExpression */
        if (res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_VALUE &&
            duk_is_number(thr, res->x1.valstack_idx)) {
            /* unary plus of a number is identity */
            return;
        }
        args = DUK_OP_UNP;
        goto unary;
    }
    case DUK_TOK_SUB: {
        /* unary minus */
        duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/);  /* UnaryExpression */
        if (res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_VALUE &&
            duk_is_number(thr, res->x1.valstack_idx)) {
            /* this optimization is important to handle negative literals
             * (which are not directly provided by the lexical grammar)
             */
            duk_tval *tv_num;
            duk_double_union du;

            tv_num = DUK_GET_TVAL_POSIDX(thr, res->x1.valstack_idx);
            DUK_ASSERT(tv_num != NULL);
            DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_num));
            du.d = DUK_TVAL_GET_NUMBER(tv_num);
            du.d = -du.d;
            DUK_DBLUNION_NORMALIZE_NAN_CHECK(&du);
            DUK_TVAL_SET_NUMBER(tv_num, du.d);
            return;
        }
        args = DUK_OP_UNM;
        goto unary;
    }
    case DUK_TOK_BNOT: {
        duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/);  /* UnaryExpression */
        args = DUK_OP_BNOT;
        goto unary;
    }
    case DUK_TOK_LNOT: {
        duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/);  /* UnaryExpression */
        if (res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_VALUE) {
            /* Very minimal inlining to handle common idioms '!0' and '!1',
             * and also boolean arguments like '!false' and '!true'.
             */
            duk_tval *tv_val;

            tv_val = DUK_GET_TVAL_POSIDX(thr, res->x1.valstack_idx);
            DUK_ASSERT(tv_val != NULL);
            if (DUK_TVAL_IS_NUMBER(tv_val)) {
                duk_double_t d;
                d = DUK_TVAL_GET_NUMBER(tv_val);
                if (duk_double_equals(d, 0.0)) {
                    /* Matches both +0 and -0 on purpose. */
                    DUK_DDD(DUK_DDDPRINT("inlined lnot: !0 -> true"));
                    DUK_TVAL_SET_BOOLEAN_TRUE(tv_val);
                    return;
                } else if (duk_double_equals(d, 1.0)) {
                    DUK_DDD(DUK_DDDPRINT("inlined lnot: !1 -> false"));
                    DUK_TVAL_SET_BOOLEAN_FALSE(tv_val);
                    return;
                }
            } else if (DUK_TVAL_IS_BOOLEAN(tv_val)) {
                duk_small_uint_t v;
                v = DUK_TVAL_GET_BOOLEAN(tv_val);
                DUK_DDD(DUK_DDDPRINT("inlined lnot boolean: %ld", (long) v));
                DUK_ASSERT(v == 0 || v == 1);
                DUK_TVAL_SET_BOOLEAN(tv_val, v ^ 0x01);
                return;
            }
        }
        args = DUK_OP_LNOT;
        goto unary;
    }

    }  /* end switch */

    DUK_ERROR_SYNTAX(thr, DUK_STR_PARSE_ERROR);
    DUK_WO_NORETURN(return;);

 unary:
    {
        /* Unary opcodes use just the 'BC' register source because it
         * matches current shuffle limits, and maps cleanly to 16 high
         * bits of the opcode.
         */

        duk_regconst_t reg_src, reg_res;

        reg_src = duk__ivalue_toregconst_raw(comp_ctx, res, -1 /*forced_reg*/, 0 /*flags*/);
        if (DUK__ISREG_TEMP(comp_ctx, reg_src)) {
            reg_res = reg_src;
        } else {
            reg_res = DUK__ALLOCTEMP(comp_ctx);
        }
        duk__emit_a_bc(comp_ctx,
                     args,
                     reg_res,
                     reg_src);
        duk__ivalue_regconst(res, reg_res);
        return;
    }

 preincdec:
    {
        /* preincrement and predecrement */
        duk_regconst_t reg_res;
        duk_small_uint_t args_op1 = args & 0xff;  /* DUK_OP_PREINCR/DUK_OP_PREDECR */
        duk_small_uint_t args_op2 = args >> 8;    /* DUK_OP_PREINCP_RR/DUK_OP_PREDECP_RR */

        /* Specific assumptions for opcode numbering. */
        DUK_ASSERT(DUK_OP_PREINCR + 4 == DUK_OP_PREINCV);
        DUK_ASSERT(DUK_OP_PREDECR + 4 == DUK_OP_PREDECV);

        reg_res = DUK__ALLOCTEMP(comp_ctx);

        duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/);  /* UnaryExpression */
        if (res->t == DUK_IVAL_VAR) {
            duk_hstring *h_varname;
            duk_regconst_t reg_varbind;
            duk_regconst_t rc_varname;

            h_varname = duk_known_hstring(thr, res->x1.valstack_idx);

            if (duk__hstring_is_eval_or_arguments_in_strict_mode(comp_ctx, h_varname)) {
                goto syntax_error;
            }

            duk_dup(thr, res->x1.valstack_idx);
            if (duk__lookup_lhs(comp_ctx, &reg_varbind, &rc_varname)) {
                duk__emit_a_bc(comp_ctx,
                               args_op1,  /* e.g. DUK_OP_PREINCR */
                               reg_res,
                               reg_varbind);
            } else {
                duk__emit_a_bc(comp_ctx,
                                args_op1 + 4,  /* e.g. DUK_OP_PREINCV */
                                reg_res,
                                rc_varname);
            }

            DUK_DDD(DUK_DDDPRINT("preincdec to '%!O' -> reg_varbind=%ld, rc_varname=%ld",
                                 (duk_heaphdr *) h_varname, (long) reg_varbind, (long) rc_varname));
        } else if (res->t == DUK_IVAL_PROP) {
            duk_regconst_t reg_obj;  /* allocate to reg only (not const) */
            duk_regconst_t rc_key;
            reg_obj = duk__ispec_toregconst_raw(comp_ctx, &res->x1, -1 /*forced_reg*/, 0 /*flags*/);  /* don't allow const */
            rc_key = duk__ispec_toregconst_raw(comp_ctx, &res->x2, -1 /*forced_reg*/, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/);
            duk__emit_a_b_c(comp_ctx,
                            args_op2 | DUK__EMIT_FLAG_BC_REGCONST,  /* e.g. DUK_OP_PREINCP */
                            reg_res,
                            reg_obj,
                            rc_key);
        } else {
            /* Technically return value is not needed because INVLHS will
             * unconditially throw a ReferenceError.  Coercion is necessary
             * for proper semantics (consider ToNumber() called for an object).
             * Use DUK_OP_UNP with a dummy register to get ToNumber().
             */

            duk__ivalue_toforcedreg(comp_ctx, res, reg_res);
            duk__emit_bc(comp_ctx,
                         DUK_OP_UNP,
                         reg_res);  /* for side effects, result ignored */
            duk__emit_op_only(comp_ctx,
                              DUK_OP_INVLHS);
        }
        DUK__SETTEMP(comp_ctx, reg_res + 1);
        duk__ivalue_regconst(res, reg_res);
        return;
    }

 plain_value:
    {
        /* Stack top contains plain value */
        duk__ivalue_plain_fromstack(comp_ctx, res);
        return;
    }

#if defined(DUK_USE_ES6)
 syntax_error_newtarget:
    DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_NEWTARGET);
    DUK_WO_NORETURN(return;);
#endif

 syntax_error:
    DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_EXPRESSION);
    DUK_WO_NORETURN(return;);
}

/* XXX: add flag to indicate whether caller cares about return value; this
 * affects e.g. handling of assignment expressions.  This change needs API
 * changes elsewhere too.
 */
DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_ivalue *res) {
    duk_hthread *thr = comp_ctx->thr;
    duk_token *tk;
    duk_small_uint_t tok;
    duk_uint32_t args;  /* temp variable to pass constants and flags to shared code */

    /*
     *  ctx->prev_token     token to process with duk__expr_led()
     *  ctx->curr_token     updated by caller
     */

    comp_ctx->curr_func.led_count++;

    /* The token in the switch has already been eaten here */
    tk = &comp_ctx->prev_token;
    tok = tk->t;

    DUK_DDD(DUK_DDDPRINT("duk__expr_led(), prev_token.t=%ld, allow_in=%ld, paren_level=%ld",
                         (long) tk->t, (long) comp_ctx->curr_func.allow_in, (long) comp_ctx->curr_func.paren_level));

    /* XXX: default priority for infix operators is duk__expr_lbp(tok) -> get it here? */

    switch (tok) {

    /* PRIMARY EXPRESSIONS */

    case DUK_TOK_PERIOD: {
        /* Property access expressions are critical for correct LHS ordering,
         * see comments in duk__expr()!
         *
         * A conservative approach would be to use duk__ivalue_totempconst()
         * for 'left'.  However, allowing a reg-bound variable seems safe here
         * and is nice because "foo.bar" is a common expression.  If the ivalue
         * is used in an expression a GETPROP will occur before any changes to
         * the base value can occur.  If the ivalue is used as an assignment
         * LHS, the assignment code will ensure the base value is safe from
         * RHS mutation.
         */

        /* XXX: This now coerces an identifier into a GETVAR to a temp, which
         * causes an extra LDREG in call setup.  It's sufficient to coerce to a
         * unary ivalue?
         */
        duk__ivalue_toplain(comp_ctx, left);

        /* NB: must accept reserved words as property name */
        if (comp_ctx->curr_token.t_nores != DUK_TOK_IDENTIFIER) {
            DUK_ERROR_SYNTAX(thr, DUK_STR_EXPECTED_IDENTIFIER);
            DUK_WO_NORETURN(return;);
        }

        res->t = DUK_IVAL_PROP;
        duk__copy_ispec(comp_ctx, &left->x1, &res->x1);  /* left.x1 -> res.x1 */
        DUK_ASSERT(comp_ctx->curr_token.str1 != NULL);
        duk_push_hstring(thr, comp_ctx->curr_token.str1);
        duk_replace(thr, res->x2.valstack_idx);
        res->x2.t = DUK_ISPEC_VALUE;

        /* special RegExp literal handling after IdentifierName */
        comp_ctx->curr_func.reject_regexp_in_adv = 1;

        duk__advance(comp_ctx);
        return;
    }
    case DUK_TOK_LBRACKET: {
        /* Property access expressions are critical for correct LHS ordering,
         * see comments in duk__expr()!
         */

        /* XXX: optimize temp reg use */
        /* XXX: similar coercion issue as in DUK_TOK_PERIOD */
        /* XXX: coerce to regs? it might be better for enumeration use, where the
         * same PROP ivalue is used multiple times.  Or perhaps coerce PROP further
         * there?
         */
        /* XXX: for simple cases like x['y'] an unnecessary LDREG is
         * emitted for the base value; could avoid it if we knew that
         * the key expression is safe (e.g. just a single literal).
         */

        /* The 'left' value must not be a register bound variable
         * because it may be mutated during the rest of the expression
         * and E5.1 Section 11.2.1 specifies the order of evaluation
         * so that the base value is evaluated first.
         * See: test-bug-nested-prop-mutate.js.
         */
        duk__ivalue_totempconst(comp_ctx, left);
        duk__expr_toplain(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/);  /* Expression, ']' terminates */
        duk__advance_expect(comp_ctx, DUK_TOK_RBRACKET);

        res->t = DUK_IVAL_PROP;
        duk__copy_ispec(comp_ctx, &res->x1, &res->x2);   /* res.x1 -> res.x2 */
        duk__copy_ispec(comp_ctx, &left->x1, &res->x1);  /* left.x1 -> res.x1 */
        return;
    }
    case DUK_TOK_LPAREN: {
        /* function call */
        duk_regconst_t reg_cs = DUK__ALLOCTEMPS(comp_ctx, 2);
        duk_int_t nargs;
        duk_small_uint_t call_op = DUK_OP_CALL0;

        /* XXX: attempt to get the call result to "next temp" whenever
         * possible to avoid unnecessary register shuffles.
         */

        /*
         *  Setup call: target and 'this' binding.  Three cases:
         *
         *    1. Identifier base (e.g. "foo()")
         *    2. Property base (e.g. "foo.bar()")
         *    3. Register base (e.g. "foo()()"; i.e. when a return value is a function)
         */

        if (left->t == DUK_IVAL_VAR) {
            duk_hstring *h_varname;
            duk_regconst_t reg_varbind;
            duk_regconst_t rc_varname;

            DUK_DDD(DUK_DDDPRINT("function call with identifier base"));

            h_varname = duk_known_hstring(thr, left->x1.valstack_idx);
            if (h_varname == DUK_HTHREAD_STRING_EVAL(thr)) {
                /* Potential direct eval call detected, flag the CALL
                 * so that a run-time "direct eval" check is made and
                 * special behavior may be triggered.  Note that this
                 * does not prevent 'eval' from being register bound.
                 */
                DUK_DDD(DUK_DDDPRINT("function call with identifier 'eval' "
                                     "-> using EVALCALL, marking function "
                                     "as may_direct_eval"));
                call_op |= DUK_BC_CALL_FLAG_CALLED_AS_EVAL;
                comp_ctx->curr_func.may_direct_eval = 1;
            }

            duk_dup(thr, left->x1.valstack_idx);
            if (duk__lookup_lhs(comp_ctx, &reg_varbind, &rc_varname)) {
                duk__emit_a_bc(comp_ctx,
                              DUK_OP_CSREG | DUK__EMIT_FLAG_A_IS_SOURCE,
                              reg_varbind,
                              reg_cs + 0);
            } else {
                /* XXX: expand target register or constant field to
                 * reduce shuffling.
                 */
                DUK_ASSERT(DUK__ISCONST(rc_varname));
                duk__emit_a_b(comp_ctx,
                              DUK_OP_CSVAR | DUK__EMIT_FLAG_BC_REGCONST,
                              reg_cs + 0,
                              rc_varname);
            }
        } else if (left->t == DUK_IVAL_PROP) {
            /* Call through a property lookup, E5 Section 11.2.3, step 6.a.i,
             * E5 Section 10.4.3.  There used to be a separate CSPROP opcode
             * but a typical call setup took 3 opcodes (e.g. LDREG, LDCONST,
             * CSPROP) and the same can be achieved with ordinary loads.
             */
#if defined(DUK_USE_VERBOSE_ERRORS)
            duk_regconst_t reg_key;
#endif

            DUK_DDD(DUK_DDDPRINT("function call with property base"));

            /* XXX: For Math.sin() this generates: LDCONST + LDREG +
             * GETPROPC + call.  The LDREG is unnecessary because LDCONST
             * could be loaded directly into reg_cs + 1.  This doesn't
             * happen now because a variable cannot be in left->x1 of a
             * DUK_IVAL_PROP.  We could notice that left->x1 is a temp
             * and reuse, but it would still be in the wrong position
             * (reg_cs + 0 rather than reg_cs + 1).
             */
            duk__ispec_toforcedreg(comp_ctx, &left->x1, reg_cs + 1);  /* base */
#if defined(DUK_USE_VERBOSE_ERRORS)
            reg_key = duk__ispec_toregconst_raw(comp_ctx, &left->x2, -1, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/);
            duk__emit_a_b_c(comp_ctx,
                            DUK_OP_GETPROPC | DUK__EMIT_FLAG_BC_REGCONST,
                            reg_cs + 0,
                            reg_cs + 1,
                            reg_key);
#else
            duk__ivalue_toforcedreg(comp_ctx, left, reg_cs + 0);  /* base[key] */
#endif
        } else {
            DUK_DDD(DUK_DDDPRINT("function call with register base"));

            duk__ivalue_toforcedreg(comp_ctx, left, reg_cs + 0);
#if 0
            duk__emit_a_bc(comp_ctx,
                           DUK_OP_CSREG | DUK__EMIT_FLAG_A_IS_SOURCE,
                           reg_cs + 0,
                           reg_cs + 0);  /* in-place setup */
#endif
            /* Because of in-place setup, REGCS is equivalent to
             * just this LDUNDEF.
             */
            duk__emit_bc(comp_ctx, DUK_OP_LDUNDEF, reg_cs + 1);
        }

        DUK__SETTEMP(comp_ctx, reg_cs + 2);
        nargs = duk__parse_arguments(comp_ctx, res);  /* parse args starting from "next temp" */

        /* Tailcalls are handled by back-patching the already emitted opcode
         * later in return statement parser.
         */

        duk__emit_a_bc(comp_ctx,
                       call_op,
                       (duk_regconst_t) nargs /*numargs*/,
                       reg_cs /*basereg*/);
        DUK__SETTEMP(comp_ctx, reg_cs + 1);    /* result in csreg */

        duk__ivalue_regconst(res, reg_cs);
        return;
    }

    /* POSTFIX EXPRESSION */

    case DUK_TOK_INCREMENT: {
        args = (DUK_OP_POSTINCP_RR << 16) + (DUK_OP_POSTINCR << 8) + 0;
        goto postincdec;
    }
    case DUK_TOK_DECREMENT: {
        args = (DUK_OP_POSTDECP_RR << 16) + (DUK_OP_POSTDECR << 8) + 0;
        goto postincdec;
    }

    /* EXPONENTIATION EXPRESSION */

#if defined(DUK_USE_ES7_EXP_OPERATOR)
    case DUK_TOK_EXP: {
        args = (DUK_OP_EXP << 8) + DUK__BP_EXPONENTIATION - 1;  /* UnaryExpression */
        goto binary;
    }
#endif

    /* MULTIPLICATIVE EXPRESSION */

    case DUK_TOK_MUL: {
        args = (DUK_OP_MUL << 8) + DUK__BP_MULTIPLICATIVE;  /* ExponentiationExpression */
        goto binary;
    }
    case DUK_TOK_DIV: {
        args = (DUK_OP_DIV << 8) + DUK__BP_MULTIPLICATIVE;  /* ExponentiationExpression */
        goto binary;
    }
    case DUK_TOK_MOD: {
        args = (DUK_OP_MOD << 8) + DUK__BP_MULTIPLICATIVE;  /* ExponentiationExpression */
        goto binary;
    }

    /* ADDITIVE EXPRESSION */

    case DUK_TOK_ADD: {
        args = (DUK_OP_ADD << 8) + DUK__BP_ADDITIVE;  /* MultiplicativeExpression */
        goto binary;
    }
    case DUK_TOK_SUB: {
        args = (DUK_OP_SUB << 8) + DUK__BP_ADDITIVE;  /* MultiplicativeExpression */
        goto binary;
    }

    /* SHIFT EXPRESSION */

    case DUK_TOK_ALSHIFT: {
        /* << */
        args = (DUK_OP_BASL << 8) + DUK__BP_SHIFT;
        goto binary;
    }
    case DUK_TOK_ARSHIFT: {
        /* >> */
        args = (DUK_OP_BASR << 8) + DUK__BP_SHIFT;
        goto binary;
    }
    case DUK_TOK_RSHIFT: {
        /* >>> */
        args = (DUK_OP_BLSR << 8) + DUK__BP_SHIFT;
        goto binary;
    }

    /* RELATIONAL EXPRESSION */

    case DUK_TOK_LT: {
        /* < */
        args = (DUK_OP_LT << 8) + DUK__BP_RELATIONAL;
        goto binary;
    }
    case DUK_TOK_GT: {
        args = (DUK_OP_GT << 8) + DUK__BP_RELATIONAL;
        goto binary;
    }
    case DUK_TOK_LE: {
        args = (DUK_OP_LE << 8) + DUK__BP_RELATIONAL;
        goto binary;
    }
    case DUK_TOK_GE: {
        args = (DUK_OP_GE << 8) + DUK__BP_RELATIONAL;
        goto binary;
    }
    case DUK_TOK_INSTANCEOF: {
        args = (DUK_OP_INSTOF << 8) + DUK__BP_RELATIONAL;
        goto binary;
    }
    case DUK_TOK_IN: {
        args = (DUK_OP_IN << 8) + DUK__BP_RELATIONAL;
        goto binary;
    }

    /* EQUALITY EXPRESSION */

    case DUK_TOK_EQ: {
        args = (DUK_OP_EQ << 8) + DUK__BP_EQUALITY;
        goto binary;
    }
    case DUK_TOK_NEQ: {
        args = (DUK_OP_NEQ << 8) + DUK__BP_EQUALITY;
        goto binary;
    }
    case DUK_TOK_SEQ: {
        args = (DUK_OP_SEQ << 8) + DUK__BP_EQUALITY;
        goto binary;
    }
    case DUK_TOK_SNEQ: {
        args = (DUK_OP_SNEQ << 8) + DUK__BP_EQUALITY;
        goto binary;
    }

    /* BITWISE EXPRESSIONS */

    case DUK_TOK_BAND: {
        args = (DUK_OP_BAND << 8) + DUK__BP_BAND;
        goto binary;
    }
    case DUK_TOK_BXOR: {
        args = (DUK_OP_BXOR << 8) + DUK__BP_BXOR;
        goto binary;
    }
    case DUK_TOK_BOR: {
        args = (DUK_OP_BOR << 8) + DUK__BP_BOR;
        goto binary;
    }

    /* LOGICAL EXPRESSIONS */

    case DUK_TOK_LAND: {
        /* syntactically left-associative but parsed as right-associative */
        args = (1 << 8) + DUK__BP_LAND - 1;
        goto binary_logical;
    }
    case DUK_TOK_LOR: {
        /* syntactically left-associative but parsed as right-associative */
        args = (0 << 8) + DUK__BP_LOR - 1;
        goto binary_logical;
    }

    /* CONDITIONAL EXPRESSION */

    case DUK_TOK_QUESTION: {
        /* XXX: common reg allocation need is to reuse a sub-expression's temp reg,
         * but only if it really is a temp.  Nothing fancy here now.
         */
        duk_regconst_t reg_temp;
        duk_int_t pc_jump1;
        duk_int_t pc_jump2;

        reg_temp = DUK__ALLOCTEMP(comp_ctx);
        duk__ivalue_toforcedreg(comp_ctx, left, reg_temp);
        duk__emit_if_true_skip(comp_ctx, reg_temp);
        pc_jump1 = duk__emit_jump_empty(comp_ctx);  /* jump to false */
        duk__expr_toforcedreg(comp_ctx, res, DUK__BP_COMMA /*rbp_flags*/, reg_temp /*forced_reg*/);  /* AssignmentExpression */
        duk__advance_expect(comp_ctx, DUK_TOK_COLON);
        pc_jump2 = duk__emit_jump_empty(comp_ctx);  /* jump to end */
        duk__patch_jump_here(comp_ctx, pc_jump1);
        duk__expr_toforcedreg(comp_ctx, res, DUK__BP_COMMA /*rbp_flags*/, reg_temp /*forced_reg*/);  /* AssignmentExpression */
        duk__patch_jump_here(comp_ctx, pc_jump2);

        DUK__SETTEMP(comp_ctx, reg_temp + 1);
        duk__ivalue_regconst(res, reg_temp);
        return;
    }

    /* ASSIGNMENT EXPRESSION */

    case DUK_TOK_EQUALSIGN: {
        /*
         *  Assignments are right associative, allows e.g.
         *    a = 5;
         *    a += b = 9;   // same as a += (b = 9)
         *  -> expression value 14, a = 14, b = 9
         *
         *  Right associativiness is reflected in the BP for recursion,
         *  "-1" ensures assignment operations are allowed.
         *
         *  XXX: just use DUK__BP_COMMA (i.e. no need for 2-step bp levels)?
         */
        args = (DUK_OP_NONE << 8) + DUK__BP_ASSIGNMENT - 1;   /* DUK_OP_NONE marks a 'plain' assignment */
        goto assign;
    }
    case DUK_TOK_ADD_EQ: {
        /* right associative */
        args = (DUK_OP_ADD << 8) + DUK__BP_ASSIGNMENT - 1;
        goto assign;
    }
    case DUK_TOK_SUB_EQ: {
        /* right associative */
        args = (DUK_OP_SUB << 8) + DUK__BP_ASSIGNMENT - 1;
        goto assign;
    }
    case DUK_TOK_MUL_EQ: {
        /* right associative */
        args = (DUK_OP_MUL << 8) + DUK__BP_ASSIGNMENT - 1;
        goto assign;
    }
    case DUK_TOK_DIV_EQ: {
        /* right associative */
        args = (DUK_OP_DIV << 8) + DUK__BP_ASSIGNMENT - 1;
        goto assign;
    }
    case DUK_TOK_MOD_EQ: {
        /* right associative */
        args = (DUK_OP_MOD << 8) + DUK__BP_ASSIGNMENT - 1;
        goto assign;
    }
#if defined(DUK_USE_ES7_EXP_OPERATOR)
    case DUK_TOK_EXP_EQ: {
        /* right associative */
        args = (DUK_OP_EXP << 8) + DUK__BP_ASSIGNMENT - 1;
        goto assign;
    }
#endif
    case DUK_TOK_ALSHIFT_EQ: {
        /* right associative */
        args = (DUK_OP_BASL << 8) + DUK__BP_ASSIGNMENT - 1;
        goto assign;
    }
    case DUK_TOK_ARSHIFT_EQ: {
        /* right associative */
        args = (DUK_OP_BASR << 8) + DUK__BP_ASSIGNMENT - 1;
        goto assign;
    }
    case DUK_TOK_RSHIFT_EQ: {
        /* right associative */
        args = (DUK_OP_BLSR << 8) + DUK__BP_ASSIGNMENT - 1;
        goto assign;
    }
    case DUK_TOK_BAND_EQ: {
        /* right associative */
        args = (DUK_OP_BAND << 8) + DUK__BP_ASSIGNMENT - 1;
        goto assign;
    }
    case DUK_TOK_BOR_EQ: {
        /* right associative */
        args = (DUK_OP_BOR << 8) + DUK__BP_ASSIGNMENT - 1;
        goto assign;
    }
    case DUK_TOK_BXOR_EQ: {
        /* right associative */
        args = (DUK_OP_BXOR << 8) + DUK__BP_ASSIGNMENT - 1;
        goto assign;
    }

    /* COMMA */

    case DUK_TOK_COMMA: {
        /* right associative */

        duk__ivalue_toplain_ignore(comp_ctx, left);  /* need side effects, not value */
        duk__expr_toplain(comp_ctx, res, DUK__BP_COMMA - 1 /*rbp_flags*/);

        /* return 'res' (of right part) as our result */
        return;
    }

    default: {
        break;
    }
    }

    DUK_D(DUK_DPRINT("parse error: unexpected token: %ld", (long) tok));
    DUK_ERROR_SYNTAX(thr, DUK_STR_PARSE_ERROR);
    DUK_WO_NORETURN(return;);

#if 0
    /* XXX: shared handling for 'duk__expr_lhs'? */
    if (comp_ctx->curr_func.paren_level == 0 && XXX) {
        comp_ctx->curr_func.duk__expr_lhs = 0;
    }
#endif

 binary:
    /*
     *  Shared handling of binary operations
     *
     *  args = (opcode << 8) + rbp
     */
    {
        duk__ivalue_toplain(comp_ctx, left);
        duk__expr_toplain(comp_ctx, res, args & 0xff /*rbp_flags*/);

        /* combine left->x1 and res->x1 (right->x1, really) -> (left->x1 OP res->x1) */
        DUK_ASSERT(left->t == DUK_IVAL_PLAIN);
        DUK_ASSERT(res->t == DUK_IVAL_PLAIN);

        res->t = DUK_IVAL_ARITH;
        res->op = (args >> 8) & 0xff;

        res->x2.t = res->x1.t;
        res->x2.regconst = res->x1.regconst;
        duk_copy(thr, res->x1.valstack_idx, res->x2.valstack_idx);

        res->x1.t = left->x1.t;
        res->x1.regconst = left->x1.regconst;
        duk_copy(thr, left->x1.valstack_idx, res->x1.valstack_idx);

        DUK_DDD(DUK_DDDPRINT("binary op, res: t=%ld, x1.t=%ld, x1.regconst=0x%08lx, x2.t=%ld, x2.regconst=0x%08lx",
                             (long) res->t, (long) res->x1.t, (unsigned long) res->x1.regconst, (long) res->x2.t, (unsigned long) res->x2.regconst));
        return;
    }

 binary_logical:
    /*
     *  Shared handling for logical AND and logical OR.
     *
     *  args = (truthval << 8) + rbp
     *
     *  Truthval determines when to skip right-hand-side.
     *  For logical AND truthval=1, for logical OR truthval=0.
     *
     *  See doc/compiler.rst for discussion on compiling logical
     *  AND and OR expressions.  The approach here is very simplistic,
     *  generating extra jumps and multiple evaluations of truth values,
     *  but generates code on-the-fly with only local back-patching.
     *
     *  Both logical AND and OR are syntactically left-associated.
     *  However, logical ANDs are compiled as right associative
     *  expressions, i.e. "A && B && C" as "A && (B && C)", to allow
     *  skip jumps to skip over the entire tail.  Similarly for logical OR.
     */

    {
        duk_regconst_t reg_temp;
        duk_int_t pc_jump;
        duk_small_uint_t args_truthval = args >> 8;
        duk_small_uint_t args_rbp = args & 0xff;

        /* XXX: unoptimal use of temps, resetting */

        reg_temp = DUK__ALLOCTEMP(comp_ctx);

        duk__ivalue_toforcedreg(comp_ctx, left, reg_temp);
        DUK_ASSERT(DUK__ISREG(reg_temp));
        duk__emit_bc(comp_ctx,
                    (args_truthval ? DUK_OP_IFTRUE_R : DUK_OP_IFFALSE_R),
                    reg_temp);  /* skip jump conditionally */
        pc_jump = duk__emit_jump_empty(comp_ctx);
        duk__expr_toforcedreg(comp_ctx, res, args_rbp /*rbp_flags*/, reg_temp /*forced_reg*/);
        duk__patch_jump_here(comp_ctx, pc_jump);

        duk__ivalue_regconst(res, reg_temp);
        return;
    }

 assign:
    /*
     *  Shared assignment expression handling
     *
     *  args = (opcode << 8) + rbp
     *
     *  If 'opcode' is DUK_OP_NONE, plain assignment without arithmetic.
     *  Syntactically valid left-hand-side forms which are not accepted as
     *  left-hand-side values (e.g. as in "f() = 1") must NOT cause a
     *  SyntaxError, but rather a run-time ReferenceError.
     *
     *  When evaluating X <op>= Y, the LHS (X) is conceptually evaluated
     *  to a temporary first.  The RHS is then evaluated.  Finally, the
     *  <op> is applied to the initial value of RHS (not the value after
     *  RHS evaluation), and written to X.  Doing so concretely generates
     *  inefficient code so we'd like to avoid the temporary when possible.
     *  See: https://github.com/svaarala/duktape/pull/992.
     *
     *  The expression value (final LHS value, written to RHS) is
     *  conceptually copied into a fresh temporary so that it won't
     *  change even if the LHS/RHS values change in outer expressions.
     *  For example, it'd be generally incorrect for the expression value
     *  to be the RHS register binding, unless there's a guarantee that it
     *  won't change during further expression evaluation.  Using the
     *  temporary concretely produces inefficient bytecode, so we try to
     *  avoid the extra temporary for some known-to-be-safe cases.
     *  Currently the only safe case we detect is a "top level assignment",
     *  for example "x = y + z;", where the assignment expression value is
     *  ignored.
     *  See: test-dev-assign-expr.js and test-bug-assign-mutate-gh381.js.
     */

    {
        duk_small_uint_t args_op = args >> 8;
        duk_small_uint_t args_rbp = args & 0xff;
        duk_bool_t toplevel_assign;

        /* XXX: here we need to know if 'left' is left-hand-side compatible.
         * That information is no longer available from current expr parsing
         * state; it would need to be carried into the 'left' ivalue or by
         * some other means.
         */

        /* A top-level assignment is e.g. "x = y;".  For these it's safe
         * to use the RHS as-is as the expression value, even if the RHS
         * is a reg-bound identifier.  The RHS ('res') is right associative
         * so it has consumed all other assignment level operations; the
         * only relevant lower binding power construct is comma operator
         * which will ignore the expression value provided here.  Usually
         * the top level assignment expression value is ignored, but it
         * is relevant for e.g. eval code.
         */
        toplevel_assign = (comp_ctx->curr_func.nud_count == 1 && /* one token before */
                           comp_ctx->curr_func.led_count == 1);  /* one operator (= assign) */
        DUK_DDD(DUK_DDDPRINT("assignment: nud_count=%ld, led_count=%ld, toplevel_assign=%ld",
                             (long) comp_ctx->curr_func.nud_count,
                             (long) comp_ctx->curr_func.led_count,
                             (long) toplevel_assign));

        if (left->t == DUK_IVAL_VAR) {
            duk_hstring *h_varname;
            duk_regconst_t reg_varbind;
            duk_regconst_t rc_varname;

            DUK_ASSERT(left->x1.t == DUK_ISPEC_VALUE);  /* LHS is already side effect free */

            h_varname = duk_known_hstring(thr, left->x1.valstack_idx);
            if (duk__hstring_is_eval_or_arguments_in_strict_mode(comp_ctx, h_varname)) {
                /* E5 Section 11.13.1 (and others for other assignments), step 4. */
                goto syntax_error_lvalue;
            }
            duk_dup(thr, left->x1.valstack_idx);
            (void) duk__lookup_lhs(comp_ctx, &reg_varbind, &rc_varname);

            if (args_op == DUK_OP_NONE) {
                duk__expr(comp_ctx, res, args_rbp /*rbp_flags*/);
                if (toplevel_assign) {
                    /* Any 'res' will do. */
                    DUK_DDD(DUK_DDDPRINT("plain assignment, toplevel assign, use as is"));
                } else {
                    /* 'res' must be a plain ivalue, and not register-bound variable. */
                    DUK_DDD(DUK_DDDPRINT("plain assignment, not toplevel assign, ensure not a reg-bound identifier"));
                    if (res->t != DUK_IVAL_PLAIN || (res->x1.t == DUK_ISPEC_REGCONST &&
                                                     DUK__ISREG_NOTTEMP(comp_ctx, res->x1.regconst))) {
                        duk__ivalue_totempconst(comp_ctx, res);
                    }
                }
            } else {
                /* For X <op>= Y we need to evaluate the pre-op
                 * value of X before evaluating the RHS: the RHS
                 * can change X, but when we do <op> we must use
                 * the pre-op value.
                 */
                duk_regconst_t reg_temp;

                reg_temp = DUK__ALLOCTEMP(comp_ctx);

                if (reg_varbind >= 0) {
                    duk_regconst_t reg_res;
                    duk_regconst_t reg_src;
                    duk_int_t pc_temp_load;
                    duk_int_t pc_before_rhs;
                    duk_int_t pc_after_rhs;

                    if (toplevel_assign) {
                        /* 'reg_varbind' is the operation result and can also
                         * become the expression value for top level assignments
                         * such as: "var x; x += y;".
                         */
                        DUK_DD(DUK_DDPRINT("<op>= expression is top level, write directly to reg_varbind"));
                        reg_res = reg_varbind;
                    } else {
                        /* Not safe to use 'reg_varbind' as assignment expression
                         * value, so go through a temp.
                         */
                        DUK_DD(DUK_DDPRINT("<op>= expression is not top level, write to reg_temp"));
                        reg_res = reg_temp;  /* reg_res should be smallest possible */
                        reg_temp = DUK__ALLOCTEMP(comp_ctx);
                    }

                    /* Try to optimize X <op>= Y for reg-bound
                     * variables.  Detect side-effect free RHS
                     * narrowly by seeing whether it emits code.
                     * If not, rewind the code emitter and overwrite
                     * the unnecessary temp reg load.
                     */

                    pc_temp_load = duk__get_current_pc(comp_ctx);
                    duk__emit_a_bc(comp_ctx,
                                   DUK_OP_LDREG,
                                   reg_temp,
                                   reg_varbind);

                    pc_before_rhs = duk__get_current_pc(comp_ctx);
                    duk__expr_toregconst(comp_ctx, res, args_rbp /*rbp_flags*/);
                    DUK_ASSERT(res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_REGCONST);
                    pc_after_rhs = duk__get_current_pc(comp_ctx);

                    DUK_DD(DUK_DDPRINT("pc_temp_load=%ld, pc_before_rhs=%ld, pc_after_rhs=%ld",
                                       (long) pc_temp_load, (long) pc_before_rhs,
                                       (long) pc_after_rhs));

                    if (pc_after_rhs == pc_before_rhs) {
                        /* Note: if the reg_temp load generated shuffling
                         * instructions, we may need to rewind more than
                         * one instruction, so use explicit PC computation.
                         */
                        DUK_DD(DUK_DDPRINT("rhs is side effect free, rewind and avoid unnecessary temp for reg-based <op>="));
                        DUK_BW_ADD_PTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code, (pc_temp_load - pc_before_rhs) * (duk_int_t) sizeof(duk_compiler_instr));
                        reg_src = reg_varbind;
                    } else {
                        DUK_DD(DUK_DDPRINT("rhs evaluation emitted code, not sure if rhs is side effect free; use temp reg for LHS"));
                        reg_src = reg_temp;
                    }

                    duk__emit_a_b_c(comp_ctx,
                                    args_op | DUK__EMIT_FLAG_BC_REGCONST,
                                    reg_res,
                                    reg_src,
                                    res->x1.regconst);

                    res->x1.regconst = reg_res;

                    /* Ensure compact use of temps. */
                    if (DUK__ISREG_TEMP(comp_ctx, reg_res)) {
                        DUK__SETTEMP(comp_ctx, reg_res + 1);
                    }
                } else {
                    /* When LHS is not register bound, always go through a
                     * temporary.  No optimization for top level assignment.
                     */

                    duk__emit_a_bc(comp_ctx,
                                   DUK_OP_GETVAR,
                                   reg_temp,
                                   rc_varname);

                    duk__expr_toregconst(comp_ctx, res, args_rbp /*rbp_flags*/);
                    DUK_ASSERT(res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_REGCONST);

                    duk__emit_a_b_c(comp_ctx,
                                    args_op | DUK__EMIT_FLAG_BC_REGCONST,
                                    reg_temp,
                                    reg_temp,
                                    res->x1.regconst);
                    res->x1.regconst = reg_temp;
                }

                DUK_ASSERT(res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_REGCONST);
            }

            /* At this point 'res' holds the potential expression value.
             * It can be basically any ivalue here, including a reg-bound
             * identifier (if code above deems it safe) or a unary/binary
             * operation.  Operations must be resolved to a side effect free
             * plain value, and the side effects must happen exactly once.
             */

            if (reg_varbind >= 0) {
                if (res->t != DUK_IVAL_PLAIN) {
                    /* Resolve 'res' directly into the LHS binding, and use
                     * that as the expression value if safe.  If not safe,
                     * resolve to a temp/const and copy to LHS.
                     */
                    if (toplevel_assign) {
                        duk__ivalue_toforcedreg(comp_ctx, res, (duk_int_t) reg_varbind);
                    } else {
                        duk__ivalue_totempconst(comp_ctx, res);
                        duk__copy_ivalue(comp_ctx, res, left);  /* use 'left' as a temp */
                        duk__ivalue_toforcedreg(comp_ctx, left, (duk_int_t) reg_varbind);
                    }
                } else {
                    /* Use 'res' as the expression value (it's side effect
                     * free and may be a plain value, a register, or a
                     * constant) and write it to the LHS binding too.
                     */
                    duk__copy_ivalue(comp_ctx, res, left);  /* use 'left' as a temp */
                    duk__ivalue_toforcedreg(comp_ctx, left, (duk_int_t) reg_varbind);
                }
            } else {
                /* Only a reg fits into 'A' so coerce 'res' into a register
                 * for PUTVAR.
                 *
                 * XXX: here the current A/B/C split is suboptimal: we could
                 * just use 9 bits for reg_res (and support constants) and 17
                 * instead of 18 bits for the varname const index.
                 */

                duk__ivalue_toreg(comp_ctx, res);
                duk__emit_a_bc(comp_ctx,
                               DUK_OP_PUTVAR | DUK__EMIT_FLAG_A_IS_SOURCE,
                               res->x1.regconst,
                               rc_varname);
            }

            /* 'res' contains expression value */
        } else if (left->t == DUK_IVAL_PROP) {
            /* E5 Section 11.13.1 (and others) step 4 never matches for prop writes -> no check */
            duk_regconst_t reg_obj;
            duk_regconst_t rc_key;
            duk_regconst_t rc_res;
            duk_regconst_t reg_temp;

            /* Property access expressions ('a[b]') are critical to correct
             * LHS evaluation ordering, see test-dev-assign-eval-order*.js.
             * We must make sure that the LHS target slot (base object and
             * key) don't change during RHS evaluation.  The only concrete
             * problem is a register reference to a variable-bound register
             * (i.e., non-temp).  Require temp regs for both key and base.
             *
             * Don't allow a constant for the object (even for a number
             * etc), as it goes into the 'A' field of the opcode.
             */

            reg_obj = duk__ispec_toregconst_raw(comp_ctx,
                                                &left->x1,
                                                -1 /*forced_reg*/,
                                                DUK__IVAL_FLAG_REQUIRE_TEMP /*flags*/);

            rc_key = duk__ispec_toregconst_raw(comp_ctx,
                                               &left->x2,
                                               -1 /*forced_reg*/,
                                               DUK__IVAL_FLAG_REQUIRE_TEMP | DUK__IVAL_FLAG_ALLOW_CONST /*flags*/);

            /* Evaluate RHS only when LHS is safe. */

            if (args_op == DUK_OP_NONE) {
                duk__expr_toregconst(comp_ctx, res, args_rbp /*rbp_flags*/);
                DUK_ASSERT(res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_REGCONST);
                rc_res = res->x1.regconst;
            } else {
                reg_temp = DUK__ALLOCTEMP(comp_ctx);
                duk__emit_a_b_c(comp_ctx,
                                DUK_OP_GETPROP | DUK__EMIT_FLAG_BC_REGCONST,
                                reg_temp,
                                reg_obj,
                                rc_key);

                duk__expr_toregconst(comp_ctx, res, args_rbp /*rbp_flags*/);
                DUK_ASSERT(res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_REGCONST);

                duk__emit_a_b_c(comp_ctx,
                                args_op | DUK__EMIT_FLAG_BC_REGCONST,
                                reg_temp,
                                reg_temp,
                                res->x1.regconst);
                rc_res = reg_temp;
            }

            duk__emit_a_b_c(comp_ctx,
                            DUK_OP_PUTPROP | DUK__EMIT_FLAG_A_IS_SOURCE | DUK__EMIT_FLAG_BC_REGCONST,
                            reg_obj,
                            rc_key,
                            rc_res);

            duk__ivalue_regconst(res, rc_res);
        } else {
            /* No support for lvalues returned from new or function call expressions.
             * However, these must NOT cause compile-time SyntaxErrors, but run-time
             * ReferenceErrors.  Both left and right sides of the assignment must be
             * evaluated before throwing a ReferenceError.  For instance:
             *
             *     f() = g();
             *
             * must result in f() being evaluated, then g() being evaluated, and
             * finally, a ReferenceError being thrown.  See E5 Section 11.13.1.
             */

            duk_regconst_t rc_res;

            /* First evaluate LHS fully to ensure all side effects are out. */
            duk__ivalue_toplain_ignore(comp_ctx, left);

            /* Then evaluate RHS fully (its value becomes the expression value too).
             * Technically we'd need the side effect safety check here too, but because
             * we always throw using INVLHS the result doesn't matter.
             */
            rc_res = duk__expr_toregconst(comp_ctx, res, args_rbp /*rbp_flags*/);

            duk__emit_op_only(comp_ctx, DUK_OP_INVLHS);

            duk__ivalue_regconst(res, rc_res);
        }

        return;
    }

 postincdec:
    {
        /*
         *  Post-increment/decrement will return the original value as its
         *  result value.  However, even that value will be coerced using
         *  ToNumber() which is quite awkward.  Specific bytecode opcodes
         *  are used to handle these semantics.
         *
         *  Note that post increment/decrement has a "no LineTerminator here"
         *  restriction.  This is handled by duk__expr_lbp(), which forcibly terminates
         *  the previous expression if a LineTerminator occurs before '++'/'--'.
         */

        duk_regconst_t reg_res;
        duk_small_uint_t args_op1 = (args >> 8) & 0xff;  /* DUK_OP_POSTINCR/DUK_OP_POSTDECR */
        duk_small_uint_t args_op2 = args >> 16;          /* DUK_OP_POSTINCP_RR/DUK_OP_POSTDECP_RR */

        /* Specific assumptions for opcode numbering. */
        DUK_ASSERT(DUK_OP_POSTINCR + 4 == DUK_OP_POSTINCV);
        DUK_ASSERT(DUK_OP_POSTDECR + 4 == DUK_OP_POSTDECV);

        reg_res = DUK__ALLOCTEMP(comp_ctx);

        if (left->t == DUK_IVAL_VAR) {
            duk_hstring *h_varname;
            duk_regconst_t reg_varbind;
            duk_regconst_t rc_varname;

            h_varname = duk_known_hstring(thr, left->x1.valstack_idx);

            if (duk__hstring_is_eval_or_arguments_in_strict_mode(comp_ctx, h_varname)) {
                goto syntax_error;
            }

            duk_dup(thr, left->x1.valstack_idx);
            if (duk__lookup_lhs(comp_ctx, &reg_varbind, &rc_varname)) {
                duk__emit_a_bc(comp_ctx,
                               args_op1,  /* e.g. DUK_OP_POSTINCR */
                               reg_res,
                               reg_varbind);
            } else {
                duk__emit_a_bc(comp_ctx,
                               args_op1 + 4,  /* e.g. DUK_OP_POSTINCV */
                               reg_res,
                               rc_varname);
            }

            DUK_DDD(DUK_DDDPRINT("postincdec to '%!O' -> reg_varbind=%ld, rc_varname=%ld",
                                 (duk_heaphdr *) h_varname, (long) reg_varbind, (long) rc_varname));
        } else if (left->t == DUK_IVAL_PROP) {
            duk_regconst_t reg_obj;  /* allocate to reg only (not const) */
            duk_regconst_t rc_key;

            reg_obj = duk__ispec_toregconst_raw(comp_ctx, &left->x1, -1 /*forced_reg*/, 0 /*flags*/);  /* don't allow const */
            rc_key = duk__ispec_toregconst_raw(comp_ctx, &left->x2, -1 /*forced_reg*/, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/);
            duk__emit_a_b_c(comp_ctx,
                            args_op2 | DUK__EMIT_FLAG_BC_REGCONST,  /* e.g. DUK_OP_POSTINCP */
                            reg_res,
                            reg_obj,
                            rc_key);
        } else {
            /* Technically return value is not needed because INVLHS will
             * unconditially throw a ReferenceError.  Coercion is necessary
             * for proper semantics (consider ToNumber() called for an object).
             * Use DUK_OP_UNP with a dummy register to get ToNumber().
             */
            duk__ivalue_toforcedreg(comp_ctx, left, reg_res);
            duk__emit_bc(comp_ctx,
                         DUK_OP_UNP,
                         reg_res);  /* for side effects, result ignored */
            duk__emit_op_only(comp_ctx,
                              DUK_OP_INVLHS);
        }

        DUK__SETTEMP(comp_ctx, reg_res + 1);
        duk__ivalue_regconst(res, reg_res);
        return;
    }

 syntax_error:
    DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_EXPRESSION);
    DUK_WO_NORETURN(return;);

 syntax_error_lvalue:
    DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_LVALUE);
    DUK_WO_NORETURN(return;);
}

DUK_LOCAL duk_small_uint_t duk__expr_lbp(duk_compiler_ctx *comp_ctx) {
    duk_small_uint_t tok = comp_ctx->curr_token.t;

    DUK_ASSERT_DISABLE(tok >= DUK_TOK_MINVAL);  /* unsigned */
    DUK_ASSERT(tok <= DUK_TOK_MAXVAL);
    DUK_ASSERT(sizeof(duk__token_lbp) == DUK_TOK_MAXVAL + 1);

    /* XXX: integrate support for this into led() instead?
     * Similar issue as post-increment/post-decrement.
     */

    /* prevent duk__expr_led() by using a binding power less than anything valid */
    if (tok == DUK_TOK_IN && !comp_ctx->curr_func.allow_in) {
        return 0;
    }

    if ((tok == DUK_TOK_DECREMENT || tok == DUK_TOK_INCREMENT) &&
        (comp_ctx->curr_token.lineterm)) {
        /* '++' or '--' in a post-increment/decrement position,
         * and a LineTerminator occurs between the operator and
         * the preceding expression.  Force the previous expr
         * to terminate, in effect treating e.g. "a,b\n++" as
         * "a,b;++" (= SyntaxError).
         */
        return 0;
    }

    return DUK__TOKEN_LBP_GET_BP(duk__token_lbp[tok]);  /* format is bit packed */
}

/*
 *  Expression parsing.
 *
 *  Upon entry to 'expr' and its variants, 'curr_tok' is assumed to be the
 *  first token of the expression.  Upon exit, 'curr_tok' will be the first
 *  token not part of the expression (e.g. semicolon terminating an expression
 *  statement).
 */

#define DUK__EXPR_RBP_MASK           0xff
#define DUK__EXPR_FLAG_REJECT_IN     (1 << 8)   /* reject 'in' token (used for for-in) */
#define DUK__EXPR_FLAG_ALLOW_EMPTY   (1 << 9)   /* allow empty expression */
#define DUK__EXPR_FLAG_REQUIRE_INIT  (1 << 10)  /* require initializer for var/const */

/* main expression parser function */
DUK_LOCAL void duk__expr(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) {
    duk_hthread *thr = comp_ctx->thr;
    duk_ivalue tmp_alloc;   /* 'res' is used for "left", and 'tmp' for "right" */
    duk_ivalue *tmp = &tmp_alloc;
    duk_small_uint_t rbp;

    DUK__RECURSION_INCREASE(comp_ctx, thr);

    duk_require_stack(thr, DUK__PARSE_EXPR_SLOTS);

    /* filter out flags from exprtop rbp_flags here to save space */
    rbp = rbp_flags & DUK__EXPR_RBP_MASK;

    DUK_DDD(DUK_DDDPRINT("duk__expr(), rbp_flags=%ld, rbp=%ld, allow_in=%ld, paren_level=%ld",
                         (long) rbp_flags, (long) rbp, (long) comp_ctx->curr_func.allow_in,
                         (long) comp_ctx->curr_func.paren_level));

    duk_memzero(&tmp_alloc, sizeof(tmp_alloc));
    tmp->x1.valstack_idx = duk_get_top(thr);
    tmp->x2.valstack_idx = tmp->x1.valstack_idx + 1;
    duk_push_undefined(thr);
    duk_push_undefined(thr);

    /* XXX: where to release temp regs in intermediate expressions?
     * e.g. 1+2+3 -> don't inflate temp register count when parsing this.
     * that particular expression temp regs can be forced here.
     */

    /* XXX: increase ctx->expr_tokens here for every consumed token
     * (this would be a nice statistic)?
     */

    if (comp_ctx->curr_token.t == DUK_TOK_SEMICOLON || comp_ctx->curr_token.t == DUK_TOK_RPAREN) {
        /* XXX: possibly incorrect handling of empty expression */
        DUK_DDD(DUK_DDDPRINT("empty expression"));
        if (!(rbp_flags & DUK__EXPR_FLAG_ALLOW_EMPTY)) {
            DUK_ERROR_SYNTAX(thr, DUK_STR_EMPTY_EXPR_NOT_ALLOWED);
            DUK_WO_NORETURN(return;);
        }
        duk_push_undefined(thr);
        duk__ivalue_plain_fromstack(comp_ctx, res);
        goto cleanup;
    }

    duk__advance(comp_ctx);
    duk__expr_nud(comp_ctx, res);  /* reuse 'res' as 'left' */
    while (rbp < duk__expr_lbp(comp_ctx)) {
        duk__advance(comp_ctx);
        duk__expr_led(comp_ctx, res, tmp);
        duk__copy_ivalue(comp_ctx, tmp, res);  /* tmp -> res */
    }

 cleanup:
    /* final result is already in 'res' */

    duk_pop_2(thr);

    DUK__RECURSION_DECREASE(comp_ctx, thr);
}

DUK_LOCAL void duk__exprtop(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) {
    duk_hthread *thr = comp_ctx->thr;

    /* Note: these variables must reside in 'curr_func' instead of the global
     * context: when parsing function expressions, expression parsing is nested.
     */
    comp_ctx->curr_func.nud_count = 0;
    comp_ctx->curr_func.led_count = 0;
    comp_ctx->curr_func.paren_level = 0;
    comp_ctx->curr_func.expr_lhs = 1;
    comp_ctx->curr_func.allow_in = (rbp_flags & DUK__EXPR_FLAG_REJECT_IN ? 0 : 1);

    duk__expr(comp_ctx, res, rbp_flags);

    if (!(rbp_flags & DUK__EXPR_FLAG_ALLOW_EMPTY) && duk__expr_is_empty(comp_ctx)) {
        DUK_ERROR_SYNTAX(thr, DUK_STR_EMPTY_EXPR_NOT_ALLOWED);
        DUK_WO_NORETURN(return;);
    }
}

/* A bunch of helpers (for size optimization) that combine duk__expr()/duk__exprtop()
 * and result conversions.
 *
 * Each helper needs at least 2-3 calls to make it worth while to wrap.
 */

#if 0  /* unused */
DUK_LOCAL duk_regconst_t duk__expr_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) {
    duk__expr(comp_ctx, res, rbp_flags);
    return duk__ivalue_toreg(comp_ctx, res);
}
#endif

#if 0  /* unused */
DUK_LOCAL duk_regconst_t duk__expr_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) {
    duk__expr(comp_ctx, res, rbp_flags);
    return duk__ivalue_totemp(comp_ctx, res);
}
#endif

DUK_LOCAL void duk__expr_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags, duk_regconst_t forced_reg) {
    DUK_ASSERT(forced_reg >= 0);
    duk__expr(comp_ctx, res, rbp_flags);
    duk__ivalue_toforcedreg(comp_ctx, res, forced_reg);
}

DUK_LOCAL duk_regconst_t duk__expr_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) {
    duk__expr(comp_ctx, res, rbp_flags);
    return duk__ivalue_toregconst(comp_ctx, res);
}

#if 0  /* unused */
DUK_LOCAL duk_regconst_t duk__expr_totempconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) {
    duk__expr(comp_ctx, res, rbp_flags);
    return duk__ivalue_totempconst(comp_ctx, res);
}
#endif

DUK_LOCAL void duk__expr_toplain(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) {
    duk__expr(comp_ctx, res, rbp_flags);
    duk__ivalue_toplain(comp_ctx, res);
}

DUK_LOCAL void duk__expr_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) {
    duk__expr(comp_ctx, res, rbp_flags);
    duk__ivalue_toplain_ignore(comp_ctx, res);
}

DUK_LOCAL duk_regconst_t duk__exprtop_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) {
    duk__exprtop(comp_ctx, res, rbp_flags);
    return duk__ivalue_toreg(comp_ctx, res);
}

#if 0  /* unused */
DUK_LOCAL duk_regconst_t duk__exprtop_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) {
    duk__exprtop(comp_ctx, res, rbp_flags);
    return duk__ivalue_totemp(comp_ctx, res);
}
#endif

DUK_LOCAL void duk__exprtop_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags, duk_regconst_t forced_reg) {
    DUK_ASSERT(forced_reg >= 0);
    duk__exprtop(comp_ctx, res, rbp_flags);
    duk__ivalue_toforcedreg(comp_ctx, res, forced_reg);
}

DUK_LOCAL duk_regconst_t duk__exprtop_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) {
    duk__exprtop(comp_ctx, res, rbp_flags);
    return duk__ivalue_toregconst(comp_ctx, res);
}

#if 0  /* unused */
DUK_LOCAL void duk__exprtop_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *res, int rbp_flags) {
    duk__exprtop(comp_ctx, res, rbp_flags);
    duk__ivalue_toplain_ignore(comp_ctx, res);
}
#endif

/*
 *  Parse an individual source element (top level statement) or a statement.
 *
 *  Handles labeled statements automatically (peeling away labels before
 *  parsing an expression that follows the label(s)).
 *
 *  Upon entry, 'curr_tok' contains the first token of the statement (parsed
 *  in "allow regexp literal" mode).  Upon exit, 'curr_tok' contains the first
 *  token following the statement (if the statement has a terminator, this is
 *  the token after the terminator).
 */

#define DUK__HAS_VAL                  (1 << 0)  /* stmt has non-empty value */
#define DUK__HAS_TERM                 (1 << 1)  /* stmt has explicit/implicit semicolon terminator */
#define DUK__ALLOW_AUTO_SEMI_ALWAYS   (1 << 2)  /* allow automatic semicolon even without lineterm (compatibility) */
#define DUK__STILL_PROLOGUE           (1 << 3)  /* statement does not terminate directive prologue */
#define DUK__IS_TERMINAL              (1 << 4)  /* statement is guaranteed to be terminal (control doesn't flow to next statement) */

/* Parse a single variable declaration (e.g. "i" or "i=10").  A leading 'var'
 * has already been eaten.  These is no return value in 'res', it is used only
 * as a temporary.
 *
 * When called from 'for-in' statement parser, the initializer expression must
 * not allow the 'in' token.  The caller supply additional expression parsing
 * flags (like DUK__EXPR_FLAG_REJECT_IN) in 'expr_flags'.
 *
 * Finally, out_rc_varname and out_reg_varbind are updated to reflect where
 * the identifier is bound:
 *
 *    If register bound:      out_reg_varbind >= 0, out_rc_varname == 0 (ignore)
 *    If not register bound:  out_reg_varbind < 0, out_rc_varname >= 0
 *
 * These allow the caller to use the variable for further assignment, e.g.
 * as is done in 'for-in' parsing.
 */

DUK_LOCAL void duk__parse_var_decl(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t expr_flags, duk_regconst_t *out_reg_varbind, duk_regconst_t *out_rc_varname) {
    duk_hthread *thr = comp_ctx->thr;
    duk_hstring *h_varname;
    duk_regconst_t reg_varbind;
    duk_regconst_t rc_varname;

    /* assume 'var' has been eaten */

    /* Note: Identifier rejects reserved words */
    if (comp_ctx->curr_token.t != DUK_TOK_IDENTIFIER) {
        goto syntax_error;
    }
    h_varname = comp_ctx->curr_token.str1;

    DUK_ASSERT(h_varname != NULL);

    /* strict mode restrictions (E5 Section 12.2.1) */
    if (duk__hstring_is_eval_or_arguments_in_strict_mode(comp_ctx, h_varname)) {
        goto syntax_error;
    }

    /* register declarations in first pass */
    if (comp_ctx->curr_func.in_scanning) {
        duk_uarridx_t n;
        DUK_DDD(DUK_DDDPRINT("register variable declaration %!O in pass 1",
                             (duk_heaphdr *) h_varname));
        n = (duk_uarridx_t) duk_get_length(thr, comp_ctx->curr_func.decls_idx);
        duk_push_hstring(thr, h_varname);
        duk_put_prop_index(thr, comp_ctx->curr_func.decls_idx, n);
        duk_push_int(thr, DUK_DECL_TYPE_VAR + (0 << 8));
        duk_put_prop_index(thr, comp_ctx->curr_func.decls_idx, n + 1);
    }

    duk_push_hstring(thr, h_varname);  /* push before advancing to keep reachable */

    /* register binding lookup is based on varmap (even in first pass) */
    duk_dup_top(thr);
    (void) duk__lookup_lhs(comp_ctx, &reg_varbind, &rc_varname);

    duk__advance(comp_ctx);  /* eat identifier */

    if (comp_ctx->curr_token.t == DUK_TOK_EQUALSIGN) {
        duk__advance(comp_ctx);

        DUK_DDD(DUK_DDDPRINT("vardecl, assign to '%!O' -> reg_varbind=%ld, rc_varname=%ld",
                             (duk_heaphdr *) h_varname, (long) reg_varbind, (long) rc_varname));

        duk__exprtop(comp_ctx, res, DUK__BP_COMMA | expr_flags /*rbp_flags*/);  /* AssignmentExpression */

        if (reg_varbind >= 0) {
            duk__ivalue_toforcedreg(comp_ctx, res, reg_varbind);
        } else {
            duk_regconst_t reg_val;
            reg_val = duk__ivalue_toreg(comp_ctx, res);
            duk__emit_a_bc(comp_ctx,
                           DUK_OP_PUTVAR | DUK__EMIT_FLAG_A_IS_SOURCE,
                           reg_val,
                           rc_varname);
        }
    } else {
        if (expr_flags & DUK__EXPR_FLAG_REQUIRE_INIT) {
            /* Used for minimal 'const': initializer required. */
            goto syntax_error;
        }
    }

    duk_pop(thr);  /* pop varname */

    *out_rc_varname = rc_varname;
    *out_reg_varbind = reg_varbind;

    return;

 syntax_error:
    DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_VAR_DECLARATION);
    DUK_WO_NORETURN(return;);
}

DUK_LOCAL void duk__parse_var_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t expr_flags) {
    duk_regconst_t reg_varbind;
    duk_regconst_t rc_varname;

    duk__advance(comp_ctx);  /* eat 'var' */

    for (;;) {
        /* rc_varname and reg_varbind are ignored here */
        duk__parse_var_decl(comp_ctx, res, 0 | expr_flags, &reg_varbind, &rc_varname);

        if (comp_ctx->curr_token.t != DUK_TOK_COMMA) {
            break;
        }
        duk__advance(comp_ctx);
    }
}

DUK_LOCAL void duk__parse_for_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site) {
    duk_hthread *thr = comp_ctx->thr;
    duk_int_t pc_v34_lhs;         /* start variant 3/4 left-hand-side code (L1 in doc/compiler.rst example) */
    duk_regconst_t temp_reset;    /* knock back "next temp" to this whenever possible */
    duk_regconst_t reg_temps;     /* preallocated temporaries (2) for variants 3 and 4 */

    DUK_DDD(DUK_DDDPRINT("start parsing a for/for-in statement"));

    /* Two temporaries are preallocated here for variants 3 and 4 which need
     * registers which are never clobbered by expressions in the loop
     * (concretely: for the enumerator object and the next enumerated value).
     * Variants 1 and 2 "release" these temps.
     */

    reg_temps = DUK__ALLOCTEMPS(comp_ctx, 2);

    temp_reset = DUK__GETTEMP(comp_ctx);

    /*
     *  For/for-in main variants are:
     *
     *    1. for (ExpressionNoIn_opt; Expression_opt; Expression_opt) Statement
     *    2. for (var VariableDeclarationNoIn; Expression_opt; Expression_opt) Statement
     *    3. for (LeftHandSideExpression in Expression) Statement
     *    4. for (var VariableDeclarationNoIn in Expression) Statement
     *
     *  Parsing these without arbitrary lookahead or backtracking is relatively
     *  tricky but we manage to do so for now.
     *
     *  See doc/compiler.rst for a detailed discussion of control flow
     *  issues, evaluation order issues, etc.
     */

    duk__advance(comp_ctx);  /* eat 'for' */
    duk__advance_expect(comp_ctx, DUK_TOK_LPAREN);

    DUK_DDD(DUK_DDDPRINT("detecting for/for-in loop variant, pc=%ld", (long) duk__get_current_pc(comp_ctx)));

    /* a label site has been emitted by duk__parse_stmt() automatically
     * (it will also emit the ENDLABEL).
     */

    if (comp_ctx->curr_token.t == DUK_TOK_VAR) {
        /*
         *  Variant 2 or 4
         */

        duk_regconst_t reg_varbind;  /* variable binding register if register-bound (otherwise < 0) */
        duk_regconst_t rc_varname;   /* variable name reg/const, if variable not register-bound */

        duk__advance(comp_ctx);  /* eat 'var' */
        duk__parse_var_decl(comp_ctx, res, DUK__EXPR_FLAG_REJECT_IN, &reg_varbind, &rc_varname);
        DUK__SETTEMP(comp_ctx, temp_reset);

        if (comp_ctx->curr_token.t == DUK_TOK_IN) {
            /*
             *  Variant 4
             */

            DUK_DDD(DUK_DDDPRINT("detected for variant 4: for (var VariableDeclarationNoIn in Expression) Statement"));
            pc_v34_lhs = duk__get_current_pc(comp_ctx);  /* jump is inserted here */
            if (reg_varbind >= 0) {
                duk__emit_a_bc(comp_ctx,
                               DUK_OP_LDREG,
                               reg_varbind,
                               reg_temps + 0);
            } else {
                duk__emit_a_bc(comp_ctx,
                               DUK_OP_PUTVAR | DUK__EMIT_FLAG_A_IS_SOURCE,
                               reg_temps + 0,
                               rc_varname);
            }
            goto parse_3_or_4;
        } else {
            /*
             *  Variant 2
             */

            DUK_DDD(DUK_DDDPRINT("detected for variant 2: for (var VariableDeclarationNoIn; Expression_opt; Expression_opt) Statement"));
            for (;;) {
                /* more initializers */
                if (comp_ctx->curr_token.t != DUK_TOK_COMMA) {
                    break;
                }
                DUK_DDD(DUK_DDDPRINT("variant 2 has another variable initializer"));

                duk__advance(comp_ctx);  /* eat comma */
                duk__parse_var_decl(comp_ctx, res, DUK__EXPR_FLAG_REJECT_IN, &reg_varbind, &rc_varname);
            }
            goto parse_1_or_2;
        }
    } else {
        /*
         *  Variant 1 or 3
         */

        pc_v34_lhs = duk__get_current_pc(comp_ctx);  /* jump is inserted here (variant 3) */

        /* Note that duk__exprtop() here can clobber any reg above current temp_next,
         * so any loop variables (e.g. enumerator) must be "preallocated".
         */

        /* don't coerce yet to a plain value (variant 3 needs special handling) */
        duk__exprtop(comp_ctx, res, DUK__BP_FOR_EXPR | DUK__EXPR_FLAG_REJECT_IN | DUK__EXPR_FLAG_ALLOW_EMPTY /*rbp_flags*/);  /* Expression */
        if (comp_ctx->curr_token.t == DUK_TOK_IN) {
            /*
             *  Variant 3
             */

            /* XXX: need to determine LHS type, and check that it is LHS compatible */
            DUK_DDD(DUK_DDDPRINT("detected for variant 3: for (LeftHandSideExpression in Expression) Statement"));
            if (duk__expr_is_empty(comp_ctx)) {
                goto syntax_error;  /* LeftHandSideExpression does not allow empty expression */
            }

            if (res->t == DUK_IVAL_VAR) {
                duk_regconst_t reg_varbind;
                duk_regconst_t rc_varname;

                duk_dup(thr, res->x1.valstack_idx);
                if (duk__lookup_lhs(comp_ctx, &reg_varbind, &rc_varname)) {
                    duk__emit_a_bc(comp_ctx,
                                   DUK_OP_LDREG,
                                   reg_varbind,
                                   reg_temps + 0);
                } else {
                    duk__emit_a_bc(comp_ctx,
                                   DUK_OP_PUTVAR | DUK__EMIT_FLAG_A_IS_SOURCE,
                                   reg_temps + 0,
                                   rc_varname);
                }
            } else if (res->t == DUK_IVAL_PROP) {
                /* Don't allow a constant for the object (even for a number etc), as
                 * it goes into the 'A' field of the opcode.
                 */
                duk_regconst_t reg_obj;
                duk_regconst_t rc_key;
                reg_obj = duk__ispec_toregconst_raw(comp_ctx, &res->x1, -1 /*forced_reg*/, 0 /*flags*/);  /* don't allow const */
                rc_key = duk__ispec_toregconst_raw(comp_ctx, &res->x2, -1 /*forced_reg*/, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/);
                duk__emit_a_b_c(comp_ctx,
                                DUK_OP_PUTPROP | DUK__EMIT_FLAG_A_IS_SOURCE | DUK__EMIT_FLAG_BC_REGCONST,
                                reg_obj,
                                rc_key,
                                reg_temps + 0);
            } else {
                duk__ivalue_toplain_ignore(comp_ctx, res);  /* just in case */
                duk__emit_op_only(comp_ctx,
                                  DUK_OP_INVLHS);
            }
            goto parse_3_or_4;
        } else {
            /*
             *  Variant 1
             */

            DUK_DDD(DUK_DDDPRINT("detected for variant 1: for (ExpressionNoIn_opt; Expression_opt; Expression_opt) Statement"));
            duk__ivalue_toplain_ignore(comp_ctx, res);
            goto parse_1_or_2;
        }
    }

 parse_1_or_2:
    /*
     *  Parse variant 1 or 2.  The first part expression (which differs
     *  in the variants) has already been parsed and its code emitted.
     *
     *  reg_temps + 0: unused
     *  reg_temps + 1: unused
     */
    {
        duk_regconst_t rc_cond;
        duk_int_t pc_l1, pc_l2, pc_l3, pc_l4;
        duk_int_t pc_jumpto_l3, pc_jumpto_l4;
        duk_bool_t expr_c_empty;

        DUK_DDD(DUK_DDDPRINT("shared code for parsing variants 1 and 2"));

        /* "release" preallocated temps since we won't need them */
        temp_reset = reg_temps + 0;
        DUK__SETTEMP(comp_ctx, temp_reset);

        duk__advance_expect(comp_ctx, DUK_TOK_SEMICOLON);

        pc_l1 = duk__get_current_pc(comp_ctx);
        duk__exprtop(comp_ctx, res, DUK__BP_FOR_EXPR | DUK__EXPR_FLAG_ALLOW_EMPTY /*rbp_flags*/);  /* Expression_opt */
        if (duk__expr_is_empty(comp_ctx)) {
            /* no need to coerce */
            pc_jumpto_l3 = duk__emit_jump_empty(comp_ctx);  /* to body */
            pc_jumpto_l4 = -1;  /* omitted */
        } else {
            rc_cond = duk__ivalue_toregconst(comp_ctx, res);
            duk__emit_if_false_skip(comp_ctx, rc_cond);
            pc_jumpto_l3 = duk__emit_jump_empty(comp_ctx);  /* to body */
            pc_jumpto_l4 = duk__emit_jump_empty(comp_ctx);  /* to exit */
        }
        DUK__SETTEMP(comp_ctx, temp_reset);

        duk__advance_expect(comp_ctx, DUK_TOK_SEMICOLON);

        pc_l2 = duk__get_current_pc(comp_ctx);
        duk__exprtop(comp_ctx, res, DUK__BP_FOR_EXPR | DUK__EXPR_FLAG_ALLOW_EMPTY /*rbp_flags*/);  /* Expression_opt */
        if (duk__expr_is_empty(comp_ctx)) {
            /* no need to coerce */
            expr_c_empty = 1;
            /* JUMP L1 omitted */
        } else {
            duk__ivalue_toplain_ignore(comp_ctx, res);
            expr_c_empty = 0;
            duk__emit_jump(comp_ctx, pc_l1);
        }
        DUK__SETTEMP(comp_ctx, temp_reset);

        comp_ctx->curr_func.allow_regexp_in_adv = 1;
        duk__advance_expect(comp_ctx, DUK_TOK_RPAREN);  /* Allow RegExp as part of next stmt. */

        pc_l3 = duk__get_current_pc(comp_ctx);
        duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/);
        if (expr_c_empty) {
            duk__emit_jump(comp_ctx, pc_l1);
        } else {
            duk__emit_jump(comp_ctx, pc_l2);
        }
        /* temp reset is not necessary after duk__parse_stmt(), which already does it */

        pc_l4 = duk__get_current_pc(comp_ctx);

        DUK_DDD(DUK_DDDPRINT("patching jumps: jumpto_l3: %ld->%ld, jumpto_l4: %ld->%ld, "
                             "break: %ld->%ld, continue: %ld->%ld",
                         (long) pc_jumpto_l3, (long) pc_l3, (long) pc_jumpto_l4, (long) pc_l4,
                             (long) (pc_label_site + 1), (long) pc_l4, (long) (pc_label_site + 2), (long) pc_l2));

        duk__patch_jump(comp_ctx, pc_jumpto_l3, pc_l3);
        duk__patch_jump(comp_ctx, pc_jumpto_l4, pc_l4);
        duk__patch_jump(comp_ctx,
                        pc_label_site + 1,
                        pc_l4);                         /* break jump */
        duk__patch_jump(comp_ctx,
                        pc_label_site + 2,
                        expr_c_empty ? pc_l1 : pc_l2);  /* continue jump */
    }
    goto finished;

 parse_3_or_4:
    /*
     *  Parse variant 3 or 4.
     *
     *  For variant 3 (e.g. "for (A in C) D;") the code for A (except the
     *  final property/variable write) has already been emitted.  The first
     *  instruction of that code is at pc_v34_lhs; a JUMP needs to be inserted
     *  there to satisfy control flow needs.
     *
     *  For variant 4, if the variable declaration had an initializer
     *  (e.g. "for (var A = B in C) D;") the code for the assignment
     *  (B) has already been emitted.
     *
     *  Variables set before entering here:
     *
     *    pc_v34_lhs:    insert a "JUMP L2" here (see doc/compiler.rst example).
     *    reg_temps + 0: iteration target value (written to LHS)
     *    reg_temps + 1: enumerator object
     */
    {
        duk_int_t pc_l1, pc_l2, pc_l3, pc_l4, pc_l5;
        duk_int_t pc_jumpto_l2, pc_jumpto_l3, pc_jumpto_l4, pc_jumpto_l5;
        duk_regconst_t reg_target;

        DUK_DDD(DUK_DDDPRINT("shared code for parsing variants 3 and 4, pc_v34_lhs=%ld", (long) pc_v34_lhs));

        DUK__SETTEMP(comp_ctx, temp_reset);

        /* First we need to insert a jump in the middle of previously
         * emitted code to get the control flow right.  No jumps can
         * cross the position where the jump is inserted.  See doc/compiler.rst
         * for discussion on the intricacies of control flow and side effects
         * for variants 3 and 4.
         */

        duk__insert_jump_entry(comp_ctx, pc_v34_lhs);
        pc_jumpto_l2 = pc_v34_lhs;  /* inserted jump */
        pc_l1 = pc_v34_lhs + 1;     /* +1, right after inserted jump */

        /* The code for writing reg_temps + 0 to the left hand side has already
         * been emitted.
         */

        pc_jumpto_l3 = duk__emit_jump_empty(comp_ctx);  /* -> loop body */

        duk__advance(comp_ctx);  /* eat 'in' */

        /* Parse enumeration target and initialize enumerator.  For 'null' and 'undefined',
         * INITENUM will creates a 'null' enumerator which works like an empty enumerator
         * (E5 Section 12.6.4, step 3).  Note that INITENUM requires the value to be in a
         * register (constant not allowed).
         */

        pc_l2 = duk__get_current_pc(comp_ctx);
        reg_target = duk__exprtop_toreg(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/);  /* Expression */
        duk__emit_b_c(comp_ctx,
                      DUK_OP_INITENUM | DUK__EMIT_FLAG_B_IS_TARGET,
                      reg_temps + 1,
                      reg_target);
        pc_jumpto_l4 = duk__emit_jump_empty(comp_ctx);
        DUK__SETTEMP(comp_ctx, temp_reset);

        comp_ctx->curr_func.allow_regexp_in_adv = 1;
        duk__advance_expect(comp_ctx, DUK_TOK_RPAREN);  /* Allow RegExp as part of next stmt. */

        pc_l3 = duk__get_current_pc(comp_ctx);
        duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/);
        /* temp reset is not necessary after duk__parse_stmt(), which already does it */

        /* NEXTENUM needs a jump slot right after the main opcode.
         * We need the code emitter to reserve the slot: if there's
         * target shuffling, the target shuffle opcodes must happen
         * after the jump slot (for NEXTENUM the shuffle opcodes are
         * not needed if the enum is finished).
         */
        pc_l4 = duk__get_current_pc(comp_ctx);
        duk__emit_b_c(comp_ctx,
                      DUK_OP_NEXTENUM | DUK__EMIT_FLAG_B_IS_TARGET | DUK__EMIT_FLAG_RESERVE_JUMPSLOT,
                      reg_temps + 0,
                      reg_temps + 1);
        pc_jumpto_l5 = comp_ctx->emit_jumpslot_pc;  /* NEXTENUM jump slot: executed when enum finished */
        duk__emit_jump(comp_ctx, pc_l1);  /* jump to next loop, using reg_v34_iter as iterated value */

        pc_l5 = duk__get_current_pc(comp_ctx);

        /* XXX: since the enumerator may be a memory expensive object,
         * perhaps clear it explicitly here?  If so, break jump must
         * go through this clearing operation.
         */

        DUK_DDD(DUK_DDDPRINT("patching jumps: jumpto_l2: %ld->%ld, jumpto_l3: %ld->%ld, "
                             "jumpto_l4: %ld->%ld, jumpto_l5: %ld->%ld, "
                             "break: %ld->%ld, continue: %ld->%ld",
                         (long) pc_jumpto_l2, (long) pc_l2, (long) pc_jumpto_l3, (long) pc_l3,
                         (long) pc_jumpto_l4, (long) pc_l4, (long) pc_jumpto_l5, (long) pc_l5,
                             (long) (pc_label_site + 1), (long) pc_l5, (long) (pc_label_site + 2), (long) pc_l4));

        duk__patch_jump(comp_ctx, pc_jumpto_l2, pc_l2);
        duk__patch_jump(comp_ctx, pc_jumpto_l3, pc_l3);
        duk__patch_jump(comp_ctx, pc_jumpto_l4, pc_l4);
        duk__patch_jump(comp_ctx, pc_jumpto_l5, pc_l5);
        duk__patch_jump(comp_ctx, pc_label_site + 1, pc_l5);  /* break jump */
        duk__patch_jump(comp_ctx, pc_label_site + 2, pc_l4);  /* continue jump */
    }
    goto finished;

 finished:
    DUK_DDD(DUK_DDDPRINT("end parsing a for/for-in statement"));
    return;

 syntax_error:
    DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_FOR);
    DUK_WO_NORETURN(return;);
}

DUK_LOCAL void duk__parse_switch_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site) {
    duk_hthread *thr = comp_ctx->thr;
    duk_regconst_t temp_at_loop;
    duk_regconst_t rc_switch;    /* reg/const for switch value */
    duk_regconst_t rc_case;      /* reg/const for case value */
    duk_regconst_t reg_temp;     /* general temp register */
    duk_int_t pc_prevcase = -1;
    duk_int_t pc_prevstmt = -1;
    duk_int_t pc_default = -1;   /* -1 == not set, -2 == pending (next statement list) */

    /* Note: negative pc values are ignored when patching jumps, so no explicit checks needed */

    /*
     *  Switch is pretty complicated because of several conflicting concerns:
     *
     *    - Want to generate code without an intermediate representation,
     *      i.e., in one go
     *
     *    - Case selectors are expressions, not values, and may thus e.g. throw
     *      exceptions (which causes evaluation order concerns)
     *
     *    - Evaluation semantics of case selectors and default clause need to be
     *      carefully implemented to provide correct behavior even with case value
     *      side effects
     *
     *    - Fall through case and default clauses; avoiding dead JUMPs if case
     *      ends with an unconditional jump (a break or a continue)
     *
     *    - The same case value may occur multiple times, but evaluation rules
     *      only process the first match before switching to a "propagation" mode
     *      where case values are no longer evaluated
     *
     *  See E5 Section 12.11.  Also see doc/compiler.rst for compilation
     *  discussion.
     */

    duk__advance(comp_ctx);
    duk__advance_expect(comp_ctx, DUK_TOK_LPAREN);
    rc_switch = duk__exprtop_toregconst(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/);
    duk__advance_expect(comp_ctx, DUK_TOK_RPAREN);  /* RegExp mode does not matter. */
    duk__advance_expect(comp_ctx, DUK_TOK_LCURLY);

    DUK_DDD(DUK_DDDPRINT("switch value in register %ld", (long) rc_switch));

    temp_at_loop = DUK__GETTEMP(comp_ctx);

    for (;;) {
        duk_int_t num_stmts;
        duk_small_uint_t tok;

        /* sufficient for keeping temp reg numbers in check */
        DUK__SETTEMP(comp_ctx, temp_at_loop);

        if (comp_ctx->curr_token.t == DUK_TOK_RCURLY) {
            break;
        }

        /*
         *  Parse a case or default clause.
         */

        if (comp_ctx->curr_token.t == DUK_TOK_CASE) {
            /*
             *  Case clause.
             *
             *  Note: cannot use reg_case as a temp register (for SEQ target)
             *  because it may be a constant.
             */

            duk__patch_jump_here(comp_ctx, pc_prevcase);  /* chain jumps for case
                                                           * evaluation and checking
                                                           */

            duk__advance(comp_ctx);
            rc_case = duk__exprtop_toregconst(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/);
            duk__advance_expect(comp_ctx, DUK_TOK_COLON);

            reg_temp = DUK__ALLOCTEMP(comp_ctx);
            duk__emit_a_b_c(comp_ctx,
                            DUK_OP_SEQ | DUK__EMIT_FLAG_BC_REGCONST,
                            reg_temp,
                            rc_switch,
                            rc_case);
            duk__emit_if_true_skip(comp_ctx, reg_temp);

            /* jump to next case clause */
            pc_prevcase = duk__emit_jump_empty(comp_ctx);  /* no match, next case */

            /* statements go here (if any) on next loop */
        } else if (comp_ctx->curr_token.t == DUK_TOK_DEFAULT) {
            /*
             *  Default clause.
             */

            if (pc_default >= 0) {
                goto syntax_error;
            }
            duk__advance(comp_ctx);
            duk__advance_expect(comp_ctx, DUK_TOK_COLON);

            /* Fix for https://github.com/svaarala/duktape/issues/155:
             * If 'default' is first clause (detected by pc_prevcase < 0)
             * we need to ensure we stay in the matching chain.
             */
            if (pc_prevcase < 0) {
                DUK_DD(DUK_DDPRINT("default clause is first, emit prevcase jump"));
                pc_prevcase = duk__emit_jump_empty(comp_ctx);
            }

            /* default clause matches next statement list (if any) */
            pc_default = -2;
        } else {
            /* Code is not accepted before the first case/default clause */
            goto syntax_error;
        }

        /*
         *  Parse code after the clause.  Possible terminators are
         *  'case', 'default', and '}'.
         *
         *  Note that there may be no code at all, not even an empty statement,
         *  between case clauses.  This must be handled just like an empty statement
         *  (omitting seemingly pointless JUMPs), to avoid situations like
         *  test-bug-case-fallthrough.js.
         */

        num_stmts = 0;
        if (pc_default == -2) {
            pc_default = duk__get_current_pc(comp_ctx);
        }

        /* Note: this is correct even for default clause statements:
         * they participate in 'fall-through' behavior even if the
         * default clause is in the middle.
         */
        duk__patch_jump_here(comp_ctx, pc_prevstmt);  /* chain jumps for 'fall-through'
                                                       * after a case matches.
                                                       */

        for (;;) {
            tok = comp_ctx->curr_token.t;
            if (tok == DUK_TOK_CASE || tok == DUK_TOK_DEFAULT ||
                tok == DUK_TOK_RCURLY) {
                break;
            }
            num_stmts++;
            duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/);
        }

        /* fall-through jump to next code of next case (backpatched) */
        pc_prevstmt = duk__emit_jump_empty(comp_ctx);

        /* XXX: would be nice to omit this jump when the jump is not
         * reachable, at least in the obvious cases (such as the case
         * ending with a 'break'.
         *
         * Perhaps duk__parse_stmt() could provide some info on whether
         * the statement is a "dead end"?
         *
         * If implemented, just set pc_prevstmt to -1 when not needed.
         */
    }

    DUK_ASSERT(comp_ctx->curr_token.t == DUK_TOK_RCURLY);
    comp_ctx->curr_func.allow_regexp_in_adv = 1;
    duk__advance(comp_ctx);  /* Allow RegExp as part of next stmt. */

    /* default case control flow patchup; note that if pc_prevcase < 0
     * (i.e. no case clauses), control enters default case automatically.
     */
    if (pc_default >= 0) {
        /* default case exists: go there if no case matches */
        duk__patch_jump(comp_ctx, pc_prevcase, pc_default);
    } else {
        /* default case does not exist, or no statements present
         * after default case: finish case evaluation
         */
        duk__patch_jump_here(comp_ctx, pc_prevcase);
    }

    /* fall-through control flow patchup; note that pc_prevstmt may be
     * < 0 (i.e. no case clauses), in which case this is a no-op.
     */
    duk__patch_jump_here(comp_ctx, pc_prevstmt);

    /* continue jump not patched, an INVALID opcode remains there */
    duk__patch_jump_here(comp_ctx, pc_label_site + 1);  /* break jump */

    /* Note: 'fast' breaks will jump to pc_label_site + 1, which will
     * then jump here.  The double jump will be eliminated by a
     * peephole pass, resulting in an optimal jump here.  The label
     * site jumps will remain in bytecode and will waste code size.
     */

    return;

 syntax_error:
    DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_SWITCH);
    DUK_WO_NORETURN(return;);
}

DUK_LOCAL void duk__parse_if_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
    duk_regconst_t temp_reset;
    duk_regconst_t rc_cond;
    duk_int_t pc_jump_false;

    DUK_DDD(DUK_DDDPRINT("begin parsing if statement"));

    temp_reset = DUK__GETTEMP(comp_ctx);

    duk__advance(comp_ctx);  /* eat 'if' */
    duk__advance_expect(comp_ctx, DUK_TOK_LPAREN);

    rc_cond = duk__exprtop_toregconst(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/);
    duk__emit_if_true_skip(comp_ctx, rc_cond);
    pc_jump_false = duk__emit_jump_empty(comp_ctx);  /* jump to end or else part */
    DUK__SETTEMP(comp_ctx, temp_reset);

    comp_ctx->curr_func.allow_regexp_in_adv = 1;
    duk__advance_expect(comp_ctx, DUK_TOK_RPAREN);  /* Allow RegExp as part of next stmt. */

    duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/);

    /* The 'else' ambiguity is resolved by 'else' binding to the innermost
     * construct, so greedy matching is correct here.
     */

    if (comp_ctx->curr_token.t == DUK_TOK_ELSE) {
        duk_int_t pc_jump_end;

        DUK_DDD(DUK_DDDPRINT("if has else part"));

        duk__advance(comp_ctx);

        pc_jump_end = duk__emit_jump_empty(comp_ctx);  /* jump from true part to end */
        duk__patch_jump_here(comp_ctx, pc_jump_false);

        duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/);

        duk__patch_jump_here(comp_ctx, pc_jump_end);
    } else {
        DUK_DDD(DUK_DDDPRINT("if does not have else part"));

        duk__patch_jump_here(comp_ctx, pc_jump_false);
    }

    DUK_DDD(DUK_DDDPRINT("end parsing if statement"));
}

DUK_LOCAL void duk__parse_do_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site) {
    duk_regconst_t rc_cond;
    duk_int_t pc_start;

    DUK_DDD(DUK_DDDPRINT("begin parsing do statement"));

    duk__advance(comp_ctx);  /* Eat 'do'; allow RegExp as part of next stmt. */

    pc_start = duk__get_current_pc(comp_ctx);
    duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/);
    duk__patch_jump_here(comp_ctx, pc_label_site + 2);  /* continue jump */

    duk__advance_expect(comp_ctx, DUK_TOK_WHILE);
    duk__advance_expect(comp_ctx, DUK_TOK_LPAREN);

    rc_cond = duk__exprtop_toregconst(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/);
    duk__emit_if_false_skip(comp_ctx, rc_cond);
    duk__emit_jump(comp_ctx, pc_start);
    /* no need to reset temps, as we're finished emitting code */

    comp_ctx->curr_func.allow_regexp_in_adv = 1;  /* Allow RegExp as part of next stmt. */
    duk__advance_expect(comp_ctx, DUK_TOK_RPAREN);

    duk__patch_jump_here(comp_ctx, pc_label_site + 1);  /* break jump */

    DUK_DDD(DUK_DDDPRINT("end parsing do statement"));
}

DUK_LOCAL void duk__parse_while_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site) {
    duk_regconst_t temp_reset;
    duk_regconst_t rc_cond;
    duk_int_t pc_start;
    duk_int_t pc_jump_false;

    DUK_DDD(DUK_DDDPRINT("begin parsing while statement"));

    temp_reset = DUK__GETTEMP(comp_ctx);

    duk__advance(comp_ctx);  /* eat 'while' */

    duk__advance_expect(comp_ctx, DUK_TOK_LPAREN);

    pc_start = duk__get_current_pc(comp_ctx);
    duk__patch_jump_here(comp_ctx, pc_label_site + 2);  /* continue jump */

    rc_cond = duk__exprtop_toregconst(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/);
    duk__emit_if_true_skip(comp_ctx, rc_cond);
    pc_jump_false = duk__emit_jump_empty(comp_ctx);
    DUK__SETTEMP(comp_ctx, temp_reset);

    comp_ctx->curr_func.allow_regexp_in_adv = 1;
    duk__advance_expect(comp_ctx, DUK_TOK_RPAREN);  /* Allow RegExp as part of next stmt. */

    duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/);
    duk__emit_jump(comp_ctx, pc_start);

    duk__patch_jump_here(comp_ctx, pc_jump_false);
    duk__patch_jump_here(comp_ctx, pc_label_site + 1);  /* break jump */

    DUK_DDD(DUK_DDDPRINT("end parsing while statement"));
}

DUK_LOCAL void duk__parse_break_or_continue_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
    duk_hthread *thr = comp_ctx->thr;
    duk_bool_t is_break = (comp_ctx->curr_token.t == DUK_TOK_BREAK);
    duk_int_t label_id;
    duk_int_t label_catch_depth;
    duk_int_t label_pc;  /* points to LABEL; pc+1 = jump site for break; pc+2 = jump site for continue */
    duk_bool_t label_is_closest;

    DUK_UNREF(res);

    duk__advance(comp_ctx);  /* eat 'break' or 'continue' */

    if (comp_ctx->curr_token.t == DUK_TOK_SEMICOLON ||  /* explicit semi follows */
        comp_ctx->curr_token.lineterm ||                /* automatic semi will be inserted */
        comp_ctx->curr_token.allow_auto_semi) {         /* automatic semi will be inserted */
        /* break/continue without label */

        duk__lookup_active_label(comp_ctx, DUK_HTHREAD_STRING_EMPTY_STRING(thr), is_break, &label_id, &label_catch_depth, &label_pc, &label_is_closest);
    } else if (comp_ctx->curr_token.t == DUK_TOK_IDENTIFIER) {
        /* break/continue with label (label cannot be a reserved word, production is 'Identifier' */
        DUK_ASSERT(comp_ctx->curr_token.str1 != NULL);
        duk__lookup_active_label(comp_ctx, comp_ctx->curr_token.str1, is_break, &label_id, &label_catch_depth, &label_pc, &label_is_closest);
        duk__advance(comp_ctx);
    } else {
        DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_BREAK_CONT_LABEL);
        DUK_WO_NORETURN(return;);
    }

    /* Use a fast break/continue when possible.  A fast break/continue is
     * just a jump to the LABEL break/continue jump slot, which then jumps
     * to an appropriate place (for break, going through ENDLABEL correctly).
     * The peephole optimizer will optimize the jump to a direct one.
     */

    if (label_catch_depth == comp_ctx->curr_func.catch_depth &&
        label_is_closest) {
        DUK_DDD(DUK_DDDPRINT("break/continue: is_break=%ld, label_id=%ld, label_is_closest=%ld, "
                             "label_catch_depth=%ld, catch_depth=%ld "
                             "-> use fast variant (direct jump)",
                             (long) is_break, (long) label_id, (long) label_is_closest,
                             (long) label_catch_depth, (long) comp_ctx->curr_func.catch_depth));

        duk__emit_jump(comp_ctx, label_pc + (is_break ? 1 : 2));
    } else {
        DUK_DDD(DUK_DDDPRINT("break/continue: is_break=%ld, label_id=%ld, label_is_closest=%ld, "
                             "label_catch_depth=%ld, catch_depth=%ld "
                             "-> use slow variant (longjmp)",
                             (long) is_break, (long) label_id, (long) label_is_closest,
                             (long) label_catch_depth, (long) comp_ctx->curr_func.catch_depth));

        duk__emit_bc(comp_ctx,
                     is_break ? DUK_OP_BREAK : DUK_OP_CONTINUE,
                     (duk_regconst_t) label_id);
    }
}

DUK_LOCAL void duk__parse_return_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
    duk_hthread *thr = comp_ctx->thr;
    duk_regconst_t rc_val;

    duk__advance(comp_ctx);  /* eat 'return' */

    /* A 'return' statement is only allowed inside an actual function body,
     * not as part of eval or global code.
     */
    if (!comp_ctx->curr_func.is_function) {
        DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_RETURN);
        DUK_WO_NORETURN(return;);
    }

    if (comp_ctx->curr_token.t == DUK_TOK_SEMICOLON ||  /* explicit semi follows */
        comp_ctx->curr_token.lineterm ||                /* automatic semi will be inserted */
        comp_ctx->curr_token.allow_auto_semi) {         /* automatic semi will be inserted */
        DUK_DDD(DUK_DDDPRINT("empty return value -> undefined"));
        duk__emit_op_only(comp_ctx, DUK_OP_RETUNDEF);
    } else {
        duk_int_t pc_before_expr;
        duk_int_t pc_after_expr;

        DUK_DDD(DUK_DDDPRINT("return with a value"));

        DUK_UNREF(pc_before_expr);
        DUK_UNREF(pc_after_expr);

        pc_before_expr = duk__get_current_pc(comp_ctx);
        rc_val = duk__exprtop_toregconst(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/);
        pc_after_expr = duk__get_current_pc(comp_ctx);

        /* Tail call check: if last opcode emitted was CALL, and
         * the context allows it, add a tailcall flag to the CALL.
         * This doesn't guarantee that a tail call will be allowed at
         * runtime, so the RETURN must still be emitted.  (Duktape
         * 0.10.0 avoided this and simulated a RETURN if a tail call
         * couldn't be used at runtime; but this didn't work
         * correctly with a thread yield/resume, see
         * test-bug-tailcall-thread-yield-resume.js for discussion.)
         *
         * In addition to the last opcode being CALL, we also need to
         * be sure that 'rc_val' is the result register of the CALL.
         * For instance, for the expression 'return 0, (function ()
         * { return 1; }), 2' the last opcode emitted is CALL (no
         * bytecode is emitted for '2') but 'rc_val' indicates
         * constant '2'.  Similarly if '2' is replaced by a register
         * bound variable, no opcodes are emitted but tail call would
         * be incorrect.
         *
         * This is tricky and easy to get wrong.  It would be best to
         * track enough expression metadata to check that 'rc_val' came
         * from that last CALL instruction.  We don't have that metadata
         * now, so we check that 'rc_val' is a temporary register result
         * (not a constant or a register bound variable).  There should
         * be no way currently for 'rc_val' to be a temporary for an
         * expression following the CALL instruction without emitting
         * some opcodes following the CALL.  This proxy check is used
         * below.
         *
         * See: test-bug-comma-expr-gh131.js.
         *
         * The non-standard 'caller' property disables tail calls
         * because they pose some special cases which haven't been
         * fixed yet.
         */

#if defined(DUK_USE_TAILCALL)
        if (comp_ctx->curr_func.catch_depth == 0 &&   /* no catchers */
            pc_after_expr > pc_before_expr) {         /* at least one opcode emitted */
            duk_compiler_instr *instr;
            duk_instr_t ins;
            duk_small_uint_t op;

            instr = duk__get_instr_ptr(comp_ctx, pc_after_expr - 1);
            DUK_ASSERT(instr != NULL);

            ins = instr->ins;
            op = (duk_small_uint_t) DUK_DEC_OP(ins);
            if ((op & ~0x0fU) == DUK_OP_CALL0 &&
                DUK__ISREG_TEMP(comp_ctx, rc_val) /* see above */) {
                DUK_DDD(DUK_DDDPRINT("return statement detected a tail call opportunity: "
                                     "catch depth is 0, duk__exprtop() emitted >= 1 instructions, "
                                     "and last instruction is a CALL "
                                     "-> change to TAILCALL"));
                ins |= DUK_ENC_OP(DUK_BC_CALL_FLAG_TAILCALL);
                instr->ins = ins;
            }
        }
#endif  /* DUK_USE_TAILCALL */

        if (DUK__ISREG(rc_val)) {
            duk__emit_bc(comp_ctx, DUK_OP_RETREG, rc_val);
        } else {
            rc_val = DUK__REMOVECONST(rc_val);
            if (duk__const_needs_refcount(comp_ctx, rc_val)) {
                duk__emit_bc(comp_ctx, DUK_OP_RETCONST, rc_val);
            } else {
                duk__emit_bc(comp_ctx, DUK_OP_RETCONSTN, rc_val);
            }
        }
    }
}

DUK_LOCAL void duk__parse_throw_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
    duk_regconst_t reg_val;

    duk__advance(comp_ctx);  /* eat 'throw' */

    /* Unlike break/continue, throw statement does not allow an empty value. */

    if (comp_ctx->curr_token.lineterm) {
        DUK_ERROR_SYNTAX(comp_ctx->thr, DUK_STR_INVALID_THROW);
        DUK_WO_NORETURN(return;);
    }

    reg_val = duk__exprtop_toreg(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/);
    duk__emit_bc(comp_ctx,
                 DUK_OP_THROW,
                 reg_val);
}

DUK_LOCAL void duk__parse_try_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
    duk_hthread *thr = comp_ctx->thr;
    duk_regconst_t reg_catch;      /* reg_catch+0 and reg_catch+1 are reserved for TRYCATCH */
    duk_regconst_t rc_varname = 0;
    duk_small_uint_t trycatch_flags = 0;
    duk_int_t pc_ldconst = -1;
    duk_int_t pc_trycatch = -1;
    duk_int_t pc_catch = -1;
    duk_int_t pc_finally = -1;

    DUK_UNREF(res);

    /*
     *  See the following documentation for discussion:
     *
     *    doc/execution.rst: control flow details
     *
     *  Try, catch, and finally "parts" are Blocks, not Statements, so
     *  they must always be delimited by curly braces.  This is unlike e.g.
     *  the if statement, which accepts any Statement.  This eliminates any
     *  questions of matching parts of nested try statements.  The Block
     *  parsing is implemented inline here (instead of calling out).
     *
     *  Finally part has a 'let scoped' variable, which requires a few kinks
     *  here.
     */

    comp_ctx->curr_func.catch_depth++;

    duk__advance(comp_ctx);  /* eat 'try' */

    reg_catch = DUK__ALLOCTEMPS(comp_ctx, 2);

    /* The target for this LDCONST may need output shuffling, but we assume
     * that 'pc_ldconst' will be the LDCONST that we can patch later.  This
     * should be the case because there's no input shuffling.  (If there's
     * no catch clause, this LDCONST will be replaced with a NOP.)
     */
    pc_ldconst = duk__get_current_pc(comp_ctx);
    duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, reg_catch, 0 /*patched later*/);

    pc_trycatch = duk__get_current_pc(comp_ctx);
    duk__emit_invalid(comp_ctx);  /* TRYCATCH, cannot emit now (not enough info) */
    duk__emit_invalid(comp_ctx);  /* jump for 'catch' case */
    duk__emit_invalid(comp_ctx);  /* jump for 'finally' case or end (if no finally) */

    /* try part */
    duk__advance_expect(comp_ctx, DUK_TOK_LCURLY);
    duk__parse_stmts(comp_ctx, 0 /*allow_source_elem*/, 0 /*expect_eof*/, 1 /*regexp_after*/);
    /* the DUK_TOK_RCURLY is eaten by duk__parse_stmts() */
    duk__emit_op_only(comp_ctx, DUK_OP_ENDTRY);

    if (comp_ctx->curr_token.t == DUK_TOK_CATCH) {
        /*
         *  The catch variable must be updated to reflect the new allocated
         *  register for the duration of the catch clause.  We need to store
         *  and restore the original value for the varmap entry (if any).
         */

        /*
         *  Note: currently register bindings must be fixed for the entire
         *  function.  So, even though the catch variable is in a register
         *  we know, we must use an explicit environment record and slow path
         *  accesses to read/write the catch binding to make closures created
         *  within the catch clause work correctly.  This restriction should
         *  be fixable (at least in common cases) later.
         *
         *  See: test-bug-catch-binding-2.js.
         *
         *  XXX: improve to get fast path access to most catch clauses.
         */

        duk_hstring *h_var;
        duk_int_t varmap_value;  /* for storing/restoring the varmap binding for catch variable */

        DUK_DDD(DUK_DDDPRINT("stack top at start of catch clause: %ld", (long) duk_get_top(thr)));

        trycatch_flags |= DUK_BC_TRYCATCH_FLAG_HAVE_CATCH;

        pc_catch = duk__get_current_pc(comp_ctx);

        duk__advance(comp_ctx);
        duk__advance_expect(comp_ctx, DUK_TOK_LPAREN);

        if (comp_ctx->curr_token.t != DUK_TOK_IDENTIFIER) {
            /* Identifier, i.e. don't allow reserved words */
            goto syntax_error;
        }
        h_var = comp_ctx->curr_token.str1;
        DUK_ASSERT(h_var != NULL);

        duk_push_hstring(thr, h_var);  /* keep in on valstack, use borrowed ref below */

        if (comp_ctx->curr_func.is_strict &&
            ((h_var == DUK_HTHREAD_STRING_EVAL(thr)) ||
             (h_var == DUK_HTHREAD_STRING_LC_ARGUMENTS(thr)))) {
            DUK_DDD(DUK_DDDPRINT("catch identifier 'eval' or 'arguments' in strict mode -> SyntaxError"));
            goto syntax_error;
        }

        duk_dup_top(thr);
        rc_varname = duk__getconst(comp_ctx);
        DUK_DDD(DUK_DDDPRINT("catch clause, rc_varname=0x%08lx (%ld)",
                             (unsigned long) rc_varname, (long) rc_varname));

        duk__advance(comp_ctx);
        duk__advance_expect(comp_ctx, DUK_TOK_RPAREN);

        duk__advance_expect(comp_ctx, DUK_TOK_LCURLY);

        DUK_DDD(DUK_DDDPRINT("varmap before modifying for catch clause: %!iT",
                             (duk_tval *) duk_get_tval(thr, comp_ctx->curr_func.varmap_idx)));

        duk_dup_top(thr);
        duk_get_prop(thr, comp_ctx->curr_func.varmap_idx);
        if (duk_is_undefined(thr, -1)) {
            varmap_value = -2;
        } else if (duk_is_null(thr, -1)) {
            varmap_value = -1;
        } else {
            DUK_ASSERT(duk_is_number(thr, -1));
            varmap_value = duk_get_int(thr, -1);
            DUK_ASSERT(varmap_value >= 0);
        }
        duk_pop(thr);

#if 0
        /* It'd be nice to do something like this - but it doesn't
         * work for closures created inside the catch clause.
         */
        duk_dup_top(thr);
        duk_push_int(thr, (duk_int_t) (reg_catch + 0));
        duk_put_prop(thr, comp_ctx->curr_func.varmap_idx);
#endif
        duk_dup_top(thr);
        duk_push_null(thr);
        duk_put_prop(thr, comp_ctx->curr_func.varmap_idx);

        duk__emit_a_bc(comp_ctx,
                       DUK_OP_PUTVAR | DUK__EMIT_FLAG_A_IS_SOURCE,
                       reg_catch + 0 /*value*/,
                       rc_varname /*varname*/);

        DUK_DDD(DUK_DDDPRINT("varmap before parsing catch clause: %!iT",
                             (duk_tval *) duk_get_tval(thr, comp_ctx->curr_func.varmap_idx)));

        duk__parse_stmts(comp_ctx, 0 /*allow_source_elem*/, 0 /*expect_eof*/, 1 /*regexp_after*/);
        /* the DUK_TOK_RCURLY is eaten by duk__parse_stmts() */

        if (varmap_value == -2) {
            /* not present */
            duk_del_prop(thr, comp_ctx->curr_func.varmap_idx);
        } else {
            if (varmap_value == -1) {
                duk_push_null(thr);
            } else {
                DUK_ASSERT(varmap_value >= 0);
                duk_push_int(thr, varmap_value);
            }
            duk_put_prop(thr, comp_ctx->curr_func.varmap_idx);
        }
        /* varname is popped by above code */

        DUK_DDD(DUK_DDDPRINT("varmap after restore catch clause: %!iT",
                             (duk_tval *) duk_get_tval(thr, comp_ctx->curr_func.varmap_idx)));

        duk__emit_op_only(comp_ctx,
                          DUK_OP_ENDCATCH);

        /*
         *  XXX: for now, indicate that an expensive catch binding
         *  declarative environment is always needed.  If we don't
         *  need it, we don't need the const_varname either.
         */

        trycatch_flags |= DUK_BC_TRYCATCH_FLAG_CATCH_BINDING;

        DUK_DDD(DUK_DDDPRINT("stack top at end of catch clause: %ld", (long) duk_get_top(thr)));
    }

    if (comp_ctx->curr_token.t == DUK_TOK_FINALLY) {
        trycatch_flags |= DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY;

        pc_finally = duk__get_current_pc(comp_ctx);

        duk__advance(comp_ctx);

        duk__advance_expect(comp_ctx, DUK_TOK_LCURLY);
        duk__parse_stmts(comp_ctx, 0 /*allow_source_elem*/, 0 /*expect_eof*/, 1 /*regexp_after*/);
        /* the DUK_TOK_RCURLY is eaten by duk__parse_stmts() */
        duk__emit_abc(comp_ctx,
                      DUK_OP_ENDFIN,
                      reg_catch);  /* rethrow */
    }

    if (!(trycatch_flags & DUK_BC_TRYCATCH_FLAG_HAVE_CATCH) &&
        !(trycatch_flags & DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY)) {
        /* must have catch and/or finally */
        goto syntax_error;
    }

    /* If there's no catch block, rc_varname will be 0 and duk__patch_trycatch()
     * will replace the LDCONST with a NOP.  For any actual constant (including
     * constant 0) the DUK__CONST_MARKER flag will be set in rc_varname.
     */

    duk__patch_trycatch(comp_ctx,
                        pc_ldconst,
                        pc_trycatch,
                        reg_catch,
                        rc_varname,
                        trycatch_flags);

    if (trycatch_flags & DUK_BC_TRYCATCH_FLAG_HAVE_CATCH) {
        DUK_ASSERT(pc_catch >= 0);
        duk__patch_jump(comp_ctx, pc_trycatch + 1, pc_catch);
    }

    if (trycatch_flags & DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY) {
        DUK_ASSERT(pc_finally >= 0);
        duk__patch_jump(comp_ctx, pc_trycatch + 2, pc_finally);
    } else {
        /* without finally, the second jump slot is used to jump to end of stmt */
        duk__patch_jump_here(comp_ctx, pc_trycatch + 2);
    }

    comp_ctx->curr_func.catch_depth--;
    return;

 syntax_error:
    DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_TRY);
    DUK_WO_NORETURN(return;);
}

DUK_LOCAL void duk__parse_with_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
    duk_int_t pc_trycatch;
    duk_int_t pc_finished;
    duk_regconst_t reg_catch;
    duk_small_uint_t trycatch_flags;

    if (comp_ctx->curr_func.is_strict) {
        DUK_ERROR_SYNTAX(comp_ctx->thr, DUK_STR_WITH_IN_STRICT_MODE);
        DUK_WO_NORETURN(return;);
    }

    comp_ctx->curr_func.catch_depth++;

    duk__advance(comp_ctx);  /* eat 'with' */

    reg_catch = DUK__ALLOCTEMPS(comp_ctx, 2);

    duk__advance_expect(comp_ctx, DUK_TOK_LPAREN);
    duk__exprtop_toforcedreg(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/, reg_catch);
    comp_ctx->curr_func.allow_regexp_in_adv = 1;
    duk__advance_expect(comp_ctx, DUK_TOK_RPAREN);  /* Allow RegExp as part of next stmt. */

    pc_trycatch = duk__get_current_pc(comp_ctx);
    trycatch_flags = DUK_BC_TRYCATCH_FLAG_WITH_BINDING;
    duk__emit_a_bc(comp_ctx,
                    DUK_OP_TRYCATCH | DUK__EMIT_FLAG_NO_SHUFFLE_A,
                    (duk_regconst_t) trycatch_flags /*a*/,
                    reg_catch /*bc*/);
    duk__emit_invalid(comp_ctx);  /* catch jump */
    duk__emit_invalid(comp_ctx);  /* finished jump */

    duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/);
    duk__emit_op_only(comp_ctx, DUK_OP_ENDTRY);

    pc_finished = duk__get_current_pc(comp_ctx);

    duk__patch_jump(comp_ctx, pc_trycatch + 2, pc_finished);

    comp_ctx->curr_func.catch_depth--;
}

DUK_LOCAL duk_int_t duk__stmt_label_site(duk_compiler_ctx *comp_ctx, duk_int_t label_id) {
    /* if a site already exists, nop: max one label site per statement */
    if (label_id >= 0) {
        return label_id;
    }

    label_id = comp_ctx->curr_func.label_next++;
    DUK_DDD(DUK_DDDPRINT("allocated new label id for label site: %ld", (long) label_id));

    duk__emit_bc(comp_ctx,
                 DUK_OP_LABEL,
                 (duk_regconst_t) label_id);
    duk__emit_invalid(comp_ctx);
    duk__emit_invalid(comp_ctx);

    return label_id;
}

/* Parse a single statement.
 *
 * Creates a label site (with an empty label) automatically for iteration
 * statements.  Also "peels off" any label statements for explicit labels.
 */
DUK_LOCAL void duk__parse_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_bool_t allow_source_elem) {
    duk_hthread *thr = comp_ctx->thr;
    duk_bool_t dir_prol_at_entry;    /* directive prologue status at entry */
    duk_regconst_t temp_at_entry;
    duk_size_t labels_len_at_entry;
    duk_int_t pc_at_entry;           /* assumed to also be PC of "LABEL" */
    duk_int_t stmt_id;
    duk_small_uint_t stmt_flags = 0;
    duk_int_t label_id = -1;
    duk_small_uint_t tok;
    duk_bool_t test_func_decl;

    DUK__RECURSION_INCREASE(comp_ctx, thr);

    temp_at_entry = DUK__GETTEMP(comp_ctx);
    pc_at_entry = duk__get_current_pc(comp_ctx);
    labels_len_at_entry = duk_get_length(thr, comp_ctx->curr_func.labelnames_idx);
    stmt_id = comp_ctx->curr_func.stmt_next++;
    dir_prol_at_entry = comp_ctx->curr_func.in_directive_prologue;

    DUK_UNREF(stmt_id);

    DUK_DDD(DUK_DDDPRINT("parsing a statement, stmt_id=%ld, temp_at_entry=%ld, labels_len_at_entry=%ld, "
                         "is_strict=%ld, in_directive_prologue=%ld, catch_depth=%ld",
                         (long) stmt_id, (long) temp_at_entry, (long) labels_len_at_entry,
                         (long) comp_ctx->curr_func.is_strict, (long) comp_ctx->curr_func.in_directive_prologue,
                         (long) comp_ctx->curr_func.catch_depth));

    /* The directive prologue flag is cleared by default so that it is
     * unset for any recursive statement parsing.  It is only "revived"
     * if a directive is detected.  (We could also make directives only
     * allowed if 'allow_source_elem' was true.)
     */
    comp_ctx->curr_func.in_directive_prologue = 0;

 retry_parse:

    DUK_DDD(DUK_DDDPRINT("try stmt parse, stmt_id=%ld, label_id=%ld, allow_source_elem=%ld, catch_depth=%ld",
                         (long) stmt_id, (long) label_id, (long) allow_source_elem,
                         (long) comp_ctx->curr_func.catch_depth));

    /*
     *  Detect iteration statements; if encountered, establish an
     *  empty label.
     */

    tok = comp_ctx->curr_token.t;
    if (tok == DUK_TOK_FOR || tok == DUK_TOK_DO || tok == DUK_TOK_WHILE ||
        tok == DUK_TOK_SWITCH) {
        DUK_DDD(DUK_DDDPRINT("iteration/switch statement -> add empty label"));

        label_id = duk__stmt_label_site(comp_ctx, label_id);
        duk__add_label(comp_ctx,
                       DUK_HTHREAD_STRING_EMPTY_STRING(thr),
                       pc_at_entry /*pc_label*/,
                       label_id);
    }

    /*
     *  Main switch for statement / source element type.
     */

    switch (comp_ctx->curr_token.t) {
    case DUK_TOK_FUNCTION: {
        /*
         *  Function declaration, function expression, or (non-standard)
         *  function statement.
         *
         *  The E5 specification only allows function declarations at
         *  the top level (in "source elements").  An ExpressionStatement
         *  is explicitly not allowed to begin with a "function" keyword
         *  (E5 Section 12.4).  Hence any non-error semantics for such
         *  non-top-level statements are non-standard.  Duktape semantics
         *  for function statements are modelled after V8, see
         *  test-dev-func-decl-outside-top.js.
         */
        test_func_decl = allow_source_elem;
#if defined(DUK_USE_NONSTD_FUNC_STMT)
        /* Lenient: allow function declarations outside top level in both
         * strict and non-strict modes.  However, don't allow labelled
         * function declarations in strict mode.
         */
        test_func_decl = test_func_decl ||
                         !comp_ctx->curr_func.is_strict ||
                         label_id < 0;
#endif  /* DUK_USE_NONSTD_FUNC_STMT */
        /* Strict: never allow function declarations outside top level. */
        if (test_func_decl) {
            /* FunctionDeclaration: not strictly a statement but handled as such.
             *
             * O(depth^2) parse count for inner functions is handled by recording a
             * lexer offset on the first compilation pass, so that the function can
             * be efficiently skipped on the second pass.  This is encapsulated into
             * duk__parse_func_like_fnum().
             */

            duk_int_t fnum;
#if defined(DUK_USE_ASSERTIONS)
            duk_idx_t top_before;
#endif

            DUK_DDD(DUK_DDDPRINT("function declaration statement"));

#if defined(DUK_USE_ASSERTIONS)
            top_before = duk_get_top(thr);
#endif

            duk__advance(comp_ctx);  /* eat 'function' */
            fnum = duk__parse_func_like_fnum(comp_ctx, DUK__FUNC_FLAG_DECL | DUK__FUNC_FLAG_PUSHNAME_PASS1);

            /* The value stack convention here is a bit odd: the function
             * name is only pushed on pass 1 (in_scanning), and is needed
             * to process function declarations.
             */
            if (comp_ctx->curr_func.in_scanning) {
                duk_uarridx_t n;

#if defined(DUK_USE_ASSERTIONS)
                DUK_ASSERT(duk_get_top(thr) == top_before + 1);
#endif
                DUK_DDD(DUK_DDDPRINT("register function declaration %!T in pass 1, fnum %ld",
                                     duk_get_tval(thr, -1), (long) fnum));
                n = (duk_uarridx_t) duk_get_length(thr, comp_ctx->curr_func.decls_idx);
                /* funcname is at index -1 */
                duk_put_prop_index(thr, comp_ctx->curr_func.decls_idx, n);
                duk_push_int(thr, (duk_int_t) (DUK_DECL_TYPE_FUNC + (fnum << 8)));
                duk_put_prop_index(thr, comp_ctx->curr_func.decls_idx, n + 1);
            } else {
#if defined(DUK_USE_ASSERTIONS)
                DUK_ASSERT(duk_get_top(thr) == top_before);
#endif
            }

            /* no statement value (unlike function expression) */
            stmt_flags = 0;
            break;
        } else {
            DUK_ERROR_SYNTAX(thr, DUK_STR_FUNC_STMT_NOT_ALLOWED);
            DUK_WO_NORETURN(return;);
        }
        break;
    }
    case DUK_TOK_LCURLY: {
        DUK_DDD(DUK_DDDPRINT("block statement"));
        duk__advance(comp_ctx);
        duk__parse_stmts(comp_ctx, 0 /*allow_source_elem*/, 0 /*expect_eof*/, 1 /*regexp_after*/);
        /* the DUK_TOK_RCURLY is eaten by duk__parse_stmts() */
        if (label_id >= 0) {
            duk__patch_jump_here(comp_ctx, pc_at_entry + 1);  /* break jump */
        }
        stmt_flags = 0;
        break;
    }
    case DUK_TOK_CONST: {
        DUK_DDD(DUK_DDDPRINT("constant declaration statement"));
        duk__parse_var_stmt(comp_ctx, res, DUK__EXPR_FLAG_REQUIRE_INIT /*expr_flags*/);
        stmt_flags = DUK__HAS_TERM;
        break;
    }
    case DUK_TOK_VAR: {
        DUK_DDD(DUK_DDDPRINT("variable declaration statement"));
        duk__parse_var_stmt(comp_ctx, res, 0 /*expr_flags*/);
        stmt_flags = DUK__HAS_TERM;
        break;
    }
    case DUK_TOK_SEMICOLON: {
        /* empty statement with an explicit semicolon */
        DUK_DDD(DUK_DDDPRINT("empty statement"));
        stmt_flags = DUK__HAS_TERM;
        break;
    }
    case DUK_TOK_IF: {
        DUK_DDD(DUK_DDDPRINT("if statement"));
        duk__parse_if_stmt(comp_ctx, res);
        if (label_id >= 0) {
            duk__patch_jump_here(comp_ctx, pc_at_entry + 1);  /* break jump */
        }
        stmt_flags = 0;
        break;
    }
    case DUK_TOK_DO: {
        /*
         *  Do-while statement is mostly trivial, but there is special
         *  handling for automatic semicolon handling (triggered by the
         *  DUK__ALLOW_AUTO_SEMI_ALWAYS) flag related to a bug filed at:
         *
         *    https://bugs.ecmascript.org/show_bug.cgi?id=8
         *
         *  See doc/compiler.rst for details.
         */
        DUK_DDD(DUK_DDDPRINT("do statement"));
        DUK_ASSERT(label_id >= 0);
        duk__update_label_flags(comp_ctx,
                                label_id,
                                DUK_LABEL_FLAG_ALLOW_BREAK | DUK_LABEL_FLAG_ALLOW_CONTINUE);
        duk__parse_do_stmt(comp_ctx, res, pc_at_entry);
        stmt_flags = DUK__HAS_TERM | DUK__ALLOW_AUTO_SEMI_ALWAYS;  /* DUK__ALLOW_AUTO_SEMI_ALWAYS workaround */
        break;
    }
    case DUK_TOK_WHILE: {
        DUK_DDD(DUK_DDDPRINT("while statement"));
        DUK_ASSERT(label_id >= 0);
        duk__update_label_flags(comp_ctx,
                                label_id,
                                DUK_LABEL_FLAG_ALLOW_BREAK | DUK_LABEL_FLAG_ALLOW_CONTINUE);
        duk__parse_while_stmt(comp_ctx, res, pc_at_entry);
        stmt_flags = 0;
        break;
    }
    case DUK_TOK_FOR: {
        /*
         *  For/for-in statement is complicated to parse because
         *  determining the statement type (three-part for vs. a
         *  for-in) requires potential backtracking.
         *
         *  See the helper for the messy stuff.
         */
        DUK_DDD(DUK_DDDPRINT("for/for-in statement"));
        DUK_ASSERT(label_id >= 0);
        duk__update_label_flags(comp_ctx,
                                label_id,
                                DUK_LABEL_FLAG_ALLOW_BREAK | DUK_LABEL_FLAG_ALLOW_CONTINUE);
        duk__parse_for_stmt(comp_ctx, res, pc_at_entry);
        stmt_flags = 0;
        break;
    }
    case DUK_TOK_CONTINUE:
    case DUK_TOK_BREAK: {
        DUK_DDD(DUK_DDDPRINT("break/continue statement"));
        duk__parse_break_or_continue_stmt(comp_ctx, res);
        stmt_flags = DUK__HAS_TERM | DUK__IS_TERMINAL;
        break;
    }
    case DUK_TOK_RETURN: {
        DUK_DDD(DUK_DDDPRINT("return statement"));
        duk__parse_return_stmt(comp_ctx, res);
        stmt_flags = DUK__HAS_TERM | DUK__IS_TERMINAL;
        break;
    }
    case DUK_TOK_WITH: {
        DUK_DDD(DUK_DDDPRINT("with statement"));
        comp_ctx->curr_func.with_depth++;
        duk__parse_with_stmt(comp_ctx, res);
        if (label_id >= 0) {
            duk__patch_jump_here(comp_ctx, pc_at_entry + 1);  /* break jump */
        }
        comp_ctx->curr_func.with_depth--;
        stmt_flags = 0;
        break;
    }
    case DUK_TOK_SWITCH: {
        /*
         *  The switch statement is pretty messy to compile.
         *  See the helper for details.
         */
        DUK_DDD(DUK_DDDPRINT("switch statement"));
        DUK_ASSERT(label_id >= 0);
        duk__update_label_flags(comp_ctx,
                                label_id,
                                DUK_LABEL_FLAG_ALLOW_BREAK);  /* don't allow continue */
        duk__parse_switch_stmt(comp_ctx, res, pc_at_entry);
        stmt_flags = 0;
        break;
    }
    case DUK_TOK_THROW: {
        DUK_DDD(DUK_DDDPRINT("throw statement"));
        duk__parse_throw_stmt(comp_ctx, res);
        stmt_flags = DUK__HAS_TERM | DUK__IS_TERMINAL;
        break;
    }
    case DUK_TOK_TRY: {
        DUK_DDD(DUK_DDDPRINT("try statement"));
        duk__parse_try_stmt(comp_ctx, res);
        stmt_flags = 0;
        break;
    }
    case DUK_TOK_DEBUGGER: {
        duk__advance(comp_ctx);
#if defined(DUK_USE_DEBUGGER_SUPPORT)
        DUK_DDD(DUK_DDDPRINT("debugger statement: debugging enabled, emit debugger opcode"));
        duk__emit_op_only(comp_ctx, DUK_OP_DEBUGGER);
#else
        DUK_DDD(DUK_DDDPRINT("debugger statement: ignored"));
#endif
        stmt_flags = DUK__HAS_TERM;
        break;
    }
    default: {
        /*
         *  Else, must be one of:
         *    - ExpressionStatement, possibly a directive (String)
         *    - LabelledStatement (Identifier followed by ':')
         *
         *  Expressions beginning with 'function' keyword are covered by a case
         *  above (such expressions are not allowed in standard E5 anyway).
         *  Also expressions starting with '{' are interpreted as block
         *  statements.  See E5 Section 12.4.
         *
         *  Directive detection is tricky; see E5 Section 14.1 on directive
         *  prologue.  A directive is an expression statement with a single
         *  string literal and an explicit or automatic semicolon.  Escape
         *  characters are significant and no parens etc are allowed:
         *
         *    'use strict';          // valid 'use strict' directive
         *    'use\u0020strict';     // valid directive, not a 'use strict' directive
         *    ('use strict');        // not a valid directive
         *
         *  The expression is determined to consist of a single string literal
         *  based on duk__expr_nud() and duk__expr_led() call counts.  The string literal
         *  of a 'use strict' directive is determined to lack any escapes based
         *  num_escapes count from the lexer.  Note that other directives may be
         *  allowed to contain escapes, so a directive with escapes does not
         *  terminate a directive prologue.
         *
         *  We rely on the fact that the expression parser will not emit any
         *  code for a single token expression.  However, it will generate an
         *  intermediate value which we will then successfully ignore.
         *
         *  A similar approach is used for labels.
         */

        duk_bool_t single_token;

        DUK_DDD(DUK_DDDPRINT("expression statement"));
        duk__exprtop(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/);

        single_token = (comp_ctx->curr_func.nud_count == 1 &&  /* one token */
                        comp_ctx->curr_func.led_count == 0);   /* no operators */

        if (single_token &&
            comp_ctx->prev_token.t == DUK_TOK_IDENTIFIER &&
            comp_ctx->curr_token.t == DUK_TOK_COLON) {
            /*
             *  Detected label
             */

            duk_hstring *h_lab;

            /* expected ival */
            DUK_ASSERT(res->t == DUK_IVAL_VAR);
            DUK_ASSERT(res->x1.t == DUK_ISPEC_VALUE);
            DUK_ASSERT(DUK_TVAL_IS_STRING(duk_get_tval(thr, res->x1.valstack_idx)));
            h_lab = comp_ctx->prev_token.str1;
            DUK_ASSERT(h_lab != NULL);

            DUK_DDD(DUK_DDDPRINT("explicit label site for label '%!O'",
                                 (duk_heaphdr *) h_lab));

            duk__advance(comp_ctx);  /* eat colon */

            label_id = duk__stmt_label_site(comp_ctx, label_id);

            duk__add_label(comp_ctx,
                           h_lab,
                           pc_at_entry /*pc_label*/,
                           label_id);

            /* a statement following a label cannot be a source element
             * (a function declaration).
             */
            allow_source_elem = 0;

            DUK_DDD(DUK_DDDPRINT("label handled, retry statement parsing"));
            goto retry_parse;
        }

        stmt_flags = 0;

        if (dir_prol_at_entry &&                           /* still in prologue */
            single_token &&                                /* single string token */
            comp_ctx->prev_token.t == DUK_TOK_STRING) {
            /*
             *  Detected a directive
             */
            duk_hstring *h_dir;

            /* expected ival */
            DUK_ASSERT(res->t == DUK_IVAL_PLAIN);
            DUK_ASSERT(res->x1.t == DUK_ISPEC_VALUE);
            DUK_ASSERT(DUK_TVAL_IS_STRING(duk_get_tval(thr, res->x1.valstack_idx)));
            h_dir = comp_ctx->prev_token.str1;
            DUK_ASSERT(h_dir != NULL);

            DUK_DDD(DUK_DDDPRINT("potential directive: %!O", h_dir));

            stmt_flags |= DUK__STILL_PROLOGUE;

            /* Note: escaped characters differentiate directives */

            if (comp_ctx->prev_token.num_escapes > 0) {
                DUK_DDD(DUK_DDDPRINT("directive contains escapes: valid directive "
                                     "but we ignore such directives"));
            } else {
                /*
                 * The length comparisons are present to handle
                 * strings like "use strict\u0000foo" as required.
                 */

                if (DUK_HSTRING_GET_BYTELEN(h_dir) == 10 &&
                    DUK_STRCMP((const char *) DUK_HSTRING_GET_DATA(h_dir), "use strict") == 0) {
#if defined(DUK_USE_STRICT_DECL)
                    DUK_DDD(DUK_DDDPRINT("use strict directive detected: strict flag %ld -> %ld",
                                         (long) comp_ctx->curr_func.is_strict, (long) 1));
                    comp_ctx->curr_func.is_strict = 1;
#else
                    DUK_DDD(DUK_DDDPRINT("use strict detected but strict declarations disabled, ignoring"));
#endif
                } else if (DUK_HSTRING_GET_BYTELEN(h_dir) == 14 &&
                           DUK_STRCMP((const char *) DUK_HSTRING_GET_DATA(h_dir), "use duk notail") == 0) {
                    DUK_DDD(DUK_DDDPRINT("use duk notail directive detected: notail flag %ld -> %ld",
                                         (long) comp_ctx->curr_func.is_notail, (long) 1));
                    comp_ctx->curr_func.is_notail = 1;
                } else {
                    DUK_DD(DUK_DDPRINT("unknown directive: '%!O', ignoring but not terminating "
                                       "directive prologue", (duk_hobject *) h_dir));
                }
            }
        } else {
            DUK_DDD(DUK_DDDPRINT("non-directive expression statement or no longer in prologue; "
                                 "prologue terminated if still active"));
                }

        stmt_flags |= DUK__HAS_VAL | DUK__HAS_TERM;
    }
    }  /* end switch (tok) */

    /*
     *  Statement value handling.
     *
     *  Global code and eval code has an implicit return value
     *  which comes from the last statement with a value
     *  (technically a non-"empty" continuation, which is
     *  different from an empty statement).
     *
     *  Since we don't know whether a later statement will
     *  override the value of the current statement, we need
     *  to coerce the statement value to a register allocated
     *  for implicit return values.  In other cases we need
     *  to coerce the statement value to a plain value to get
     *  any side effects out (consider e.g. "foo.bar;").
     */

    /* XXX: what about statements which leave a half-cooked value in 'res'
     * but have no stmt value?  Any such statements?
     */

    if (stmt_flags & DUK__HAS_VAL) {
        duk_regconst_t reg_stmt_value = comp_ctx->curr_func.reg_stmt_value;
        if (reg_stmt_value >= 0) {
            duk__ivalue_toforcedreg(comp_ctx, res, reg_stmt_value);
        } else {
            duk__ivalue_toplain_ignore(comp_ctx, res);
        }
    } else {
        ;
    }

    /*
     *  Statement terminator check, including automatic semicolon
     *  handling.  After this step, 'curr_tok' should be the first
     *  token after a possible statement terminator.
     */

    if (stmt_flags & DUK__HAS_TERM) {
        if (comp_ctx->curr_token.t == DUK_TOK_SEMICOLON) {
            DUK_DDD(DUK_DDDPRINT("explicit semicolon terminates statement"));
            duk__advance(comp_ctx);
        } else {
            if (comp_ctx->curr_token.allow_auto_semi) {
                DUK_DDD(DUK_DDDPRINT("automatic semicolon terminates statement"));
            } else if (stmt_flags & DUK__ALLOW_AUTO_SEMI_ALWAYS) {
                /* XXX: make this lenience dependent on flags or strictness? */
                DUK_DDD(DUK_DDDPRINT("automatic semicolon terminates statement (allowed for compatibility "
                                     "even though no lineterm present before next token)"));
            } else {
                DUK_ERROR_SYNTAX(thr, DUK_STR_UNTERMINATED_STMT);
                DUK_WO_NORETURN(return;);
            }
        }
    } else {
        DUK_DDD(DUK_DDDPRINT("statement has no terminator"));
    }

    /*
     *  Directive prologue tracking.
     */

    if (stmt_flags & DUK__STILL_PROLOGUE) {
        DUK_DDD(DUK_DDDPRINT("setting in_directive_prologue"));
        comp_ctx->curr_func.in_directive_prologue = 1;
    }

    /*
     *  Cleanups (all statement parsing flows through here).
     *
     *  Pop label site and reset labels.  Reset 'next temp' to value at
     *  entry to reuse temps.
     */

    if (label_id >= 0) {
        duk__emit_bc(comp_ctx,
                     DUK_OP_ENDLABEL,
                     (duk_regconst_t) label_id);
    }

    DUK__SETTEMP(comp_ctx, temp_at_entry);

    duk__reset_labels_to_length(comp_ctx, labels_len_at_entry);

    /* XXX: return indication of "terminalness" (e.g. a 'throw' is terminal) */

    DUK__RECURSION_DECREASE(comp_ctx, thr);
}

/*
 *  Parse a statement list.
 *
 *  Handles automatic semicolon insertion and implicit return value.
 *
 *  Upon entry, 'curr_tok' should contain the first token of the first
 *  statement (parsed in the "allow regexp literal" mode).  Upon exit,
 *  'curr_tok' contains the token following the statement list terminator
 *  (EOF or closing brace).
 */

DUK_LOCAL void duk__parse_stmts(duk_compiler_ctx *comp_ctx, duk_bool_t allow_source_elem, duk_bool_t expect_eof, duk_bool_t regexp_after) {
    duk_hthread *thr = comp_ctx->thr;
    duk_ivalue res_alloc;
    duk_ivalue *res = &res_alloc;

    /* Setup state.  Initial ivalue is 'undefined'. */

    duk_require_stack(thr, DUK__PARSE_STATEMENTS_SLOTS);

    /* XXX: 'res' setup can be moved to function body level; in fact, two 'res'
     * intermediate values suffice for parsing of each function.  Nesting is needed
     * for nested functions (which may occur inside expressions).
     */

    duk_memzero(&res_alloc, sizeof(res_alloc));
    res->t = DUK_IVAL_PLAIN;
    res->x1.t = DUK_ISPEC_VALUE;
    res->x1.valstack_idx = duk_get_top(thr);
    res->x2.valstack_idx = res->x1.valstack_idx + 1;
    duk_push_undefined(thr);
    duk_push_undefined(thr);

    /* Parse statements until a closing token (EOF or '}') is found. */

    for (;;) {
        /* Check whether statement list ends. */

        if (expect_eof) {
            if (comp_ctx->curr_token.t == DUK_TOK_EOF) {
                break;
            }
        } else {
            if (comp_ctx->curr_token.t == DUK_TOK_RCURLY) {
                break;
            }
        }

        /* Check statement type based on the first token type.
         *
         * Note: expression parsing helpers expect 'curr_tok' to
         * contain the first token of the expression upon entry.
         */

        DUK_DDD(DUK_DDDPRINT("TOKEN %ld (non-whitespace, non-comment)", (long) comp_ctx->curr_token.t));

        duk__parse_stmt(comp_ctx, res, allow_source_elem);
    }

    /* RegExp is allowed / not allowed depending on context.  For function
     * declarations RegExp is allowed because it follows a function
     * declaration statement and may appear as part of the next statement.
     * For function expressions RegExp is not allowed, and it's possible
     * to do something like '(function () {} / 123)'.
     */
    if (regexp_after) {
        comp_ctx->curr_func.allow_regexp_in_adv = 1;
    }
    duk__advance(comp_ctx);

    /* Tear down state. */

    duk_pop_2(thr);
}

/*
 *  Declaration binding instantiation conceptually happens when calling a
 *  function; for us it essentially means that function prologue.  The
 *  conceptual process is described in E5 Section 10.5.
 *
 *  We need to keep track of all encountered identifiers to (1) create an
 *  identifier-to-register map ("varmap"); and (2) detect duplicate
 *  declarations.  Identifiers which are not bound to registers still need
 *  to be tracked for detecting duplicates.  Currently such identifiers
 *  are put into the varmap with a 'null' value, which is later cleaned up.
 *
 *  To support functions with a large number of variable and function
 *  declarations, registers are not allocated beyond a certain limit;
 *  after that limit, variables and functions need slow path access.
 *  Arguments are currently always register bound, which imposes a hard
 *  (and relatively small) argument count limit.
 *
 *  Some bindings in E5 are not configurable (= deletable) and almost all
 *  are mutable (writable).  Exceptions are:
 *
 *    - The 'arguments' binding, established only if no shadowing argument
 *      or function declaration exists.  We handle 'arguments' creation
 *      and binding through an explicit slow path environment record.
 *
 *    - The "name" binding for a named function expression.  This is also
 *      handled through an explicit slow path environment record.
 */

/* XXX: add support for variables to not be register bound always, to
 * handle cases with a very large number of variables?
 */

DUK_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ctx, duk_regconst_t *out_stmt_value_reg) {
    duk_hthread *thr = comp_ctx->thr;
    duk_hstring *h_name;
    duk_bool_t configurable_bindings;
    duk_uarridx_t num_args;
    duk_uarridx_t num_decls;
    duk_regconst_t rc_name;
    duk_small_uint_t declvar_flags;
    duk_uarridx_t i;
#if defined(DUK_USE_ASSERTIONS)
    duk_idx_t entry_top;
#endif

#if defined(DUK_USE_ASSERTIONS)
    entry_top = duk_get_top(thr);
#endif

    /*
     *  Preliminaries
     */

    configurable_bindings = comp_ctx->curr_func.is_eval;
    DUK_DDD(DUK_DDDPRINT("configurable_bindings=%ld", (long) configurable_bindings));

    /* varmap is already in comp_ctx->curr_func.varmap_idx */

    /*
     *  Function formal arguments, always bound to registers
     *  (there's no support for shuffling them now).
     */

    num_args = (duk_uarridx_t) duk_get_length(thr, comp_ctx->curr_func.argnames_idx);
    DUK_DDD(DUK_DDDPRINT("num_args=%ld", (long) num_args));
    /* XXX: check num_args */

    for (i = 0; i < num_args; i++) {
        duk_get_prop_index(thr, comp_ctx->curr_func.argnames_idx, i);
        h_name = duk_known_hstring(thr, -1);

        if (comp_ctx->curr_func.is_strict) {
            if (duk__hstring_is_eval_or_arguments(comp_ctx, h_name)) {
                DUK_DDD(DUK_DDDPRINT("arg named 'eval' or 'arguments' in strict mode -> SyntaxError"));
                goto error_argname;
            }
            duk_dup_top(thr);
            if (duk_has_prop(thr, comp_ctx->curr_func.varmap_idx)) {
                DUK_DDD(DUK_DDDPRINT("duplicate arg name in strict mode -> SyntaxError"));
                goto error_argname;
            }

            /* Ensure argument name is not a reserved word in current
             * (final) strictness.  Formal argument parsing may not
             * catch reserved names if strictness changes during
             * parsing.
             *
             * We only need to do this in strict mode because non-strict
             * keyword are always detected in formal argument parsing.
             */

            if (DUK_HSTRING_HAS_STRICT_RESERVED_WORD(h_name)) {
                goto error_argname;
            }
        }

        /* overwrite any previous binding of the same name; the effect is
         * that last argument of a certain name wins.
         */

        /* only functions can have arguments */
        DUK_ASSERT(comp_ctx->curr_func.is_function);
        duk_push_uarridx(thr, i);  /* -> [ ... name index ] */
        duk_put_prop(thr, comp_ctx->curr_func.varmap_idx); /* -> [ ... ] */

        /* no code needs to be emitted, the regs already have values */
    }

    /* use temp_next for tracking register allocations */
    DUK__SETTEMP_CHECKMAX(comp_ctx, (duk_regconst_t) num_args);

    /*
     *  After arguments, allocate special registers (like shuffling temps)
     */

    if (out_stmt_value_reg) {
        *out_stmt_value_reg = DUK__ALLOCTEMP(comp_ctx);
    }
    if (comp_ctx->curr_func.needs_shuffle) {
        duk_regconst_t shuffle_base = DUK__ALLOCTEMPS(comp_ctx, 3);
        comp_ctx->curr_func.shuffle1 = shuffle_base;
        comp_ctx->curr_func.shuffle2 = shuffle_base + 1;
        comp_ctx->curr_func.shuffle3 = shuffle_base + 2;
        DUK_D(DUK_DPRINT("shuffle registers needed by function, allocated: %ld %ld %ld",
                         (long) comp_ctx->curr_func.shuffle1,
                         (long) comp_ctx->curr_func.shuffle2,
                         (long) comp_ctx->curr_func.shuffle3));
    }
    if (comp_ctx->curr_func.temp_next > 0x100) {
        DUK_D(DUK_DPRINT("not enough 8-bit regs: temp_next=%ld", (long) comp_ctx->curr_func.temp_next));
        goto error_outofregs;
    }

    /*
     *  Function declarations
     */

    num_decls = (duk_uarridx_t) duk_get_length(thr, comp_ctx->curr_func.decls_idx);
    DUK_DDD(DUK_DDDPRINT("num_decls=%ld -> %!T",
                         (long) num_decls,
                         (duk_tval *) duk_get_tval(thr, comp_ctx->curr_func.decls_idx)));
    for (i = 0; i < num_decls; i += 2) {
        duk_int_t decl_type;
        duk_int_t fnum;

        duk_get_prop_index(thr, comp_ctx->curr_func.decls_idx, i + 1);  /* decl type */
        decl_type = duk_to_int(thr, -1);
        fnum = decl_type >> 8;  /* XXX: macros */
        decl_type = decl_type & 0xff;
        duk_pop(thr);

        if (decl_type != DUK_DECL_TYPE_FUNC) {
            continue;
        }

        duk_get_prop_index(thr, comp_ctx->curr_func.decls_idx, i);  /* decl name */

        /* XXX: spilling */
        if (comp_ctx->curr_func.is_function) {
            duk_regconst_t reg_bind;
            duk_dup_top(thr);
            if (duk_has_prop(thr, comp_ctx->curr_func.varmap_idx)) {
                /* shadowed; update value */
                duk_dup_top(thr);
                duk_get_prop(thr, comp_ctx->curr_func.varmap_idx);
                reg_bind = duk_to_int(thr, -1);  /* [ ... name reg_bind ] */
                duk__emit_a_bc(comp_ctx,
                               DUK_OP_CLOSURE,
                               reg_bind,
                               (duk_regconst_t) fnum);
            } else {
                /* function: always register bound */
                reg_bind = DUK__ALLOCTEMP(comp_ctx);
                duk__emit_a_bc(comp_ctx,
                               DUK_OP_CLOSURE,
                               reg_bind,
                               (duk_regconst_t) fnum);
                duk_push_int(thr, (duk_int_t) reg_bind);
            }
        } else {
            /* Function declaration for global/eval code is emitted even
             * for duplicates, because of E5 Section 10.5, step 5.e of
             * E5.1 (special behavior for variable bound to global object).
             *
             * DECLVAR will not re-declare a variable as such, but will
             * update the binding value.
             */

            duk_regconst_t reg_temp = DUK__ALLOCTEMP(comp_ctx);
            duk_dup_top(thr);
            rc_name = duk__getconst(comp_ctx);
            duk_push_null(thr);

            duk__emit_a_bc(comp_ctx,
                           DUK_OP_CLOSURE,
                           reg_temp,
                           (duk_regconst_t) fnum);

            declvar_flags = DUK_PROPDESC_FLAG_WRITABLE |
                            DUK_PROPDESC_FLAG_ENUMERABLE |
                            DUK_BC_DECLVAR_FLAG_FUNC_DECL;

            if (configurable_bindings) {
                declvar_flags |= DUK_PROPDESC_FLAG_CONFIGURABLE;
            }

            duk__emit_a_b_c(comp_ctx,
                            DUK_OP_DECLVAR | DUK__EMIT_FLAG_NO_SHUFFLE_A | DUK__EMIT_FLAG_BC_REGCONST,
                            (duk_regconst_t) declvar_flags /*flags*/,
                            rc_name /*name*/,
                            reg_temp /*value*/);

            DUK__SETTEMP(comp_ctx, reg_temp);  /* forget temp */
        }

        DUK_DDD(DUK_DDDPRINT("function declaration to varmap: %!T -> %!T",
                             (duk_tval *) duk_get_tval(thr, -2),
                             (duk_tval *) duk_get_tval(thr, -1)));

#if defined(DUK_USE_FASTINT)
        DUK_ASSERT(DUK_TVAL_IS_NULL(duk_get_tval(thr, -1)) || DUK_TVAL_IS_FASTINT(duk_get_tval(thr, -1)));
#endif
        duk_put_prop(thr, comp_ctx->curr_func.varmap_idx);  /* [ ... name reg/null ] -> [ ... ] */
    }

    /*
     *  'arguments' binding is special; if a shadowing argument or
     *  function declaration exists, an arguments object will
     *  definitely not be needed, regardless of whether the identifier
     *  'arguments' is referenced inside the function body.
     */

    if (duk_has_prop_stridx(thr, comp_ctx->curr_func.varmap_idx, DUK_STRIDX_LC_ARGUMENTS)) {
        DUK_DDD(DUK_DDDPRINT("'arguments' is shadowed by argument or function declaration "
                             "-> arguments object creation can be skipped"));
        comp_ctx->curr_func.is_arguments_shadowed = 1;
    }

    /*
     *  Variable declarations.
     *
     *  Unlike function declarations, variable declaration values don't get
     *  assigned on entry.  If a binding of the same name already exists, just
     *  ignore it silently.
     */

    for (i = 0; i < num_decls; i += 2) {
        duk_int_t decl_type;

        duk_get_prop_index(thr, comp_ctx->curr_func.decls_idx, i + 1);  /* decl type */
        decl_type = duk_to_int(thr, -1);
        decl_type = decl_type & 0xff;
        duk_pop(thr);

        if (decl_type != DUK_DECL_TYPE_VAR) {
            continue;
        }

        duk_get_prop_index(thr, comp_ctx->curr_func.decls_idx, i);  /* decl name */

        if (duk_has_prop(thr, comp_ctx->curr_func.varmap_idx)) {
            /* shadowed, ignore */
        } else {
            duk_get_prop_index(thr, comp_ctx->curr_func.decls_idx, i);  /* decl name */
            h_name = duk_known_hstring(thr, -1);

            if (h_name == DUK_HTHREAD_STRING_LC_ARGUMENTS(thr) &&
                !comp_ctx->curr_func.is_arguments_shadowed) {
                /* E5 Section steps 7-8 */
                DUK_DDD(DUK_DDDPRINT("'arguments' not shadowed by a function declaration, "
                                     "but appears as a variable declaration -> treat as "
                                     "a no-op for variable declaration purposes"));
                duk_pop(thr);
                continue;
            }

            /* XXX: spilling */
            if (comp_ctx->curr_func.is_function) {
                duk_regconst_t reg_bind = DUK__ALLOCTEMP(comp_ctx);
                /* no need to init reg, it will be undefined on entry */
                duk_push_int(thr, (duk_int_t) reg_bind);
            } else {
                duk_dup_top(thr);
                rc_name = duk__getconst(comp_ctx);
                duk_push_null(thr);

                declvar_flags = DUK_PROPDESC_FLAG_WRITABLE |
                                    DUK_PROPDESC_FLAG_ENUMERABLE;
                if (configurable_bindings) {
                    declvar_flags |= DUK_PROPDESC_FLAG_CONFIGURABLE;
                }

                duk__emit_a_b_c(comp_ctx,
                                DUK_OP_DECLVAR | DUK__EMIT_FLAG_NO_SHUFFLE_A | DUK__EMIT_FLAG_BC_REGCONST,
                                (duk_regconst_t) declvar_flags /*flags*/,
                                rc_name /*name*/,
                                0 /*value*/);
            }

            duk_put_prop(thr, comp_ctx->curr_func.varmap_idx);  /* [ ... name reg/null ] -> [ ... ] */
        }
    }

    /*
     *  Wrap up
     */

    DUK_DDD(DUK_DDDPRINT("varmap: %!T, is_arguments_shadowed=%ld",
                         (duk_tval *) duk_get_tval(thr, comp_ctx->curr_func.varmap_idx),
                         (long) comp_ctx->curr_func.is_arguments_shadowed));

    DUK_ASSERT_TOP(thr, entry_top);
    return;

 error_outofregs:
    DUK_ERROR_RANGE(thr, DUK_STR_REG_LIMIT);
    DUK_WO_NORETURN(return;);

 error_argname:
    DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_ARG_NAME);
    DUK_WO_NORETURN(return;);
}

/*
 *  Parse a function-body-like expression (FunctionBody or Program
 *  in E5 grammar) using a two-pass parse.  The productions appear
 *  in the following contexts:
 *
 *    - function expression
 *    - function statement
 *    - function declaration
 *    - getter in object literal
 *    - setter in object literal
 *    - global code
 *    - eval code
 *    - Function constructor body
 *
 *  This function only parses the statement list of the body; the argument
 *  list and possible function name must be initialized by the caller.
 *  For instance, for Function constructor, the argument names are originally
 *  on the value stack.  The parsing of statements ends either at an EOF or
 *  a closing brace; this is controlled by an input flag.
 *
 *  Note that there are many differences affecting parsing and even code
 *  generation:
 *
 *    - Global and eval code have an implicit return value generated
 *      by the last statement; function code does not
 *
 *    - Global code, eval code, and Function constructor body end in
 *      an EOF, other bodies in a closing brace ('}')
 *
 *  Upon entry, 'curr_tok' is ignored and the function will pull in the
 *  first token on its own.  Upon exit, 'curr_tok' is the terminating
 *  token (EOF or closing brace).
 */

DUK_LOCAL void duk__parse_func_body(duk_compiler_ctx *comp_ctx, duk_bool_t expect_eof, duk_bool_t implicit_return_value, duk_bool_t regexp_after, duk_small_int_t expect_token) {
    duk_compiler_func *func;
    duk_hthread *thr;
    duk_regconst_t reg_stmt_value = -1;
    duk_lexer_point lex_pt;
    duk_regconst_t temp_first;
    duk_small_int_t compile_round = 1;

    DUK_ASSERT(comp_ctx != NULL);

    thr = comp_ctx->thr;
    DUK_ASSERT(thr != NULL);

    func = &comp_ctx->curr_func;
    DUK_ASSERT(func != NULL);

    DUK__RECURSION_INCREASE(comp_ctx, thr);

    duk_require_stack(thr, DUK__FUNCTION_BODY_REQUIRE_SLOTS);

    /*
     *  Store lexer position for a later rewind
     */

    DUK_LEXER_GETPOINT(&comp_ctx->lex, &lex_pt);

    /*
     *  Program code (global and eval code) has an implicit return value
     *  from the last statement value (e.g. eval("1; 2+3;") returns 3).
     *  This is not the case with functions.  If implicit statement return
     *  value is requested, all statements are coerced to a register
     *  allocated here, and used in the implicit return statement below.
     */

    /* XXX: this is pointless here because pass 1 is throw-away */
    if (implicit_return_value) {
        reg_stmt_value = DUK__ALLOCTEMP(comp_ctx);

        /* If an implicit return value is needed by caller, it must be
         * initialized to 'undefined' because we don't know whether any
         * non-empty (where "empty" is a continuation type, and different
         * from an empty statement) statements will be executed.
         *
         * However, since 1st pass is a throwaway one, no need to emit
         * it here.
         */
#if 0
        duk__emit_bc(comp_ctx,
                     DUK_OP_LDUNDEF,
                     0);
#endif
    }

    /*
     *  First pass.
     *
     *  Gather variable/function declarations needed for second pass.
     *  Code generated is dummy and discarded.
     */

    func->in_directive_prologue = 1;
    func->in_scanning = 1;
    func->may_direct_eval = 0;
    func->id_access_arguments = 0;
    func->id_access_slow = 0;
    func->id_access_slow_own = 0;
    func->reg_stmt_value = reg_stmt_value;
#if defined(DUK_USE_DEBUGGER_SUPPORT)
    func->min_line = DUK_INT_MAX;
    func->max_line = 0;
#endif

    /* duk__parse_stmts() expects curr_tok to be set; parse in "allow
     * regexp literal" mode with current strictness.
     */
    if (expect_token >= 0) {
        /* Eating a left curly; regexp mode is allowed by left curly
         * based on duk__token_lbp[] automatically.
         */
        DUK_ASSERT(expect_token == DUK_TOK_LCURLY);
        duk__update_lineinfo_currtoken(comp_ctx);
        duk__advance_expect(comp_ctx, expect_token);
    } else {
        /* Need to set curr_token.t because lexing regexp mode depends on current
         * token type.  Zero value causes "allow regexp" mode.
         */
        comp_ctx->curr_token.t = 0;
        duk__advance(comp_ctx);
    }

    DUK_DDD(DUK_DDDPRINT("begin 1st pass"));
    duk__parse_stmts(comp_ctx,
                     1,             /* allow source elements */
                     expect_eof,    /* expect EOF instead of } */
                     regexp_after); /* regexp after */
    DUK_DDD(DUK_DDDPRINT("end 1st pass"));

    /*
     *  Second (and possibly third) pass.
     *
     *  Generate actual code.  In most cases the need for shuffle
     *  registers is detected during pass 1, but in some corner cases
     *  we'll only detect it during pass 2 and a third pass is then
     *  needed (see GH-115).
     */

    for (;;) {
        duk_bool_t needs_shuffle_before = comp_ctx->curr_func.needs_shuffle;
        compile_round++;

        /*
         *  Rewind lexer.
         *
         *  duk__parse_stmts() expects curr_tok to be set; parse in "allow regexp
         *  literal" mode with current strictness.
         *
         *  curr_token line number info should be initialized for pass 2 before
         *  generating prologue, to ensure prologue bytecode gets nice line numbers.
         */

        DUK_DDD(DUK_DDDPRINT("rewind lexer"));
        DUK_LEXER_SETPOINT(&comp_ctx->lex, &lex_pt);
        comp_ctx->curr_token.t = 0;  /* this is needed for regexp mode */
        comp_ctx->curr_token.start_line = 0;  /* needed for line number tracking (becomes prev_token.start_line) */
        duk__advance(comp_ctx);

        /*
         *  Reset function state and perform register allocation, which creates
         *  'varmap' for second pass.  Function prologue for variable declarations,
         *  binding value initializations etc is emitted as a by-product.
         *
         *  Strict mode restrictions for duplicate and invalid argument
         *  names are checked here now that we know whether the function
         *  is actually strict.  See: test-dev-strict-mode-boundary.js.
         *
         *  Inner functions are compiled during pass 1 and are not reset.
         */

        duk__reset_func_for_pass2(comp_ctx);
        func->in_directive_prologue = 1;
        func->in_scanning = 0;

        /* must be able to emit code, alloc consts, etc. */

        duk__init_varmap_and_prologue_for_pass2(comp_ctx,
                                                (implicit_return_value ? &reg_stmt_value : NULL));
        func->reg_stmt_value = reg_stmt_value;

        temp_first = DUK__GETTEMP(comp_ctx);

        func->temp_first = temp_first;
        func->temp_next = temp_first;
        func->stmt_next = 0;
        func->label_next = 0;

        /* XXX: init or assert catch depth etc -- all values */
        func->id_access_arguments = 0;
        func->id_access_slow = 0;
        func->id_access_slow_own = 0;

        /*
         *  Check function name validity now that we know strictness.
         *  This only applies to function declarations and expressions,
         *  not setter/getter name.
         *
         *  See: test-dev-strict-mode-boundary.js
         */

        if (func->is_function && !func->is_setget && func->h_name != NULL) {
            if (func->is_strict) {
                if (duk__hstring_is_eval_or_arguments(comp_ctx, func->h_name)) {
                    DUK_DDD(DUK_DDDPRINT("func name is 'eval' or 'arguments' in strict mode"));
                    goto error_funcname;
                }
                if (DUK_HSTRING_HAS_STRICT_RESERVED_WORD(func->h_name)) {
                    DUK_DDD(DUK_DDDPRINT("func name is a reserved word in strict mode"));
                    goto error_funcname;
                }
            } else {
                if (DUK_HSTRING_HAS_RESERVED_WORD(func->h_name) &&
                    !DUK_HSTRING_HAS_STRICT_RESERVED_WORD(func->h_name)) {
                    DUK_DDD(DUK_DDDPRINT("func name is a reserved word in non-strict mode"));
                    goto error_funcname;
                }
            }
        }

        /*
         *  Second pass parsing.
         */

        if (implicit_return_value) {
            /* Default implicit return value. */
            duk__emit_bc(comp_ctx,
                         DUK_OP_LDUNDEF,
                         0);
        }

        DUK_DDD(DUK_DDDPRINT("begin 2nd pass"));
        duk__parse_stmts(comp_ctx,
                         1,             /* allow source elements */
                         expect_eof,    /* expect EOF instead of } */
                         regexp_after); /* regexp after */
        DUK_DDD(DUK_DDDPRINT("end 2nd pass"));

        duk__update_lineinfo_currtoken(comp_ctx);

        if (needs_shuffle_before == comp_ctx->curr_func.needs_shuffle) {
            /* Shuffle decision not changed. */
            break;
        }
        if (compile_round >= 3) {
            /* Should never happen but avoid infinite loop just in case. */
            DUK_D(DUK_DPRINT("more than 3 compile passes needed, should never happen"));
            DUK_ERROR_INTERNAL(thr);
            DUK_WO_NORETURN(return;);
        }
        DUK_D(DUK_DPRINT("need additional round to compile function, round now %d", (int) compile_round));
    }

    /*
     *  Emit a final RETURN.
     *
     *  It would be nice to avoid emitting an unnecessary "return" opcode
     *  if the current PC is not reachable.  However, this cannot be reliably
     *  detected; even if the previous instruction is an unconditional jump,
     *  there may be a previous jump which jumps to current PC (which is the
     *  case for iteration and conditional statements, for instance).
     */

    /* XXX: request a "last statement is terminal" from duk__parse_stmt() and duk__parse_stmts();
     * we could avoid the last RETURN if we could ensure there is no way to get here
     * (directly or via a jump)
     */

    DUK_ASSERT(comp_ctx->curr_func.catch_depth == 0);
    if (reg_stmt_value >= 0) {
        DUK_ASSERT(DUK__ISREG(reg_stmt_value));
        duk__emit_bc(comp_ctx, DUK_OP_RETREG, reg_stmt_value /*reg*/);
    } else {
        duk__emit_op_only(comp_ctx, DUK_OP_RETUNDEF);
    }

    /*
     *  Peephole optimize JUMP chains.
     */

    duk__peephole_optimize_bytecode(comp_ctx);

    /*
     *  comp_ctx->curr_func is now ready to be converted into an actual
     *  function template.
     */

    DUK__RECURSION_DECREASE(comp_ctx, thr);
    return;

 error_funcname:
    DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_FUNC_NAME);
    DUK_WO_NORETURN(return;);
}

/*
 *  Parse a function-like expression:
 *
 *    - function expression
 *    - function declaration
 *    - function statement (non-standard)
 *    - setter/getter
 *
 *  Adds the function to comp_ctx->curr_func function table and returns the
 *  function number.
 *
 *  On entry, curr_token points to:
 *
 *    - the token after 'function' for function expression/declaration/statement
 *    - the token after 'set' or 'get' for setter/getter
 */

/* Parse formals. */
DUK_LOCAL void duk__parse_func_formals(duk_compiler_ctx *comp_ctx) {
    duk_hthread *thr = comp_ctx->thr;
    duk_bool_t first = 1;
    duk_uarridx_t n;

    for (;;) {
        if (comp_ctx->curr_token.t == DUK_TOK_RPAREN) {
            break;
        }

        if (first) {
            /* no comma */
            first = 0;
        } else {
            duk__advance_expect(comp_ctx, DUK_TOK_COMMA);
        }

        /* Note: when parsing a formal list in non-strict context, e.g.
         * "implements" is parsed as an identifier.  When the function is
         * later detected to be strict, the argument list must be rechecked
         * against a larger set of reserved words (that of strict mode).
         * This is handled by duk__parse_func_body().  Here we recognize
         * whatever tokens are considered reserved in current strictness
         * (which is not always enough).
         */

        if (comp_ctx->curr_token.t != DUK_TOK_IDENTIFIER) {
            DUK_ERROR_SYNTAX(thr, DUK_STR_EXPECTED_IDENTIFIER);
            DUK_WO_NORETURN(return;);
        }
        DUK_ASSERT(comp_ctx->curr_token.t == DUK_TOK_IDENTIFIER);
        DUK_ASSERT(comp_ctx->curr_token.str1 != NULL);
        DUK_DDD(DUK_DDDPRINT("formal argument: %!O",
                             (duk_heaphdr *) comp_ctx->curr_token.str1));

        /* XXX: append primitive */
        duk_push_hstring(thr, comp_ctx->curr_token.str1);
        n = (duk_uarridx_t) duk_get_length(thr, comp_ctx->curr_func.argnames_idx);
        duk_put_prop_index(thr, comp_ctx->curr_func.argnames_idx, n);

        duk__advance(comp_ctx);  /* eat identifier */
    }
}

/* Parse a function-like expression, assuming that 'comp_ctx->curr_func' is
 * correctly set up.  Assumes that curr_token is just after 'function' (or
 * 'set'/'get' etc).
 */
DUK_LOCAL void duk__parse_func_like_raw(duk_compiler_ctx *comp_ctx, duk_small_uint_t flags) {
    duk_hthread *thr = comp_ctx->thr;
    duk_token *tok;
    duk_bool_t no_advance;

    DUK_ASSERT(comp_ctx->curr_func.num_formals == 0);
    DUK_ASSERT(comp_ctx->curr_func.is_function == 1);
    DUK_ASSERT(comp_ctx->curr_func.is_eval == 0);
    DUK_ASSERT(comp_ctx->curr_func.is_global == 0);
    DUK_ASSERT(comp_ctx->curr_func.is_setget == ((flags & DUK__FUNC_FLAG_GETSET) != 0));

    duk__update_lineinfo_currtoken(comp_ctx);

    /*
     *  Function name (if any)
     *
     *  We don't check for prohibited names here, because we don't
     *  yet know whether the function will be strict.  Function body
     *  parsing handles this retroactively.
     *
     *  For function expressions and declarations function name must
     *  be an Identifer (excludes reserved words).  For setter/getter
     *  it is a PropertyName which allows reserved words and also
     *  strings and numbers (e.g. "{ get 1() { ... } }").
     *
     *  Function parsing may start either from prev_token or curr_token
     *  (object literal method definition uses prev_token for example).
     *  This is dealt with for the initial token.
     */

    no_advance = (flags & DUK__FUNC_FLAG_USE_PREVTOKEN);
    if (no_advance) {
        tok = &comp_ctx->prev_token;
    } else {
        tok = &comp_ctx->curr_token;
    }

    if (flags & DUK__FUNC_FLAG_GETSET) {
        /* PropertyName -> IdentifierName | StringLiteral | NumericLiteral */
        if (tok->t_nores == DUK_TOK_IDENTIFIER || tok->t == DUK_TOK_STRING) {
            duk_push_hstring(thr, tok->str1);       /* keep in valstack */
        } else if (tok->t == DUK_TOK_NUMBER) {
            duk_push_number(thr, tok->num);
            duk_to_string(thr, -1);
        } else {
            DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_GETSET_NAME);
            DUK_WO_NORETURN(return;);
        }
        comp_ctx->curr_func.h_name = duk_known_hstring(thr, -1);  /* borrowed reference */
    } else {
        /* Function name is an Identifier (not IdentifierName), but we get
         * the raw name (not recognizing keywords) here and perform the name
         * checks only after pass 1.
         */
        if (tok->t_nores == DUK_TOK_IDENTIFIER) {
            duk_push_hstring(thr, tok->str1);       /* keep in valstack */
            comp_ctx->curr_func.h_name = duk_known_hstring(thr, -1);  /* borrowed reference */
        } else {
            /* valstack will be unbalanced, which is OK */
            DUK_ASSERT((flags & DUK__FUNC_FLAG_GETSET) == 0);
            DUK_ASSERT(comp_ctx->curr_func.h_name == NULL);
            no_advance = 1;
            if (flags & DUK__FUNC_FLAG_DECL) {
                DUK_ERROR_SYNTAX(thr, DUK_STR_FUNC_NAME_REQUIRED);
                DUK_WO_NORETURN(return;);
            }
        }
    }

    DUK_DD(DUK_DDPRINT("function name: %!O",
                       (duk_heaphdr *) comp_ctx->curr_func.h_name));

    if (!no_advance) {
        duk__advance(comp_ctx);
    }

    /*
     *  Formal argument list
     *
     *  We don't check for prohibited names or for duplicate argument
     *  names here, becase we don't yet know whether the function will
     *  be strict.  Function body parsing handles this retroactively.
     */

    duk__advance_expect(comp_ctx, DUK_TOK_LPAREN);

    duk__parse_func_formals(comp_ctx);

    DUK_ASSERT(comp_ctx->curr_token.t == DUK_TOK_RPAREN);
    duk__advance(comp_ctx);

    /*
     *  Parse function body
     */

    duk__parse_func_body(comp_ctx,
                         0,   /* expect_eof */
                         0,   /* implicit_return_value */
                         flags & DUK__FUNC_FLAG_DECL, /* regexp_after */
                         DUK_TOK_LCURLY);  /* expect_token */

    /*
     *  Convert duk_compiler_func to a function template and add it
     *  to the parent function table.
     */

    duk__convert_to_func_template(comp_ctx);  /* -> [ ... func ] */
}

/* Parse an inner function, adding the function template to the current function's
 * function table.  Return a function number to be used by the outer function.
 *
 * Avoiding O(depth^2) inner function parsing is handled here.  On the first pass,
 * compile and register the function normally into the 'funcs' array, also recording
 * a lexer point (offset/line) to the closing brace of the function.  On the second
 * pass, skip the function and return the same 'fnum' as on the first pass by using
 * a running counter.
 *
 * An unfortunate side effect of this is that when parsing the inner function, almost
 * nothing is known of the outer function, i.e. the inner function's scope.  We don't
 * need that information at the moment, but it would allow some optimizations if it
 * were used.
 */
DUK_LOCAL duk_int_t duk__parse_func_like_fnum(duk_compiler_ctx *comp_ctx, duk_small_uint_t flags) {
    duk_hthread *thr = comp_ctx->thr;
    duk_compiler_func old_func;
    duk_idx_t entry_top;
    duk_int_t fnum;

    /*
     *  On second pass, skip the function.
     */

    if (!comp_ctx->curr_func.in_scanning) {
        duk_lexer_point lex_pt;

        fnum = comp_ctx->curr_func.fnum_next++;
        duk_get_prop_index(thr, comp_ctx->curr_func.funcs_idx, (duk_uarridx_t) (fnum * 3 + 1));
        lex_pt.offset = (duk_size_t) duk_to_uint(thr, -1);
        duk_pop(thr);
        duk_get_prop_index(thr, comp_ctx->curr_func.funcs_idx, (duk_uarridx_t) (fnum * 3 + 2));
        lex_pt.line = duk_to_int(thr, -1);
        duk_pop(thr);

        DUK_DDD(DUK_DDDPRINT("second pass of an inner func, skip the function, reparse closing brace; lex offset=%ld, line=%ld",
                             (long) lex_pt.offset, (long) lex_pt.line));

        DUK_LEXER_SETPOINT(&comp_ctx->lex, &lex_pt);
        comp_ctx->curr_token.t = 0;  /* this is needed for regexp mode */
        comp_ctx->curr_token.start_line = 0;  /* needed for line number tracking (becomes prev_token.start_line) */
        duk__advance(comp_ctx);

        /* RegExp is not allowed after a function expression, e.g. in
         * (function () {} / 123).  A RegExp *is* allowed after a
         * function declaration!
         */
        if (flags & DUK__FUNC_FLAG_DECL) {
            comp_ctx->curr_func.allow_regexp_in_adv = 1;
        }
        duk__advance_expect(comp_ctx, DUK_TOK_RCURLY);

        return fnum;
    }

    /*
     *  On first pass, perform actual parsing.  Remember valstack top on entry
     *  to restore it later, and switch to using a new function in comp_ctx.
     */

    entry_top = duk_get_top(thr);
    DUK_DDD(DUK_DDDPRINT("before func: entry_top=%ld, curr_tok.start_offset=%ld",
                         (long) entry_top, (long) comp_ctx->curr_token.start_offset));

    duk_memcpy(&old_func, &comp_ctx->curr_func, sizeof(duk_compiler_func));

    duk_memzero(&comp_ctx->curr_func, sizeof(duk_compiler_func));
    duk__init_func_valstack_slots(comp_ctx);
    DUK_ASSERT(comp_ctx->curr_func.num_formals == 0);

    /* inherit initial strictness from parent */
    comp_ctx->curr_func.is_strict = old_func.is_strict;

    /* XXX: It might be better to just store the flags into the curr_func
     * struct and use them as is without this flag interpretation step
     * here.
     */
    DUK_ASSERT(comp_ctx->curr_func.is_notail == 0);
    comp_ctx->curr_func.is_function = 1;
    DUK_ASSERT(comp_ctx->curr_func.is_eval == 0);
    DUK_ASSERT(comp_ctx->curr_func.is_global == 0);
    comp_ctx->curr_func.is_setget = ((flags & DUK__FUNC_FLAG_GETSET) != 0);
    comp_ctx->curr_func.is_namebinding = !(flags & (DUK__FUNC_FLAG_GETSET |
                                                    DUK__FUNC_FLAG_METDEF |
                                                    DUK__FUNC_FLAG_DECL));  /* no name binding for: declarations, objlit getset, objlit method def */
    comp_ctx->curr_func.is_constructable = !(flags & (DUK__FUNC_FLAG_GETSET |
                                                      DUK__FUNC_FLAG_METDEF));  /* not constructable: objlit getset, objlit method def */

    /*
     *  Parse inner function
     */

    duk__parse_func_like_raw(comp_ctx, flags);  /* pushes function template */

    /* prev_token.start_offset points to the closing brace here; when skipping
     * we're going to reparse the closing brace to ensure semicolon insertion
     * etc work as expected.
     */
    DUK_DDD(DUK_DDDPRINT("after func: prev_tok.start_offset=%ld, curr_tok.start_offset=%ld",
                         (long) comp_ctx->prev_token.start_offset, (long) comp_ctx->curr_token.start_offset));
    DUK_ASSERT(comp_ctx->lex.input[comp_ctx->prev_token.start_offset] == (duk_uint8_t) DUK_ASC_RCURLY);

    /* XXX: append primitive */
    DUK_ASSERT(duk_get_length(thr, old_func.funcs_idx) == (duk_size_t) (old_func.fnum_next * 3));
    fnum = old_func.fnum_next++;

    if (fnum > DUK__MAX_FUNCS) {
        DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_FUNC_LIMIT);
        DUK_WO_NORETURN(return 0;);
    }

    /* array writes autoincrement length */
    (void) duk_put_prop_index(thr, old_func.funcs_idx, (duk_uarridx_t) (fnum * 3));
    duk_push_size_t(thr, comp_ctx->prev_token.start_offset);
    (void) duk_put_prop_index(thr, old_func.funcs_idx, (duk_uarridx_t) (fnum * 3 + 1));
    duk_push_int(thr, comp_ctx->prev_token.start_line);
    (void) duk_put_prop_index(thr, old_func.funcs_idx, (duk_uarridx_t) (fnum * 3 + 2));

    /*
     *  Cleanup: restore original function, restore valstack state.
     *
     *  Function declaration handling needs the function name to be pushed
     *  on the value stack.
     */

    if (flags & DUK__FUNC_FLAG_PUSHNAME_PASS1) {
        DUK_ASSERT(comp_ctx->curr_func.h_name != NULL);
        duk_push_hstring(thr, comp_ctx->curr_func.h_name);
        duk_replace(thr, entry_top);
        duk_set_top(thr, entry_top + 1);
    } else {
        duk_set_top(thr, entry_top);
    }
    duk_memcpy((void *) &comp_ctx->curr_func, (void *) &old_func, sizeof(duk_compiler_func));

    return fnum;
}

/*
 *  Compile input string into an executable function template without
 *  arguments.
 *
 *  The string is parsed as the "Program" production of ECMAScript E5.
 *  Compilation context can be either global code or eval code (see E5
 *  Sections 14 and 15.1.2.1).
 *
 *  Input stack:  [ ... filename ]
 *  Output stack: [ ... func_template ]
 */

/* XXX: source code property */

DUK_LOCAL duk_ret_t duk__js_compile_raw(duk_hthread *thr, void *udata) {
    duk_hstring *h_filename;
    duk__compiler_stkstate *comp_stk;
    duk_compiler_ctx *comp_ctx;
    duk_lexer_point *lex_pt;
    duk_compiler_func *func;
    duk_idx_t entry_top;
    duk_bool_t is_strict;
    duk_bool_t is_eval;
    duk_bool_t is_funcexpr;
    duk_small_uint_t flags;

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

    /*
     *  Arguments check
     */

    entry_top = duk_get_top(thr);
    DUK_ASSERT(entry_top >= 1);

    comp_stk = (duk__compiler_stkstate *) udata;
    comp_ctx = &comp_stk->comp_ctx_alloc;
    lex_pt = &comp_stk->lex_pt_alloc;
    DUK_ASSERT(comp_ctx != NULL);
    DUK_ASSERT(lex_pt != NULL);

    flags = comp_stk->flags;
    is_eval = (flags & DUK_COMPILE_EVAL ? 1 : 0);
    is_strict = (flags & DUK_COMPILE_STRICT ? 1 : 0);
    is_funcexpr = (flags & DUK_COMPILE_FUNCEXPR ? 1 : 0);

    h_filename = duk_get_hstring(thr, -1);  /* may be undefined */

    /*
     *  Init compiler and lexer contexts
     */

    func = &comp_ctx->curr_func;
#if defined(DUK_USE_EXPLICIT_NULL_INIT)
    comp_ctx->thr = NULL;
    comp_ctx->h_filename = NULL;
    comp_ctx->prev_token.str1 = NULL;
    comp_ctx->prev_token.str2 = NULL;
    comp_ctx->curr_token.str1 = NULL;
    comp_ctx->curr_token.str2 = NULL;
#endif

    duk_require_stack(thr, DUK__COMPILE_ENTRY_SLOTS);

    duk_push_dynamic_buffer(thr, 0);       /* entry_top + 0 */
    duk_push_undefined(thr);               /* entry_top + 1 */
    duk_push_undefined(thr);               /* entry_top + 2 */
    duk_push_undefined(thr);               /* entry_top + 3 */
    duk_push_undefined(thr);               /* entry_top + 4 */

    comp_ctx->thr = thr;
    comp_ctx->h_filename = h_filename;
    comp_ctx->tok11_idx = entry_top + 1;
    comp_ctx->tok12_idx = entry_top + 2;
    comp_ctx->tok21_idx = entry_top + 3;
    comp_ctx->tok22_idx = entry_top + 4;
    comp_ctx->recursion_limit = DUK_USE_COMPILER_RECLIMIT;

    /* comp_ctx->lex has been pre-initialized by caller: it has been
     * zeroed and input/input_length has been set.
     */
    comp_ctx->lex.thr = thr;
    /* comp_ctx->lex.input and comp_ctx->lex.input_length filled by caller */
    comp_ctx->lex.slot1_idx = comp_ctx->tok11_idx;
    comp_ctx->lex.slot2_idx = comp_ctx->tok12_idx;
    comp_ctx->lex.buf_idx = entry_top + 0;
    comp_ctx->lex.buf = (duk_hbuffer_dynamic *) duk_known_hbuffer(thr, entry_top + 0);
    DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(comp_ctx->lex.buf) && !DUK_HBUFFER_HAS_EXTERNAL(comp_ctx->lex.buf));
    comp_ctx->lex.token_limit = DUK_COMPILER_TOKEN_LIMIT;

    lex_pt->offset = 0;
    lex_pt->line = 1;
    DUK_LEXER_SETPOINT(&comp_ctx->lex, lex_pt);    /* fills window */
    comp_ctx->curr_token.start_line = 0;  /* needed for line number tracking (becomes prev_token.start_line) */

    /*
     *  Initialize function state for a zero-argument function
     */

    duk__init_func_valstack_slots(comp_ctx);
    DUK_ASSERT(func->num_formals == 0);

    if (is_funcexpr) {
        /* Name will be filled from function expression, not by caller.
         * This case is used by Function constructor and duk_compile()
         * API with the DUK_COMPILE_FUNCTION option.
         */
        DUK_ASSERT(func->h_name == NULL);
    } else {
        duk_push_hstring_stridx(thr, (is_eval ? DUK_STRIDX_EVAL :
                                                DUK_STRIDX_GLOBAL));
        func->h_name = duk_get_hstring(thr, -1);
    }

    /*
     *  Parse a function body or a function-like expression, depending
     *  on flags.
     */

    DUK_ASSERT(func->is_setget == 0);
    func->is_strict = (duk_uint8_t) is_strict;
    DUK_ASSERT(func->is_notail == 0);

    if (is_funcexpr) {
        func->is_function = 1;
        DUK_ASSERT(func->is_eval == 0);
        DUK_ASSERT(func->is_global == 0);
        func->is_namebinding = 1;
        func->is_constructable = 1;

        duk__advance(comp_ctx);  /* init 'curr_token' */
        duk__advance_expect(comp_ctx, DUK_TOK_FUNCTION);
        (void) duk__parse_func_like_raw(comp_ctx, 0 /*flags*/);
    } else {
        DUK_ASSERT(func->is_function == 0);
        DUK_ASSERT(is_eval == 0 || is_eval == 1);
        func->is_eval = (duk_uint8_t) is_eval;
        func->is_global = (duk_uint8_t) !is_eval;
        DUK_ASSERT(func->is_namebinding == 0);
        DUK_ASSERT(func->is_constructable == 0);

        duk__parse_func_body(comp_ctx,
                             1,             /* expect_eof */
                             1,             /* implicit_return_value */
                             1,             /* regexp_after (does not matter) */
                             -1);           /* expect_token */
    }

    /*
     *  Convert duk_compiler_func to a function template
     */

    duk__convert_to_func_template(comp_ctx);

    /*
     *  Wrapping duk_safe_call() will mangle the stack, just return stack top
     */

    /* [ ... filename (temps) func ] */

    return 1;
}

DUK_INTERNAL void duk_js_compile(duk_hthread *thr, const duk_uint8_t *src_buffer, duk_size_t src_length, duk_small_uint_t flags) {
    duk__compiler_stkstate comp_stk;
    duk_compiler_ctx *prev_ctx;
    duk_ret_t safe_rc;

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

    /* preinitialize lexer state partially */
    duk_memzero(&comp_stk, sizeof(comp_stk));
    comp_stk.flags = flags;
    DUK_LEXER_INITCTX(&comp_stk.comp_ctx_alloc.lex);
    comp_stk.comp_ctx_alloc.lex.input = src_buffer;
    comp_stk.comp_ctx_alloc.lex.input_length = src_length;
    comp_stk.comp_ctx_alloc.lex.flags = flags;  /* Forward flags directly for now. */

    /* [ ... filename ] */

    prev_ctx = thr->compile_ctx;
    thr->compile_ctx = &comp_stk.comp_ctx_alloc;  /* for duk_error_augment.c */
    safe_rc = duk_safe_call(thr, duk__js_compile_raw, (void *) &comp_stk /*udata*/, 1 /*nargs*/, 1 /*nrets*/);
    thr->compile_ctx = prev_ctx;  /* must restore reliably before returning */

    if (safe_rc != DUK_EXEC_SUCCESS) {
        DUK_D(DUK_DPRINT("compilation failed: %!T", duk_get_tval(thr, -1)));
        (void) duk_throw(thr);
        DUK_WO_NORETURN(return;);
    }

    /* [ ... template ] */
}

/* automatic undefs */
#undef DUK__ALLOCTEMP
#undef DUK__ALLOCTEMPS
#undef DUK__ALLOW_AUTO_SEMI_ALWAYS
#undef DUK__BC_INITIAL_INSTS
#undef DUK__BP_ADDITIVE
#undef DUK__BP_ASSIGNMENT
#undef DUK__BP_BAND
#undef DUK__BP_BOR
#undef DUK__BP_BXOR
#undef DUK__BP_CALL
#undef DUK__BP_CLOSING
#undef DUK__BP_COMMA
#undef DUK__BP_CONDITIONAL
#undef DUK__BP_EOF
#undef DUK__BP_EQUALITY
#undef DUK__BP_EXPONENTIATION
#undef DUK__BP_FOR_EXPR
#undef DUK__BP_INVALID
#undef DUK__BP_LAND
#undef DUK__BP_LOR
#undef DUK__BP_MEMBER
#undef DUK__BP_MULTIPLICATIVE
#undef DUK__BP_POSTFIX
#undef DUK__BP_RELATIONAL
#undef DUK__BP_SHIFT
#undef DUK__COMPILE_ENTRY_SLOTS
#undef DUK__CONST_MARKER
#undef DUK__DUMP_ISPEC
#undef DUK__DUMP_IVALUE
#undef DUK__EMIT_FLAG_A_IS_SOURCE
#undef DUK__EMIT_FLAG_BC_REGCONST
#undef DUK__EMIT_FLAG_B_IS_TARGET
#undef DUK__EMIT_FLAG_C_IS_TARGET
#undef DUK__EMIT_FLAG_NO_SHUFFLE_A
#undef DUK__EMIT_FLAG_NO_SHUFFLE_B
#undef DUK__EMIT_FLAG_NO_SHUFFLE_C
#undef DUK__EMIT_FLAG_RESERVE_JUMPSLOT
#undef DUK__EXPR_FLAG_ALLOW_EMPTY
#undef DUK__EXPR_FLAG_REJECT_IN
#undef DUK__EXPR_FLAG_REQUIRE_INIT
#undef DUK__EXPR_RBP_MASK
#undef DUK__FUNCTION_BODY_REQUIRE_SLOTS
#undef DUK__FUNCTION_INIT_REQUIRE_SLOTS
#undef DUK__FUNC_FLAG_DECL
#undef DUK__FUNC_FLAG_GETSET
#undef DUK__FUNC_FLAG_METDEF
#undef DUK__FUNC_FLAG_PUSHNAME_PASS1
#undef DUK__FUNC_FLAG_USE_PREVTOKEN
#undef DUK__GETCONST_MAX_CONSTS_CHECK
#undef DUK__GETTEMP
#undef DUK__HAS_TERM
#undef DUK__HAS_VAL
#undef DUK__ISCONST
#undef DUK__ISREG
#undef DUK__ISREG_NOTTEMP
#undef DUK__ISREG_TEMP
#undef DUK__IS_TERMINAL
#undef DUK__IVAL_FLAG_ALLOW_CONST
#undef DUK__IVAL_FLAG_REQUIRE_SHORT
#undef DUK__IVAL_FLAG_REQUIRE_TEMP
#undef DUK__MAX_ARRAY_INIT_VALUES
#undef DUK__MAX_CONSTS
#undef DUK__MAX_FUNCS
#undef DUK__MAX_OBJECT_INIT_PAIRS
#undef DUK__MAX_TEMPS
#undef DUK__MK_LBP
#undef DUK__MK_LBP_FLAGS
#undef DUK__OBJ_LIT_KEY_GET
#undef DUK__OBJ_LIT_KEY_PLAIN
#undef DUK__OBJ_LIT_KEY_SET
#undef DUK__PARSE_EXPR_SLOTS
#undef DUK__PARSE_STATEMENTS_SLOTS
#undef DUK__RECURSION_DECREASE
#undef DUK__RECURSION_INCREASE
#undef DUK__REMOVECONST
#undef DUK__SETTEMP
#undef DUK__SETTEMP_CHECKMAX
#undef DUK__STILL_PROLOGUE
#undef DUK__TOKEN_LBP_BP_MASK
#undef DUK__TOKEN_LBP_FLAG_NO_REGEXP
#undef DUK__TOKEN_LBP_FLAG_TERMINATES
#undef DUK__TOKEN_LBP_FLAG_UNUSED
#undef DUK__TOKEN_LBP_GET_BP
//#line 1 "duk_js_executor.c"
/*
 *  ECMAScript bytecode executor.
 */

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

/*
 *  Local declarations.
 */

DUK_LOCAL_DECL void duk__js_execute_bytecode_inner(duk_hthread *entry_thread, duk_activation *entry_act);

/*
 *  Misc helpers.
 */

/* Replace value stack top to value at 'tv_ptr'.  Optimize for
 * performance by only applying the net refcount change.
 */
#define DUK__REPLACE_TO_TVPTR(thr,tv_ptr) do { \
        duk_hthread *duk__thr; \
        duk_tval *duk__tvsrc; \
        duk_tval *duk__tvdst; \
        duk_tval duk__tvtmp; \
        duk__thr = (thr); \
        duk__tvsrc = DUK_GET_TVAL_NEGIDX(duk__thr, -1); \
        duk__tvdst = (tv_ptr); \
        DUK_TVAL_SET_TVAL(&duk__tvtmp, duk__tvdst); \
        DUK_TVAL_SET_TVAL(duk__tvdst, duk__tvsrc); \
        DUK_TVAL_SET_UNDEFINED(duk__tvsrc);  /* value stack init policy */ \
        duk__thr->valstack_top = duk__tvsrc; \
        DUK_TVAL_DECREF(duk__thr, &duk__tvtmp); \
    } while (0)

/* XXX: candidate of being an internal shared API call */
#if 0  /* unused */
DUK_LOCAL void duk__push_tvals_incref_only(duk_hthread *thr, duk_tval *tv_src, duk_small_uint_fast_t count) {
    duk_tval *tv_dst;
    duk_size_t copy_size;
    duk_size_t i;

    tv_dst = thr->valstack_top;
    copy_size = sizeof(duk_tval) * count;
    duk_memcpy((void *) tv_dst, (const void *) tv_src, copy_size);
    for (i = 0; i < count; i++) {
        DUK_TVAL_INCREF(thr, tv_dst);
        tv_dst++;
    }
    thr->valstack_top = tv_dst;
}
#endif

/*
 *  Arithmetic, binary, and logical helpers.
 *
 *  Note: there is no opcode for logical AND or logical OR; this is on
 *  purpose, because the evalution order semantics for them make such
 *  opcodes pretty pointless: short circuiting means they are most
 *  comfortably implemented as jumps.  However, a logical NOT opcode
 *  is useful.
 *
 *  Note: careful with duk_tval pointers here: they are potentially
 *  invalidated by any DECREF and almost any API call.  It's still
 *  preferable to work without making a copy but that's not always
 *  possible.
 */

DUK_LOCAL DUK_EXEC_ALWAYS_INLINE_PERF duk_double_t duk__compute_mod(duk_double_t d1, duk_double_t d2) {
    return (duk_double_t) duk_js_arith_mod((double) d1, (double) d2);
}

#if defined(DUK_USE_ES7_EXP_OPERATOR)
DUK_LOCAL DUK_EXEC_ALWAYS_INLINE_PERF duk_double_t duk__compute_exp(duk_double_t d1, duk_double_t d2) {
    return (duk_double_t) duk_js_arith_pow((double) d1, (double) d2);
}
#endif

DUK_LOCAL DUK_EXEC_ALWAYS_INLINE_PERF void duk__vm_arith_add(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_uint_fast_t idx_z) {
    /*
     *  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_EXEC_ALWAYS_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).
             */
            if (v1 == 0 || v2 == 0) {
                goto skip_fastint;
            }
            v3 = v1 / v2;
            if (v3 * v2 != v1) {
                goto skip_fastint;
            }
            break;
        }
        case DUK_OP_MOD >> 2: {
            /* Don't allow a zero divisor.  Restrict both v1 and
             * v2 to positive values to avoid compiler specific
             * behavior.
             */
            if (v1 < 1 || v2 < 1) {
                goto skip_fastint;
            }
            v3 = v1 % v2;
            DUK_ASSERT(v3 >= 0);
            DUK_ASSERT(v3 < v2);
            DUK_ASSERT(v1 - (v1 / v2) * v2 == v3);
            break;
        }
        default: {
            /* Possible with DUK_OP_EXP. */
            goto skip_fastint;
        }
        }

        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;
        }
        /* 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_EXEC_ALWAYS_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;
        break;
    }
    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_EXEC_ALWAYS_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_EXEC_ALWAYS_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_EXEC_ALWAYS_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.
     */
    tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_src);
    res = duk_js_toboolean(tv);  /* does not modify 'tv' */
    DUK_ASSERT(res == 0 || res == 1);
    res ^= 1;
    tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_dst);
    /* XXX: size optimize: push+replace? */
    DUK_TVAL_SET_BOOLEAN_UPDREF(thr, tv, res);  /* side effects */
}

/* XXX: size optimized variant */
DUK_LOCAL DUK_EXEC_ALWAYS_INLINE_PERF void duk__prepost_incdec_reg_helper(duk_hthread *thr, duk_tval *tv_dst, duk_tval *tv_src, duk_small_uint_t op) {
    duk_double_t x, y, z;

    /* Two lowest bits of opcode are used to distinguish
     * variants.  Bit 0 = inc(0)/dec(1), bit 1 = pre(0)/post(1).
     */
    DUK_ASSERT((DUK_OP_PREINCR & 0x03) == 0x00);
    DUK_ASSERT((DUK_OP_PREDECR & 0x03) == 0x01);
    DUK_ASSERT((DUK_OP_POSTINCR & 0x03) == 0x02);
    DUK_ASSERT((DUK_OP_POSTDECR & 0x03) == 0x03);

#if defined(DUK_USE_FASTINT)
    if (DUK_TVAL_IS_FASTINT(tv_src)) {
        duk_int64_t x_fi, y_fi, z_fi;
        x_fi = DUK_TVAL_GET_FASTINT(tv_src);
        if (op & 0x01) {
            if (DUK_UNLIKELY(x_fi == DUK_FASTINT_MIN)) {
                goto skip_fastint;
            }
            y_fi = x_fi - 1;
        } else {
            if (DUK_UNLIKELY(x_fi == DUK_FASTINT_MAX)) {
                goto skip_fastint;
            }
            y_fi = x_fi + 1;
        }

        DUK_TVAL_SET_FASTINT(tv_src, y_fi);  /* no need for refcount update */

        z_fi = (op & 0x02) ? x_fi : y_fi;
        DUK_TVAL_SET_FASTINT_UPDREF(thr, tv_dst, z_fi);  /* side effects */
        return;
    }
 skip_fastint:
#endif
    if (DUK_TVAL_IS_NUMBER(tv_src)) {
        /* Fast path for the case where the register
         * is a number (e.g. loop counter).
         */

        x = DUK_TVAL_GET_NUMBER(tv_src);
        if (op & 0x01) {
            y = x - 1.0;
        } else {
            y = x + 1.0;
        }

        DUK_TVAL_SET_NUMBER(tv_src, y);  /* no need for refcount update */
    } else {
        /* Preserve duk_tval pointer(s) across a potential valstack
         * resize by converting them into offsets temporarily.
         */
        duk_idx_t bc;
        duk_size_t off_dst;

        off_dst = (duk_size_t) ((duk_uint8_t *) tv_dst - (duk_uint8_t *) thr->valstack_bottom);
        bc = (duk_idx_t) (tv_src - thr->valstack_bottom);  /* XXX: pass index explicitly? */
        tv_src = NULL;  /* no longer referenced */

        x = duk_to_number(thr, bc);
        if (op & 0x01) {
            y = x - 1.0;
        } else {
            y = x + 1.0;
        }

        duk_push_number(thr, y);
        duk_replace(thr, bc);

        tv_dst = (duk_tval *) (void *) (((duk_uint8_t *) thr->valstack_bottom) + off_dst);
    }

    z = (op & 0x02) ? x : y;
    DUK_TVAL_SET_NUMBER_UPDREF(thr, tv_dst, z);  /* side effects */
}

DUK_LOCAL DUK_EXEC_ALWAYS_INLINE_PERF void duk__prepost_incdec_var_helper(duk_hthread *thr, duk_small_uint_t idx_dst, duk_tval *tv_id, duk_small_uint_t op, duk_small_uint_t is_strict) {
    duk_activation *act;
    duk_double_t x, y;
    duk_hstring *name;

    /* XXX: The pre/post inc/dec for an identifier lookup is
     * missing the important fast path where the identifier
     * has a storage location e.g. in a scope object so that
     * it can be updated in-place.  In particular, the case
     * where the identifier has a storage location AND the
     * previous value is a number should be optimized because
     * it's side effect free.
     */

    /* Two lowest bits of opcode are used to distinguish
     * variants.  Bit 0 = inc(0)/dec(1), bit 1 = pre(0)/post(1).
     */
    DUK_ASSERT((DUK_OP_PREINCV & 0x03) == 0x00);
    DUK_ASSERT((DUK_OP_PREDECV & 0x03) == 0x01);
    DUK_ASSERT((DUK_OP_POSTINCV & 0x03) == 0x02);
    DUK_ASSERT((DUK_OP_POSTDECV & 0x03) == 0x03);

    DUK_ASSERT(DUK_TVAL_IS_STRING(tv_id));
    name = DUK_TVAL_GET_STRING(tv_id);
    DUK_ASSERT(name != NULL);
    act = thr->callstack_curr;
    (void) duk_js_getvar_activation(thr, act, name, 1 /*throw*/);  /* -> [ ... val this ] */

    /* XXX: Fastint fast path would be useful here.  Also fastints
     * now lose their fastint status in current handling which is
     * not intuitive.
     */

    x = duk_to_number_m2(thr);
    if (op & 0x01) {
        y = x - 1.0;
    } else {
        y = x + 1.0;
    }

    /* [... x this] */

    if (op & 0x02) {
        duk_push_number(thr, y);  /* -> [ ... x this y ] */
        DUK_ASSERT(act == thr->callstack_curr);
        duk_js_putvar_activation(thr, act, name, DUK_GET_TVAL_NEGIDX(thr, -1), is_strict);
        duk_pop_2_unsafe(thr);  /* -> [ ... x ] */
    } else {
        duk_pop_2_unsafe(thr);  /* -> [ ... ] */
        duk_push_number(thr, y);  /* -> [ ... y ] */
        DUK_ASSERT(act == thr->callstack_curr);
        duk_js_putvar_activation(thr, act, name, DUK_GET_TVAL_NEGIDX(thr, -1), is_strict);
    }

#if defined(DUK_USE_EXEC_PREFER_SIZE)
    duk_replace(thr, (duk_idx_t) idx_dst);
#else  /* DUK_USE_EXEC_PREFER_SIZE */
    DUK__REPLACE_TO_TVPTR(thr, DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_dst));
#endif  /* DUK_USE_EXEC_PREFER_SIZE */
}

/*
 *  Longjmp and other control flow transfer for the bytecode executor.
 *
 *  The longjmp handler can handle all longjmp types: error, yield, and
 *  resume (pseudotypes are never actually thrown).
 *
 *  Error policy for longjmp: should not ordinarily throw errors; if errors
 *  occur (e.g. due to out-of-memory) they bubble outwards rather than being
 *  handled recursively.
 */

#define DUK__LONGJMP_RESTART   0  /* state updated, restart bytecode execution */
#define DUK__LONGJMP_RETHROW   1  /* exit bytecode executor by rethrowing an error to caller */

#define DUK__RETHAND_RESTART   0  /* state updated, restart bytecode execution */
#define DUK__RETHAND_FINISHED  1  /* exit bytecode execution with return value */

/* XXX: optimize reconfig valstack operations so that resize, clamp, and setting
 * top are combined into one pass.
 */

/* Reconfigure value stack for return to an ECMAScript function at
 * callstack top (caller unwinds).
 */
DUK_LOCAL void duk__reconfig_valstack_ecma_return(duk_hthread *thr) {
    duk_activation *act;
    duk_hcompfunc *h_func;
    duk_idx_t clamp_top;

    DUK_ASSERT(thr != NULL);
    act = thr->callstack_curr;
    DUK_ASSERT(act != NULL);
    DUK_ASSERT(DUK_ACT_GET_FUNC(act) != NULL);
    DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(act)));

    /* Clamp so that values at 'clamp_top' and above are wiped and won't
     * retain reachable garbage.  Then extend to 'nregs' because we're
     * returning to an ECMAScript function.
     */

    h_func = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act);

    thr->valstack_bottom = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + act->bottom_byteoff);
    DUK_ASSERT(act->retval_byteoff >= act->bottom_byteoff);
    clamp_top = (duk_idx_t) ((act->retval_byteoff - act->bottom_byteoff + sizeof(duk_tval)) / sizeof(duk_tval));  /* +1 = one retval */
    duk_set_top_and_wipe(thr, h_func->nregs, clamp_top);

    DUK_ASSERT((duk_uint8_t *) thr->valstack_end >= (duk_uint8_t *) thr->valstack + act->reserve_byteoff);
    thr->valstack_end = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + act->reserve_byteoff);

    /* XXX: a best effort shrink check would be OK here */
}

/* Reconfigure value stack for an ECMAScript catcher.  Use topmost catcher
 * in 'act'.
 */
DUK_LOCAL void duk__reconfig_valstack_ecma_catcher(duk_hthread *thr, duk_activation *act) {
    duk_catcher *cat;
    duk_hcompfunc *h_func;
    duk_size_t idx_bottom;
    duk_idx_t clamp_top;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(act != NULL);
    DUK_ASSERT(DUK_ACT_GET_FUNC(act) != NULL);
    DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(act)));
    cat = act->cat;
    DUK_ASSERT(cat != NULL);

    h_func = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act);

    thr->valstack_bottom = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + act->bottom_byteoff);
    idx_bottom = (duk_size_t) (thr->valstack_bottom - thr->valstack);
    DUK_ASSERT(cat->idx_base >= idx_bottom);
    clamp_top = (duk_idx_t) (cat->idx_base - idx_bottom + 2);  /* +2 = catcher value, catcher lj_type */
    duk_set_top_and_wipe(thr, h_func->nregs, clamp_top);

    DUK_ASSERT((duk_uint8_t *) thr->valstack_end >= (duk_uint8_t *) thr->valstack + act->reserve_byteoff);
    thr->valstack_end = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + act->reserve_byteoff);

    /* XXX: a best effort shrink check would be OK here */
}

/* Set catcher regs: idx_base+0 = value, idx_base+1 = lj_type.
 * No side effects.
 */
DUK_LOCAL void duk__set_catcher_regs_norz(duk_hthread *thr, duk_catcher *cat, duk_tval *tv_val_unstable, duk_small_uint_t lj_type) {
    duk_tval *tv1;

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

    tv1 = thr->valstack + cat->idx_base;
    DUK_ASSERT(tv1 < thr->valstack_top);
    DUK_TVAL_SET_TVAL_UPDREF_NORZ(thr, tv1, tv_val_unstable);

    tv1++;
    DUK_ASSERT(tv1 == thr->valstack + cat->idx_base + 1);
    DUK_ASSERT(tv1 < thr->valstack_top);
    DUK_TVAL_SET_U32_UPDREF_NORZ(thr, tv1, (duk_uint32_t) lj_type);
}

DUK_LOCAL void duk__handle_catch_part1(duk_hthread *thr, duk_tval *tv_val_unstable, duk_small_uint_t lj_type, volatile duk_bool_t *out_delayed_catch_setup) {
    duk_activation *act;
    duk_catcher *cat;

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

    act = thr->callstack_curr;
    DUK_ASSERT(act != NULL);
    DUK_DD(DUK_DDPRINT("handle catch, part 1; act=%!A, cat=%!C", act, act->cat));

    DUK_ASSERT(act->cat != NULL);
    DUK_ASSERT(DUK_CAT_GET_TYPE(act->cat) == DUK_CAT_TYPE_TCF);

    /* The part1/part2 split could also be made here at the very top
     * of catch handling.  Value stack would be reconfigured inside
     * part2's protection.  Value stack reconfiguration should be free
     * of allocs, however.
     */

    duk__set_catcher_regs_norz(thr, act->cat, tv_val_unstable, lj_type);

    DUK_ASSERT(thr->callstack_top >= 1);
    DUK_ASSERT(thr->callstack_curr != NULL);
    DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL);
    DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr)));

    DUK_ASSERT(thr->callstack_top >= 1);
    DUK_ASSERT(act == thr->callstack_curr);
    DUK_ASSERT(act != NULL);
    duk__reconfig_valstack_ecma_catcher(thr, act);

    DUK_ASSERT(thr->callstack_top >= 1);
    DUK_ASSERT(act == thr->callstack_curr);
    DUK_ASSERT(act != NULL);
    cat = act->cat;
    DUK_ASSERT(cat != NULL);

    act->curr_pc = cat->pc_base + 0;  /* +0 = catch */

    /*
     *  If the catch block has an automatic catch variable binding,
     *  we need to create a lexical environment for it which requires
     *  allocations.  Move out of "error handling state" before the
     *  allocations to avoid e.g. out-of-memory errors (leading to
     *  GH-2022 or similar).
     */

    if (DUK_CAT_HAS_CATCH_BINDING_ENABLED(cat)) {
        DUK_DDD(DUK_DDDPRINT("catcher has an automatic catch binding, handle in part 2"));
        *out_delayed_catch_setup = 1;
    } else {
        DUK_DDD(DUK_DDDPRINT("catcher has no catch binding"));
    }

    DUK_CAT_CLEAR_CATCH_ENABLED(cat);
}

DUK_LOCAL void duk__handle_catch_part2(duk_hthread *thr) {
    duk_activation *act;
    duk_catcher *cat;
    duk_hdecenv *new_env;

    DUK_ASSERT(thr != NULL);

    act = thr->callstack_curr;
    DUK_ASSERT(act != NULL);
    DUK_DD(DUK_DDPRINT("handle catch, part 2; act=%!A, cat=%!C", act, act->cat));

    DUK_ASSERT(act->cat != NULL);
    cat = act->cat;
    DUK_ASSERT(cat != NULL);
    DUK_ASSERT(DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_TCF);
    DUK_ASSERT(DUK_CAT_HAS_CATCH_BINDING_ENABLED(cat));
    DUK_ASSERT(thr->valstack + cat->idx_base < thr->valstack_top);

    /*
     *  Create lexical environment for the catch clause, containing
     *  a binding for the caught value.
     *
     *  The binding is mutable (= writable) but not deletable.
     *  Step 4 for the catch production in E5 Section 12.14;
     *  no value is given for CreateMutableBinding 'D' argument,
     *  which implies the binding is not deletable.
     */

    if (act->lex_env == NULL) {
        DUK_ASSERT(act->var_env == NULL);
        DUK_DDD(DUK_DDDPRINT("delayed environment initialization"));

        duk_js_init_activation_environment_records_delayed(thr, act);
        DUK_ASSERT(act == thr->callstack_curr);
        DUK_ASSERT(act != NULL);
    }
    DUK_ASSERT(act->lex_env != NULL);
    DUK_ASSERT(act->var_env != NULL);
    DUK_ASSERT(DUK_ACT_GET_FUNC(act) != NULL);

    new_env = duk_hdecenv_alloc(thr,
                                DUK_HOBJECT_FLAG_EXTENSIBLE |
                                DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV));
    DUK_ASSERT(new_env != NULL);
    duk_push_hobject(thr, (duk_hobject *) new_env);
    DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) new_env) == NULL);
    DUK_DDD(DUK_DDDPRINT("new_env allocated: %!iO", (duk_heaphdr *) new_env));

    /* Note: currently the catch binding is handled without a register
     * binding because we don't support dynamic register bindings (they
     * must be fixed for an entire function).  So, there is no need to
     * record regbases etc.
     */

    /* [ ...env ] */

    DUK_ASSERT(cat->h_varname != NULL);
    duk_push_hstring(thr, cat->h_varname);
    DUK_ASSERT(thr->valstack + cat->idx_base < thr->valstack_top);
    duk_push_tval(thr, thr->valstack + cat->idx_base);
    duk_xdef_prop(thr, -3, DUK_PROPDESC_FLAGS_W);  /* writable, not configurable */

    /* [ ... env ] */

    DUK_ASSERT(act == thr->callstack_curr);
    DUK_ASSERT(act != NULL);
    DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) new_env, act->lex_env);
    act->lex_env = (duk_hobject *) new_env;
    DUK_HOBJECT_INCREF(thr, (duk_hobject *) new_env);  /* reachable through activation */
    /* Net refcount change to act->lex_env is 0: incref for new_env's
     * prototype, decref for act->lex_env overwrite.
     */

    DUK_CAT_SET_LEXENV_ACTIVE(cat);

    duk_pop_unsafe(thr);

    DUK_DDD(DUK_DDDPRINT("new_env finished: %!iO", (duk_heaphdr *) new_env));
}

DUK_LOCAL void duk__handle_finally(duk_hthread *thr, duk_tval *tv_val_unstable, duk_small_uint_t lj_type) {
    duk_activation *act;
    duk_catcher *cat;

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

    act = thr->callstack_curr;
    DUK_ASSERT(act != NULL);
    DUK_ASSERT(act->cat != NULL);
    DUK_ASSERT(DUK_CAT_GET_TYPE(act->cat) == DUK_CAT_TYPE_TCF);

    duk__set_catcher_regs_norz(thr, act->cat, tv_val_unstable, lj_type);

    DUK_ASSERT(thr->callstack_top >= 1);
    DUK_ASSERT(thr->callstack_curr != NULL);
    DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL);
    DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr)));

    DUK_ASSERT(thr->callstack_top >= 1);
    DUK_ASSERT(act == thr->callstack_curr);
    DUK_ASSERT(act != NULL);
    duk__reconfig_valstack_ecma_catcher(thr, act);

    DUK_ASSERT(thr->callstack_top >= 1);
    DUK_ASSERT(act == thr->callstack_curr);
    DUK_ASSERT(act != NULL);
    cat = act->cat;
    DUK_ASSERT(cat != NULL);

    act->curr_pc = cat->pc_base + 1;  /* +1 = finally */

    DUK_CAT_CLEAR_FINALLY_ENABLED(cat);
}

DUK_LOCAL void duk__handle_label(duk_hthread *thr, duk_small_uint_t lj_type) {
    duk_activation *act;
    duk_catcher *cat;

    DUK_ASSERT(thr != NULL);

    DUK_ASSERT(thr->callstack_top >= 1);
    act = thr->callstack_curr;
    DUK_ASSERT(act != NULL);
    DUK_ASSERT(DUK_ACT_GET_FUNC(act) != NULL);
    DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(DUK_ACT_GET_FUNC(act)));

    /* +0 = break, +1 = continue */
    cat = act->cat;
    DUK_ASSERT(cat != NULL);
    DUK_ASSERT(DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_LABEL);

    act->curr_pc = cat->pc_base + (lj_type == DUK_LJ_TYPE_CONTINUE ? 1 : 0);

    /* valstack should not need changes */
#if defined(DUK_USE_ASSERTIONS)
    DUK_ASSERT(thr->callstack_top >= 1);
    DUK_ASSERT(act == thr->callstack_curr);
    DUK_ASSERT(act != NULL);
    DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) ==
               (duk_size_t) ((duk_hcompfunc *) DUK_ACT_GET_FUNC(act))->nregs);
#endif
}

/* Called for handling both a longjmp() with type DUK_LJ_TYPE_YIELD and
 * when a RETURN opcode terminates a thread and yields to the resumer.
 * Caller unwinds so that top of callstack is the activation we return to.
 */
#if defined(DUK_USE_COROUTINE_SUPPORT)
DUK_LOCAL void duk__handle_yield(duk_hthread *thr, duk_hthread *resumer, duk_tval *tv_val_unstable) {
    duk_activation *act_resumer;
    duk_tval *tv1;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(resumer != NULL);
    DUK_ASSERT(tv_val_unstable != NULL);
    act_resumer = resumer->callstack_curr;
    DUK_ASSERT(act_resumer != NULL);
    DUK_ASSERT(DUK_ACT_GET_FUNC(act_resumer) != NULL);
    DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(act_resumer)));  /* resume caller must be an ECMAScript func */

    tv1 = (duk_tval *) (void *) ((duk_uint8_t *) resumer->valstack + act_resumer->retval_byteoff);  /* return value from Duktape.Thread.resume() */
    DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv_val_unstable);  /* side effects */  /* XXX: avoid side effects */

    duk__reconfig_valstack_ecma_return(resumer);

    /* caller must change active thread, and set thr->resumer to NULL */
}
#endif  /* DUK_USE_COROUTINE_SUPPORT */

DUK_LOCAL duk_small_uint_t duk__handle_longjmp(duk_hthread *thr, duk_activation *entry_act, volatile duk_bool_t *out_delayed_catch_setup) {
    duk_small_uint_t retval = DUK__LONGJMP_RESTART;

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

    /* 'thr' is the current thread, as no-one resumes except us and we
     * switch 'thr' in that case.
     */
    DUK_ASSERT(thr == thr->heap->curr_thread);

    /*
     *  (Re)try handling the longjmp.
     *
     *  A longjmp handler may convert the longjmp to a different type and
     *  "virtually" rethrow by goto'ing to 'check_longjmp'.  Before the goto,
     *  the following must be updated:
     *    - the heap 'lj' state
     *    - 'thr' must reflect the "throwing" thread
     */

 check_longjmp:

    DUK_DD(DUK_DDPRINT("handling longjmp: type=%ld, value1=%!T, value2=%!T, iserror=%ld, top=%ld",
                       (long) thr->heap->lj.type,
                       (duk_tval *) &thr->heap->lj.value1,
                       (duk_tval *) &thr->heap->lj.value2,
                       (long) thr->heap->lj.iserror,
               (long) duk_get_top(thr)));

    switch (thr->heap->lj.type) {

#if defined(DUK_USE_COROUTINE_SUPPORT)
    case DUK_LJ_TYPE_RESUME: {
        /*
         *  Note: lj.value1 is 'value', lj.value2 is 'resumee'.
         *  This differs from YIELD.
         */

        duk_tval *tv;
        duk_tval *tv2;
        duk_hthread *resumee;

        /* duk_bi_duk_object_yield() and duk_bi_duk_object_resume() ensure all of these are met */

        DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);                                                         /* unchanged by Duktape.Thread.resume() */
        DUK_ASSERT(thr->callstack_top >= 2);                                                                         /* ECMAScript activation + Duktape.Thread.resume() activation */
        DUK_ASSERT(thr->callstack_curr != NULL);
        DUK_ASSERT(thr->callstack_curr->parent != NULL);
        DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL &&
                   DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr)) &&
                   ((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->callstack_curr))->func == duk_bi_thread_resume);

        tv = &thr->heap->lj.value2;  /* resumee */
        DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv));
        DUK_ASSERT(DUK_TVAL_GET_OBJECT(tv) != NULL);
        DUK_ASSERT(DUK_HOBJECT_IS_THREAD(DUK_TVAL_GET_OBJECT(tv)));
        resumee = (duk_hthread *) DUK_TVAL_GET_OBJECT(tv);

        DUK_ASSERT(resumee != NULL);
        DUK_ASSERT(resumee->resumer == NULL);
        DUK_ASSERT(resumee->state == DUK_HTHREAD_STATE_INACTIVE ||
                   resumee->state == DUK_HTHREAD_STATE_YIELDED);                                                     /* checked by Duktape.Thread.resume() */
        DUK_ASSERT(resumee->state != DUK_HTHREAD_STATE_YIELDED ||
                   resumee->callstack_top >= 2);                                                                     /* YIELDED: ECMAScript activation + Duktape.Thread.yield() activation */
        DUK_ASSERT(resumee->state != DUK_HTHREAD_STATE_YIELDED ||
                   (DUK_ACT_GET_FUNC(resumee->callstack_curr) != NULL &&
                    DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(resumee->callstack_curr)) &&
                    ((duk_hnatfunc *) DUK_ACT_GET_FUNC(resumee->callstack_curr))->func == duk_bi_thread_yield));
        DUK_ASSERT(resumee->state != DUK_HTHREAD_STATE_INACTIVE ||
                   resumee->callstack_top == 0);                                                                     /* INACTIVE: no activation, single function value on valstack */

        if (thr->heap->lj.iserror) {
            /*
             *  Throw the error in the resumed thread's context; the
             *  error value is pushed onto the resumee valstack.
             *
             *  Note: the callstack of the target may empty in this case
             *  too (i.e. the target thread has never been resumed).  The
             *  value stack will contain the initial function in that case,
             *  which we simply ignore.
             */

            DUK_ASSERT(resumee->resumer == NULL);
            resumee->resumer = thr;
            DUK_HTHREAD_INCREF(thr, thr);
            resumee->state = DUK_HTHREAD_STATE_RUNNING;
            thr->state = DUK_HTHREAD_STATE_RESUMED;
            DUK_HEAP_SWITCH_THREAD(thr->heap, resumee);
            thr = resumee;

            thr->heap->lj.type = DUK_LJ_TYPE_THROW;

            /* thr->heap->lj.value1 is already the value to throw */
            /* thr->heap->lj.value2 is 'thread', will be wiped out at the end */

            DUK_ASSERT(thr->heap->lj.iserror);  /* already set */

            DUK_DD(DUK_DDPRINT("-> resume with an error, converted to a throw in the resumee, propagate"));
            goto check_longjmp;
        } else if (resumee->state == DUK_HTHREAD_STATE_YIELDED) {
            /* Unwind previous Duktape.Thread.yield() call.  The
             * activation remaining must always be an ECMAScript
             * call now (yield() accepts calls from ECMAScript
             * only).
             */
            duk_activation *act_resumee;

            DUK_ASSERT(resumee->callstack_top >= 2);
            act_resumee = resumee->callstack_curr;  /* Duktape.Thread.yield() */
            DUK_ASSERT(act_resumee != NULL);
            act_resumee = act_resumee->parent;      /* ECMAScript call site for yield() */
            DUK_ASSERT(act_resumee != NULL);

            tv = (duk_tval *) (void *) ((duk_uint8_t *) resumee->valstack + act_resumee->retval_byteoff);  /* return value from Duktape.Thread.yield() */
            DUK_ASSERT(tv >= resumee->valstack && tv < resumee->valstack_top);
            tv2 = &thr->heap->lj.value1;
            DUK_TVAL_SET_TVAL_UPDREF(thr, tv, tv2);  /* side effects */  /* XXX: avoid side effects */

            duk_hthread_activation_unwind_norz(resumee);  /* unwind to 'yield' caller */
            /* no need to unwind catch stack */

            duk__reconfig_valstack_ecma_return(resumee);

            DUK_ASSERT(resumee->resumer == NULL);
            resumee->resumer = thr;
            DUK_HTHREAD_INCREF(thr, thr);
            resumee->state = DUK_HTHREAD_STATE_RUNNING;
            thr->state = DUK_HTHREAD_STATE_RESUMED;
            DUK_HEAP_SWITCH_THREAD(thr->heap, resumee);
#if 0
            thr = resumee;  /* not needed, as we exit right away */
#endif
            DUK_DD(DUK_DDPRINT("-> resume with a value, restart execution in resumee"));
            retval = DUK__LONGJMP_RESTART;
            goto wipe_and_return;
        } else {
            /* Initial resume call. */
            duk_small_uint_t call_flags;
            duk_int_t setup_rc;

            /* resumee: [... initial_func]  (currently actually: [initial_func]) */

            duk_push_undefined(resumee);
            tv = &thr->heap->lj.value1;
            duk_push_tval(resumee, tv);

            /* resumee: [... initial_func undefined(= this) resume_value ] */

            call_flags = DUK_CALL_FLAG_ALLOW_ECMATOECMA;  /* not tailcall, ecma-to-ecma (assumed to succeed) */

            setup_rc = duk_handle_call_unprotected_nargs(resumee, 1 /*nargs*/, call_flags);
            if (setup_rc == 0) {
                /* This shouldn't happen; Duktape.Thread.resume()
                 * should make sure of that.  If it does happen
                 * this internal error will propagate out of the
                 * executor which can be quite misleading.
                 */
                DUK_ERROR_INTERNAL(thr);
                DUK_WO_NORETURN(return 0;);
            }

            DUK_ASSERT(resumee->resumer == NULL);
            resumee->resumer = thr;
            DUK_HTHREAD_INCREF(thr, thr);
            resumee->state = DUK_HTHREAD_STATE_RUNNING;
            thr->state = DUK_HTHREAD_STATE_RESUMED;
            DUK_HEAP_SWITCH_THREAD(thr->heap, resumee);
#if 0
            thr = resumee;  /* not needed, as we exit right away */
#endif
            DUK_DD(DUK_DDPRINT("-> resume with a value, restart execution in resumee"));
            retval = DUK__LONGJMP_RESTART;
            goto wipe_and_return;
        }
        DUK_UNREACHABLE();
        break;  /* never here */
    }

    case DUK_LJ_TYPE_YIELD: {
        /*
         *  Currently only allowed only if yielding thread has only
         *  ECMAScript activations (except for the Duktape.Thread.yield()
         *  call at the callstack top) and none of them constructor
         *  calls.
         *
         *  This excludes the 'entry' thread which will always have
         *  a preventcount > 0.
         */

        duk_hthread *resumer;

        /* duk_bi_duk_object_yield() and duk_bi_duk_object_resume() ensure all of these are met */

#if 0  /* entry_thread not available for assert */
        DUK_ASSERT(thr != entry_thread);                                                                             /* Duktape.Thread.yield() should prevent */
#endif
        DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);                                                         /* unchanged from Duktape.Thread.yield() */
        DUK_ASSERT(thr->callstack_top >= 2);                                                                         /* ECMAScript activation + Duktape.Thread.yield() activation */
        DUK_ASSERT(thr->callstack_curr != NULL);
        DUK_ASSERT(thr->callstack_curr->parent != NULL);
        DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL &&
                   DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr)) &&
                   ((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->callstack_curr))->func == duk_bi_thread_yield);
        DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr->parent) != NULL &&
                   DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr->parent)));                              /* an ECMAScript function */

        resumer = thr->resumer;

        DUK_ASSERT(resumer != NULL);
        DUK_ASSERT(resumer->state == DUK_HTHREAD_STATE_RESUMED);                                                     /* written by a previous RESUME handling */
        DUK_ASSERT(resumer->callstack_top >= 2);                                                                     /* ECMAScript activation + Duktape.Thread.resume() activation */
        DUK_ASSERT(resumer->callstack_curr != NULL);
        DUK_ASSERT(resumer->callstack_curr->parent != NULL);
        DUK_ASSERT(DUK_ACT_GET_FUNC(resumer->callstack_curr) != NULL &&
                   DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(resumer->callstack_curr)) &&
                   ((duk_hnatfunc *) DUK_ACT_GET_FUNC(resumer->callstack_curr))->func == duk_bi_thread_resume);
        DUK_ASSERT(DUK_ACT_GET_FUNC(resumer->callstack_curr->parent) != NULL &&
                   DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(resumer->callstack_curr->parent)));                            /* an ECMAScript function */

        if (thr->heap->lj.iserror) {
            thr->state = DUK_HTHREAD_STATE_YIELDED;
            thr->resumer = NULL;
            DUK_HTHREAD_DECREF_NORZ(thr, resumer);
            resumer->state = DUK_HTHREAD_STATE_RUNNING;
            DUK_HEAP_SWITCH_THREAD(thr->heap, resumer);
            thr = resumer;

            thr->heap->lj.type = DUK_LJ_TYPE_THROW;
            /* lj.value1 is already set */
            DUK_ASSERT(thr->heap->lj.iserror);  /* already set */

            DUK_DD(DUK_DDPRINT("-> yield an error, converted to a throw in the resumer, propagate"));
            goto check_longjmp;
        } else {
            /* When handling the yield, the last reference to
             * 'thr' may disappear.
             */

            DUK_GC_TORTURE(resumer->heap);
            duk_hthread_activation_unwind_norz(resumer);
            DUK_GC_TORTURE(resumer->heap);
            thr->state = DUK_HTHREAD_STATE_YIELDED;
            thr->resumer = NULL;
            DUK_HTHREAD_DECREF_NORZ(thr, resumer);
            resumer->state = DUK_HTHREAD_STATE_RUNNING;
            DUK_HEAP_SWITCH_THREAD(thr->heap, resumer);
            duk__handle_yield(thr, resumer, &thr->heap->lj.value1);
            thr = resumer;
            DUK_GC_TORTURE(resumer->heap);

            DUK_DD(DUK_DDPRINT("-> yield a value, restart execution in resumer"));
            retval = DUK__LONGJMP_RESTART;
            goto wipe_and_return;
        }
        DUK_UNREACHABLE();
        break;  /* never here */
    }
#endif  /* DUK_USE_COROUTINE_SUPPORT */

    case DUK_LJ_TYPE_THROW: {
        /*
         *  Three possible outcomes:
         *    * A try or finally catcher is found => resume there.
         *      (or)
         *    * The error propagates to the bytecode executor entry
         *      level (and we're in the entry thread) => rethrow
         *      with a new longjmp(), after restoring the previous
         *      catchpoint.
         *    * The error is not caught in the current thread, so
         *      the thread finishes with an error.  This works like
         *      a yielded error, except that the thread is finished
         *      and can no longer be resumed.  (There is always a
         *      resumer in this case.)
         *
         *  Note: until we hit the entry level, there can only be
         *  ECMAScript activations.
         */

        duk_activation *act;
        duk_catcher *cat;
        duk_hthread *resumer;

        for (;;) {
            act = thr->callstack_curr;
            if (act == NULL) {
                break;
            }

            for (;;) {
                cat = act->cat;
                if (cat == NULL) {
                    break;
                }

                if (DUK_CAT_HAS_CATCH_ENABLED(cat)) {
                    DUK_ASSERT(DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_TCF);

                    DUK_DDD(DUK_DDDPRINT("before catch part 1: thr=%p, act=%p, cat=%p",
                                         (void *) thr, (void *) act, (void *) act->cat));
                    duk__handle_catch_part1(thr,
                                            &thr->heap->lj.value1,
                                            DUK_LJ_TYPE_THROW,
                                    out_delayed_catch_setup);

                    DUK_DD(DUK_DDPRINT("-> throw caught by a 'catch' clause, restart execution"));
                    retval = DUK__LONGJMP_RESTART;
                    goto wipe_and_return;
                }

                if (DUK_CAT_HAS_FINALLY_ENABLED(cat)) {
                    DUK_ASSERT(DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_TCF);
                    DUK_ASSERT(!DUK_CAT_HAS_CATCH_ENABLED(cat));

                    duk__handle_finally(thr,
                                        &thr->heap->lj.value1,
                                        DUK_LJ_TYPE_THROW);

                    DUK_DD(DUK_DDPRINT("-> throw caught by a 'finally' clause, restart execution"));
                    retval = DUK__LONGJMP_RESTART;
                    goto wipe_and_return;
                }

                duk_hthread_catcher_unwind_norz(thr, act);
            }

            if (act == entry_act) {
                /* Not caught by anything before entry level; rethrow and let the
                 * final catcher finish unwinding (esp. value stack).
                 */
                DUK_D(DUK_DPRINT("-> throw propagated up to entry level, rethrow and exit bytecode executor"));
                retval = DUK__LONGJMP_RETHROW;
                goto just_return;
            }

            duk_hthread_activation_unwind_norz(thr);
        }

        DUK_DD(DUK_DDPRINT("-> throw not caught by current thread, yield error to resumer and recheck longjmp"));

        /* Not caught by current thread, thread terminates (yield error to resumer);
         * note that this may cause a cascade if the resumer terminates with an uncaught
         * exception etc (this is OK, but needs careful testing).
         */

        DUK_ASSERT(thr->resumer != NULL);
        DUK_ASSERT(thr->resumer->callstack_top >= 2);  /* ECMAScript activation + Duktape.Thread.resume() activation */
        DUK_ASSERT(thr->resumer->callstack_curr != NULL);
        DUK_ASSERT(thr->resumer->callstack_curr->parent != NULL);
        DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr->parent) != NULL &&
                   DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr->parent)));  /* an ECMAScript function */

        resumer = thr->resumer;

        /* reset longjmp */

        DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_THROW);  /* already set */
        /* lj.value1 already set */

        duk_hthread_terminate(thr);  /* updates thread state, minimizes its allocations */
        DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_TERMINATED);

        thr->resumer = NULL;
        DUK_HTHREAD_DECREF_NORZ(thr, resumer);
        resumer->state = DUK_HTHREAD_STATE_RUNNING;
        DUK_HEAP_SWITCH_THREAD(thr->heap, resumer);
        thr = resumer;
        goto check_longjmp;
    }

    case DUK_LJ_TYPE_BREAK:  /* pseudotypes, not used in actual longjmps */
    case DUK_LJ_TYPE_CONTINUE:
    case DUK_LJ_TYPE_RETURN:
    case DUK_LJ_TYPE_NORMAL:
    default: {
        /* should never happen, but be robust */
        DUK_D(DUK_DPRINT("caught unknown longjmp type %ld, treat as internal error", (long) thr->heap->lj.type));
        goto convert_to_internal_error;
    }

    }  /* end switch */

    DUK_UNREACHABLE();

 wipe_and_return:
    DUK_DD(DUK_DDPRINT("handling longjmp done, wipe-and-return, top=%ld",
                       (long) duk_get_top(thr)));
    thr->heap->lj.type = DUK_LJ_TYPE_UNKNOWN;
    thr->heap->lj.iserror = 0;

    DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value1);  /* side effects */
    DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value2);  /* side effects */

    DUK_GC_TORTURE(thr->heap);

 just_return:
    return retval;

 convert_to_internal_error:
    /* This could also be thrown internally (set the error, goto check_longjmp),
     * but it's better for internal errors to bubble outwards so that we won't
     * infinite loop in this catchpoint.
     */
    DUK_ERROR_INTERNAL(thr);
    DUK_WO_NORETURN(return 0;);
}

/* Handle a BREAK/CONTINUE opcode.  Avoid using longjmp() for BREAK/CONTINUE
 * handling because it has a measurable performance impact in ordinary
 * environments and an extreme impact in Emscripten (GH-342).
 */
DUK_LOCAL DUK_EXEC_NOINLINE_PERF void duk__handle_break_or_continue(duk_hthread *thr,
                                                                    duk_uint_t label_id,
                                                                    duk_small_uint_t lj_type) {
    duk_activation *act;
    duk_catcher *cat;

    DUK_ASSERT(thr != NULL);

    /* Find a matching label catcher or 'finally' catcher in
     * the same function, unwinding catchers as we go.
     *
     * A label catcher must always exist and will match unless
     * a 'finally' captures the break/continue first.  It is the
     * compiler's responsibility to ensure that labels are used
     * correctly.
     */

    act = thr->callstack_curr;
    DUK_ASSERT(act != NULL);

    for (;;) {
        cat = act->cat;
        if (cat == NULL) {
            break;
        }

        DUK_DDD(DUK_DDDPRINT("considering catcher %p: type=%ld label=%ld",
                             (void *) cat,
                             (long) DUK_CAT_GET_TYPE(cat),
                             (long) DUK_CAT_GET_LABEL(cat)));

        /* XXX: bit mask test; FINALLY <-> TCF, single bit mask would suffice? */

        if (DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_TCF &&
            DUK_CAT_HAS_FINALLY_ENABLED(cat)) {
            duk_tval tv_tmp;

            DUK_TVAL_SET_U32(&tv_tmp, (duk_uint32_t) label_id);
            duk__handle_finally(thr, &tv_tmp, lj_type);

            DUK_DD(DUK_DDPRINT("-> break/continue caught by 'finally', restart execution"));
            return;
        }
        if (DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_LABEL &&
            (duk_uint_t) DUK_CAT_GET_LABEL(cat) == label_id) {
            duk__handle_label(thr, lj_type);

            DUK_DD(DUK_DDPRINT("-> break/continue caught by a label catcher (in the same function), restart execution"));
            return;
        }

        duk_hthread_catcher_unwind_norz(thr, act);
    }

    /* Should never happen, but be robust. */
    DUK_D(DUK_DPRINT("-> break/continue not caught by anything in the current function (should never happen), throw internal error"));
    DUK_ERROR_INTERNAL(thr);
    DUK_WO_NORETURN(return;);
}

/* Handle a RETURN opcode.  Avoid using longjmp() for return handling because
 * it has a measurable performance impact in ordinary environments and an extreme
 * impact in Emscripten (GH-342).  Return value is on value stack top.
 */
DUK_LOCAL duk_small_uint_t duk__handle_return(duk_hthread *thr, duk_activation *entry_act) {
    duk_tval *tv1;
    duk_tval *tv2;
#if defined(DUK_USE_COROUTINE_SUPPORT)
    duk_hthread *resumer;
#endif
    duk_activation *act;
    duk_catcher *cat;

    /* We can directly access value stack here. */

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(entry_act != NULL);
    DUK_ASSERT(thr->valstack_top - 1 >= thr->valstack_bottom);
    tv1 = thr->valstack_top - 1;
    DUK_TVAL_CHKFAST_INPLACE_FAST(tv1);  /* fastint downgrade check for return values */

    /*
     *  Four possible outcomes:
     *
     *    1. A 'finally' in the same function catches the 'return'.
     *       It may continue to propagate when 'finally' is finished,
     *       or it may be neutralized by 'finally' (both handled by
     *       ENDFIN).
     *
     *    2. The return happens at the entry level of the bytecode
     *       executor, so return from the executor (in C stack).
     *
     *    3. There is a calling (ECMAScript) activation in the call
     *       stack => return to it, in the same executor instance.
     *
     *    4. There is no calling activation, and the thread is
     *       terminated.  There is always a resumer in this case,
     *       which gets the return value similarly to a 'yield'
     *       (except that the current thread can no longer be
     *       resumed).
     */

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->callstack_top >= 1);

    act = thr->callstack_curr;
    DUK_ASSERT(act != NULL);

    for (;;) {
        cat = act->cat;
        if (cat == NULL) {
            break;
        }

        if (DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_TCF &&
            DUK_CAT_HAS_FINALLY_ENABLED(cat)) {
            DUK_ASSERT(thr->valstack_top - 1 >= thr->valstack_bottom);
            duk__handle_finally(thr, thr->valstack_top - 1, DUK_LJ_TYPE_RETURN);

            DUK_DD(DUK_DDPRINT("-> return caught by 'finally', restart execution"));
            return DUK__RETHAND_RESTART;
        }

        duk_hthread_catcher_unwind_norz(thr, act);
    }

    if (act == entry_act) {
        /* Return to the bytecode executor caller who will unwind stacks
         * and handle constructor post-processing.
         * Return value is already on the stack top: [ ... retval ].
         */

        DUK_DDD(DUK_DDDPRINT("-> return propagated up to entry level, exit bytecode executor"));
        return DUK__RETHAND_FINISHED;
    }

    if (thr->callstack_top >= 2) {
        /* There is a caller; it MUST be an ECMAScript caller (otherwise it would
         * match entry_act check).
         */
        DUK_DDD(DUK_DDDPRINT("return to ECMAScript caller, retval_byteoff=%ld, lj_value1=%!T",
                             (long) (thr->callstack_curr->parent->retval_byteoff),
                             (duk_tval *) &thr->heap->lj.value1));

        DUK_ASSERT(thr->callstack_curr != NULL);
        DUK_ASSERT(thr->callstack_curr->parent != NULL);
        DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr->parent)));   /* must be ECMAScript */

#if defined(DUK_USE_ES6_PROXY)
        if (thr->callstack_curr->flags & (DUK_ACT_FLAG_CONSTRUCT | DUK_ACT_FLAG_CONSTRUCT_PROXY)) {
            duk_call_construct_postprocess(thr, thr->callstack_curr->flags & DUK_ACT_FLAG_CONSTRUCT_PROXY);  /* side effects */
        }
#else
        if (thr->callstack_curr->flags & DUK_ACT_FLAG_CONSTRUCT) {
            duk_call_construct_postprocess(thr, 0);  /* side effects */
        }
#endif

        tv1 = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + thr->callstack_curr->parent->retval_byteoff);
        DUK_ASSERT(thr->valstack_top - 1 >= thr->valstack_bottom);
        tv2 = thr->valstack_top - 1;
        DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv2);  /* side effects */

        /* Catch stack unwind happens inline in callstack unwind. */
        duk_hthread_activation_unwind_norz(thr);

        duk__reconfig_valstack_ecma_return(thr);

        DUK_DD(DUK_DDPRINT("-> return not intercepted, restart execution in caller"));
        return DUK__RETHAND_RESTART;
    }

#if defined(DUK_USE_COROUTINE_SUPPORT)
    DUK_DD(DUK_DDPRINT("no calling activation, thread finishes (similar to yield)"));

    DUK_ASSERT(thr->resumer != NULL);
    DUK_ASSERT(thr->resumer->callstack_top >= 2);  /* ECMAScript activation + Duktape.Thread.resume() activation */
    DUK_ASSERT(thr->resumer->callstack_curr != NULL);
    DUK_ASSERT(thr->resumer->callstack_curr->parent != NULL);
    DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr) != NULL &&
            DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr)) &&
            ((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->resumer->callstack_curr))->func == duk_bi_thread_resume);  /* Duktape.Thread.resume() */
    DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr->parent) != NULL &&
            DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr->parent)));  /* an ECMAScript function */
    DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
    DUK_ASSERT(thr->resumer->state == DUK_HTHREAD_STATE_RESUMED);

    resumer = thr->resumer;

    /* Share yield longjmp handler.
     *
     * This sequence of steps is a bit fragile (see GH-1845):
     * - We need the return value from 'thr' (resumed thread) value stack.
     *   The termination unwinds its value stack, losing the value.
     * - We need a refcounted reference for 'thr', which may only exist
     *   in the caller value stack.  We can't unwind or reconfigure the
     *   caller's value stack without potentially freeing 'thr'.
     *
     * Current approach is to capture the 'thr' return value and store
     * a reference to 'thr' in the caller value stack temporarily.  This
     * keeps 'thr' reachable until final yield/return handling which
     * removes the references atomatically.
     */

    DUK_ASSERT(thr->valstack_top - 1 >= thr->valstack_bottom);
    duk_hthread_activation_unwind_norz(resumer);  /* May remove last reference to 'thr', but is NORZ. */
    duk_push_tval(resumer, thr->valstack_top - 1);  /* Capture return value, side effect free. */
    duk_push_hthread(resumer, thr);  /* Make 'thr' reachable again, before side effects. */

    duk_hthread_terminate(thr);  /* Updates thread state, minimizes its allocations. */
    thr->resumer = NULL;
    DUK_HTHREAD_DECREF(thr, resumer);
    DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_TERMINATED);

    resumer->state = DUK_HTHREAD_STATE_RUNNING;
    DUK_HEAP_SWITCH_THREAD(thr->heap, resumer);

    DUK_ASSERT(resumer->valstack_top - 2 >= resumer->valstack_bottom);
    duk__handle_yield(thr, resumer, resumer->valstack_top - 2);
    thr = NULL;  /* 'thr' invalidated by call */

#if 0
    thr = resumer;  /* not needed */
#endif

    DUK_DD(DUK_DDPRINT("-> return not caught, thread terminated; handle like yield, restart execution in resumer"));
    return DUK__RETHAND_RESTART;
#else
    /* Without coroutine support this case should never happen. */
    DUK_ERROR_INTERNAL(thr);
    DUK_WO_NORETURN(return 0;);
#endif
}

/*
 *  Executor interrupt handling
 *
 *  The handler is called whenever the interrupt countdown reaches zero
 *  (or below).  The handler must perform whatever checks are activated,
 *  e.g. check for cumulative step count to impose an execution step
 *  limit or check for breakpoints or other debugger interaction.
 *
 *  When the actions are done, the handler must reinit the interrupt
 *  init and counter values.  The 'init' value must indicate how many
 *  bytecode instructions are executed before the next interrupt.  The
 *  counter must interface with the bytecode executor loop.  Concretely,
 *  the new init value is normally one higher than the new counter value.
 *  For instance, to execute exactly one bytecode instruction the init
 *  value is set to 1 and the counter to 0.  If an error is thrown by the
 *  interrupt handler, the counters are set to the same value (e.g. both
 *  to 0 to cause an interrupt when the next bytecode instruction is about
 *  to be executed after error handling).
 *
 *  Maintaining the init/counter value properly is important for accurate
 *  behavior.  For instance, executor step limit needs a cumulative step
 *  count which is simply computed as a sum of 'init' values.  This must
 *  work accurately even when single stepping.
 */

#if defined(DUK_USE_INTERRUPT_COUNTER)

#define DUK__INT_NOACTION    0    /* no specific action, resume normal execution */
#define DUK__INT_RESTART     1    /* must "goto restart_execution", e.g. breakpoints changed */

#if defined(DUK_USE_DEBUGGER_SUPPORT)
DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_immediate, duk_small_uint_t *out_interrupt_retval) {
    duk_activation *act;
    duk_breakpoint *bp;
    duk_breakpoint **bp_active;
    duk_uint_fast32_t line = 0;
    duk_bool_t process_messages;
    duk_bool_t processed_messages = 0;

    DUK_ASSERT(thr->heap->dbg_processing == 0);  /* don't re-enter e.g. during Eval */

    act = thr->callstack_curr;
    DUK_ASSERT(act != NULL);

    /* It might seem that replacing 'thr->heap' with just 'heap' below
     * might be a good idea, but it increases code size slightly
     * (probably due to unnecessary spilling) at least on x64.
     */

    /*
     *  Single opcode step check
     */

    if (thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_ONE_OPCODE_ACTIVE) {
        DUK_D(DUK_DPRINT("PAUSE TRIGGERED by one opcode step"));
        duk_debug_set_paused(thr->heap);
    }

    /*
     *  Breakpoint and step state checks
     */

    if (act->flags & DUK_ACT_FLAG_BREAKPOINT_ACTIVE ||
        (thr->heap->dbg_pause_act == thr->callstack_curr)) {
        line = duk_debug_curr_line(thr);

        if (act->prev_line != line) {
            /* Stepped?  Step out is handled by callstack unwind. */
            if ((thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_LINE_CHANGE) &&
                (thr->heap->dbg_pause_act == thr->callstack_curr) &&
                (line != thr->heap->dbg_pause_startline)) {
                DUK_D(DUK_DPRINT("PAUSE TRIGGERED by line change, at line %ld",
                                 (long) line));
                duk_debug_set_paused(thr->heap);
            }

            /* Check for breakpoints only on line transition.
             * Breakpoint is triggered when we enter the target
             * line from a different line, and the previous line
             * was within the same function.
             *
             * This condition is tricky: the condition used to be
             * that transition to -or across- the breakpoint line
             * triggered the breakpoint.  This seems intuitively
             * better because it handles breakpoints on lines with
             * no emitted opcodes; but this leads to the issue
             * described in: https://github.com/svaarala/duktape/issues/263.
             */
            bp_active = thr->heap->dbg_breakpoints_active;
            for (;;) {
                bp = *bp_active++;
                if (bp == NULL) {
                    break;
                }

                DUK_ASSERT(bp->filename != NULL);
                if (act->prev_line != bp->line && line == bp->line) {
                    DUK_D(DUK_DPRINT("PAUSE TRIGGERED by breakpoint at %!O:%ld",
                                     (duk_heaphdr *) bp->filename, (long) bp->line));
                    duk_debug_set_paused(thr->heap);
                }
            }
        } else {
            ;
        }

        act->prev_line = (duk_uint32_t) line;
    }

    /*
     *  Rate limit check for sending status update or peeking into
     *  the debug transport.  Both can be expensive operations that
     *  we don't want to do on every opcode.
     *
     *  Making sure the interval remains reasonable on a wide variety
     *  of targets and bytecode is difficult without a timestamp, so
     *  we use a Date-provided timestamp for the rate limit check.
     *  But since it's also expensive to get a timestamp, a bytecode
     *  counter is used to rate limit getting timestamps.
     */

    process_messages = 0;
    if (thr->heap->dbg_state_dirty || DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap) || thr->heap->dbg_detaching) {
        /* Enter message processing loop for sending Status notifys and
         * to finish a pending detach.
         */
        process_messages = 1;
    }

    /* XXX: remove heap->dbg_exec_counter, use heap->inst_count_interrupt instead? */
    DUK_ASSERT(thr->interrupt_init >= 0);
    thr->heap->dbg_exec_counter += (duk_uint_t) thr->interrupt_init;
    if (thr->heap->dbg_exec_counter - thr->heap->dbg_last_counter >= DUK_HEAP_DBG_RATELIMIT_OPCODES) {
        /* Overflow of the execution counter is fine and doesn't break
         * anything here.
         */

        duk_double_t now, diff_last;

        thr->heap->dbg_last_counter = thr->heap->dbg_exec_counter;
        now = duk_time_get_monotonic_time(thr);

        diff_last = now - thr->heap->dbg_last_time;
        if (diff_last < 0.0 || diff_last >= (duk_double_t) DUK_HEAP_DBG_RATELIMIT_MILLISECS) {
            /* Monotonic time should not experience time jumps,
             * but the provider may be missing and we're actually
             * using ECMAScript time.  So, tolerate negative values
             * so that a time jump works reasonably.
             *
             * Same interval is now used for status sending and
             * peeking.
             */

            thr->heap->dbg_last_time = now;
            thr->heap->dbg_state_dirty = 1;
            process_messages = 1;
        }
    }

    /*
     *  Process messages and send status if necessary.
     *
     *  If we're paused, we'll block for new messages.  If we're not
     *  paused, we'll process anything we can peek but won't block
     *  for more.  Detach (and re-attach) handling is all localized
     *  to duk_debug_process_messages() too.
     *
     *  Debugger writes outside the message loop may cause debugger
     *  detach1 phase to run, after which dbg_read_cb == NULL and
     *  dbg_detaching != 0.  The message loop will finish the detach
     *  by running detach2 phase, so enter the message loop also when
     *  detaching.
     */

    if (process_messages) {
        DUK_ASSERT(thr->heap->dbg_processing == 0);
        processed_messages = duk_debug_process_messages(thr, 0 /*no_block*/);
        DUK_ASSERT(thr->heap->dbg_processing == 0);
    }

    /* Continue checked execution if there are breakpoints or we're stepping.
     * Also use checked execution if paused flag is active - it shouldn't be
     * because the debug message loop shouldn't terminate if it was.  Step out
     * is handled by callstack unwind and doesn't need checked execution.
     * Note that debugger may have detached due to error or explicit request
     * above, so we must recheck attach status.
     */

    if (duk_debug_is_attached(thr->heap)) {
        DUK_ASSERT(act == thr->callstack_curr);
        DUK_ASSERT(act != NULL);
        if (act->flags & DUK_ACT_FLAG_BREAKPOINT_ACTIVE ||
            (thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_ONE_OPCODE) ||
            ((thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_LINE_CHANGE) &&
             thr->heap->dbg_pause_act == thr->callstack_curr) ||
             DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap)) {
            *out_immediate = 1;
        }

        /* If we processed any debug messages breakpoints may have
         * changed; restart execution to re-check active breakpoints.
         */
        if (processed_messages) {
            DUK_D(DUK_DPRINT("processed debug messages, restart execution to recheck possibly changed breakpoints"));
            *out_interrupt_retval = DUK__INT_RESTART;
        } else {
            if (thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_ONE_OPCODE) {
                /* Set 'pause after one opcode' active only when we're
                 * actually just about to execute code.
                 */
                thr->heap->dbg_pause_flags |= DUK_PAUSE_FLAG_ONE_OPCODE_ACTIVE;
            }
        }
    } else {
        DUK_D(DUK_DPRINT("debugger became detached, resume normal execution"));
    }
}
#endif  /* DUK_USE_DEBUGGER_SUPPORT */

DUK_LOCAL DUK_EXEC_NOINLINE_PERF DUK_COLD duk_small_uint_t duk__executor_interrupt(duk_hthread *thr) {
    duk_int_t ctr;
    duk_activation *act;
    duk_hcompfunc *fun;
    duk_bool_t immediate = 0;
    duk_small_uint_t retval;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->heap != NULL);
    DUK_ASSERT(thr->callstack_top > 0);

#if defined(DUK_USE_DEBUG)
    thr->heap->inst_count_interrupt += thr->interrupt_init;
    DUK_DD(DUK_DDPRINT("execution interrupt, counter=%ld, init=%ld, "
                       "instruction counts: executor=%ld, interrupt=%ld",
                       (long) thr->interrupt_counter, (long) thr->interrupt_init,
                       (long) thr->heap->inst_count_exec, (long) thr->heap->inst_count_interrupt));
#endif

    retval = DUK__INT_NOACTION;
    ctr = DUK_HTHREAD_INTCTR_DEFAULT;

    /*
     *  Avoid nested calls.  Concretely this happens during debugging, e.g.
     *  when we eval() an expression.
     *
     *  Also don't interrupt if we're currently doing debug processing
     *  (which can be initiated outside the bytecode executor) as this
     *  may cause the debugger to be called recursively.  Check required
     *  for correct operation of throw intercept and other "exotic" halting
     * scenarios.
     */

#if defined(DUK_USE_DEBUGGER_SUPPORT)
    if (DUK_HEAP_HAS_INTERRUPT_RUNNING(thr->heap) || thr->heap->dbg_processing) {
#else
    if (DUK_HEAP_HAS_INTERRUPT_RUNNING(thr->heap)) {
#endif
        DUK_DD(DUK_DDPRINT("nested executor interrupt, ignoring"));

        /* Set a high interrupt counter; the original executor
         * interrupt invocation will rewrite before exiting.
         */
        thr->interrupt_init = ctr;
        thr->interrupt_counter = ctr - 1;
        return DUK__INT_NOACTION;
    }
    DUK_HEAP_SET_INTERRUPT_RUNNING(thr->heap);

    act = thr->callstack_curr;
    DUK_ASSERT(act != NULL);

    fun = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act);
    DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC((duk_hobject *) fun));

    DUK_UNREF(fun);

#if defined(DUK_USE_EXEC_TIMEOUT_CHECK)
    /*
     *  Execution timeout check
     */

    if (DUK_USE_EXEC_TIMEOUT_CHECK(thr->heap->heap_udata)) {
        /* Keep throwing an error whenever we get here.  The unusual values
         * are set this way because no instruction is ever executed, we just
         * throw an error until all try/catch/finally and other catchpoints
         * have been exhausted.  Duktape/C code gets control at each protected
         * call but whenever it enters back into Duktape the RangeError gets
         * raised.  User exec timeout check must consistently indicate a timeout
         * until we've fully bubbled out of Duktape.
         */
        DUK_D(DUK_DPRINT("execution timeout, throwing a RangeError"));
        thr->interrupt_init = 0;
        thr->interrupt_counter = 0;
        DUK_HEAP_CLEAR_INTERRUPT_RUNNING(thr->heap);
        DUK_ERROR_RANGE(thr, "execution timeout");
        DUK_WO_NORETURN(return 0;);
    }
#endif  /* DUK_USE_EXEC_TIMEOUT_CHECK */

#if defined(DUK_USE_DEBUGGER_SUPPORT)
    if (!thr->heap->dbg_processing &&
        (thr->heap->dbg_read_cb != NULL || thr->heap->dbg_detaching)) {
        /* Avoid recursive re-entry; enter when we're attached or
         * detaching (to finish off the pending detach).
         */
        duk__interrupt_handle_debugger(thr, &immediate, &retval);
        DUK_ASSERT(act == thr->callstack_curr);
    }
#endif  /* DUK_USE_DEBUGGER_SUPPORT */

    /*
     *  Update the interrupt counter
     */

    if (immediate) {
        /* Cause an interrupt after executing one instruction. */
        ctr = 1;
    }

    /* The counter value is one less than the init value: init value should
     * indicate how many instructions are executed before interrupt.  To
     * execute 1 instruction (after interrupt handler return), counter must
     * be 0.
     */
    DUK_ASSERT(ctr >= 1);
    thr->interrupt_init = ctr;
    thr->interrupt_counter = ctr - 1;
    DUK_HEAP_CLEAR_INTERRUPT_RUNNING(thr->heap);

    return retval;
}
#endif  /* DUK_USE_INTERRUPT_COUNTER */

/*
 *  Debugger handling for executor restart
 *
 *  Check for breakpoints, stepping, etc, and figure out if we should execute
 *  in checked or normal mode.  Note that we can't do this when an activation
 *  is created, because breakpoint status (and stepping status) may change
 *  later, so we must recheck every time we're executing an activation.
 *  This primitive should be side effect free to avoid changes during check.
 */

#if defined(DUK_USE_DEBUGGER_SUPPORT)
DUK_LOCAL void duk__executor_recheck_debugger(duk_hthread *thr, duk_activation *act, duk_hcompfunc *fun) {
    duk_heap *heap;
    duk_tval *tv_tmp;
    duk_hstring *filename;
    duk_small_uint_t bp_idx;
    duk_breakpoint **bp_active;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(act != NULL);
    DUK_ASSERT(fun != NULL);

    heap = thr->heap;
    bp_active = heap->dbg_breakpoints_active;
    act->flags &= ~DUK_ACT_FLAG_BREAKPOINT_ACTIVE;

    tv_tmp = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, (duk_hobject *) fun, DUK_STRIDX_FILE_NAME);
    if (tv_tmp && DUK_TVAL_IS_STRING(tv_tmp)) {
        filename = DUK_TVAL_GET_STRING(tv_tmp);

        /* Figure out all active breakpoints.  A breakpoint is
         * considered active if the current function's fileName
         * matches the breakpoint's fileName, AND there is no
         * inner function that has matching line numbers
         * (otherwise a breakpoint would be triggered both
         * inside and outside of the inner function which would
         * be confusing).  Example:
         *
         *     function foo() {
         *         print('foo');
         *         function bar() {    <-.  breakpoints in these
         *             print('bar');     |  lines should not affect
         *         }                   <-'  foo() execution
         *         bar();
         *     }
         *
         * We need a few things that are only available when
         * debugger support is enabled: (1) a line range for
         * each function, and (2) access to the function
         * template to access the inner functions (and their
         * line ranges).
         *
         * It's important to have a narrow match for active
         * breakpoints so that we don't enter checked execution
         * when that's not necessary.  For instance, if we're
         * running inside a certain function and there's
         * breakpoint outside in (after the call site), we
         * don't want to slow down execution of the function.
         */

        for (bp_idx = 0; bp_idx < heap->dbg_breakpoint_count; bp_idx++) {
            duk_breakpoint *bp = heap->dbg_breakpoints + bp_idx;
            duk_hobject **funcs, **funcs_end;
            duk_hcompfunc *inner_fun;
            duk_bool_t bp_match;

            if (bp->filename == filename &&
                bp->line >= fun->start_line && bp->line <= fun->end_line) {
                bp_match = 1;
                DUK_DD(DUK_DDPRINT("breakpoint filename and line match: "
                                   "%s:%ld vs. %s (line %ld vs. %ld-%ld)",
                                   DUK_HSTRING_GET_DATA(bp->filename),
                                   (long) bp->line,
                                   DUK_HSTRING_GET_DATA(filename),
                                   (long) bp->line,
                                   (long) fun->start_line,
                                   (long) fun->end_line));

                funcs = DUK_HCOMPFUNC_GET_FUNCS_BASE(thr->heap, fun);
                funcs_end = DUK_HCOMPFUNC_GET_FUNCS_END(thr->heap, fun);
                while (funcs != funcs_end) {
                    inner_fun = (duk_hcompfunc *) *funcs;
                    DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) inner_fun));
                    if (bp->line >= inner_fun->start_line && bp->line <= inner_fun->end_line) {
                        DUK_DD(DUK_DDPRINT("inner function masks ('captures') breakpoint"));
                        bp_match = 0;
                        break;
                    }
                    funcs++;
                }

                if (bp_match) {
                    /* No need to check for size of bp_active list,
                     * it's always larger than maximum number of
                     * breakpoints.
                     */
                    act->flags |= DUK_ACT_FLAG_BREAKPOINT_ACTIVE;
                    *bp_active = heap->dbg_breakpoints + bp_idx;
                    bp_active++;
                }
            }
        }
    }

    *bp_active = NULL;  /* terminate */

    DUK_DD(DUK_DDPRINT("ACTIVE BREAKPOINTS: %ld", (long) (bp_active - thr->heap->dbg_breakpoints_active)));

    /* Force pause if we were doing "step into" in another activation. */
    if ((thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_FUNC_ENTRY) &&
        thr->heap->dbg_pause_act != thr->callstack_curr) {
        DUK_D(DUK_DPRINT("PAUSE TRIGGERED by function entry"));
        duk_debug_set_paused(thr->heap);
    }

    /* Force interrupt right away if we're paused or in "checked mode".
     * Step out is handled by callstack unwind.
     */
    if ((act->flags & DUK_ACT_FLAG_BREAKPOINT_ACTIVE) ||
        DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap) ||
        ((thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_LINE_CHANGE) &&
         thr->heap->dbg_pause_act == thr->callstack_curr)) {
        /* We'll need to interrupt early so recompute the init
         * counter to reflect the number of bytecode instructions
         * executed so that step counts for e.g. debugger rate
         * limiting are accurate.
         */
        DUK_ASSERT(thr->interrupt_counter <= thr->interrupt_init);
        thr->interrupt_init = thr->interrupt_init - thr->interrupt_counter;
        thr->interrupt_counter = 0;
    }
}
#endif  /* DUK_USE_DEBUGGER_SUPPORT */

/*
 *  Opcode handlers for opcodes with a lot of code and which are relatively
 *  rare; NOINLINE to reduce amount of code in main bytecode dispatcher.
 */

DUK_LOCAL DUK_EXEC_NOINLINE_PERF void duk__handle_op_initset_initget(duk_hthread *thr, duk_uint_fast32_t ins) {
    duk_bool_t is_set = (DUK_DEC_OP(ins) == DUK_OP_INITSET);
    duk_uint_fast_t idx;
    duk_uint_t defprop_flags;

    /* A -> object register (acts as a source)
     * BC -> BC+0 contains key, BC+1 closure (value)
     */

    /* INITSET/INITGET are only used to initialize object literal keys.
     * There may be a previous propery in ES2015 because duplicate property
     * names are allowed.
     */

    /* This could be made more optimal by accessing internals directly. */

    idx = (duk_uint_fast_t) DUK_DEC_BC(ins);
    duk_dup(thr, (duk_idx_t) (idx + 0));  /* key */
    duk_dup(thr, (duk_idx_t) (idx + 1));  /* getter/setter */
    if (is_set) {
            defprop_flags = DUK_DEFPROP_HAVE_SETTER |
                            DUK_DEFPROP_FORCE |
                            DUK_DEFPROP_SET_ENUMERABLE |
                            DUK_DEFPROP_SET_CONFIGURABLE;
    } else {
            defprop_flags = DUK_DEFPROP_HAVE_GETTER |
                            DUK_DEFPROP_FORCE |
                            DUK_DEFPROP_SET_ENUMERABLE |
                            DUK_DEFPROP_SET_CONFIGURABLE;
    }
    duk_def_prop(thr, (duk_idx_t) DUK_DEC_A(ins), defprop_flags);
}

DUK_LOCAL DUK_EXEC_NOINLINE_PERF void duk__handle_op_trycatch(duk_hthread *thr, duk_uint_fast32_t ins, duk_instr_t *curr_pc) {
    duk_activation *act;
    duk_catcher *cat;
    duk_tval *tv1;
    duk_small_uint_fast_t a;
    duk_small_uint_fast_t bc;

    /* A -> flags
     * BC -> reg_catch; base register for two registers used both during
     *       trycatch setup and when catch is triggered
     *
     *      If DUK_BC_TRYCATCH_FLAG_CATCH_BINDING set:
     *          reg_catch + 0: catch binding variable name (string).
     *          Automatic declarative environment is established for
     *          the duration of the 'catch' clause.
     *
     *      If DUK_BC_TRYCATCH_FLAG_WITH_BINDING set:
     *          reg_catch + 0: with 'target value', which is coerced to
     *          an object and then used as a bindind object for an
     *          environment record.  The binding is initialized here, for
     *          the 'try' clause.
     *
     * Note that a TRYCATCH generated for a 'with' statement has no
     * catch or finally parts.
     */

    /* XXX: TRYCATCH handling should be reworked to avoid creating
     * an explicit scope unless it is actually needed (e.g. function
     * instances or eval is executed inside the catch block).  This
     * rework is not trivial because the compiler doesn't have an
     * intermediate representation.  When the rework is done, the
     * opcode format can also be made more straightforward.
     */

    /* XXX: side effect handling is quite awkward here */

    DUK_DDD(DUK_DDDPRINT("TRYCATCH: reg_catch=%ld, have_catch=%ld, "
                         "have_finally=%ld, catch_binding=%ld, with_binding=%ld (flags=0x%02lx)",
                         (long) DUK_DEC_BC(ins),
                         (long) (DUK_DEC_A(ins) & DUK_BC_TRYCATCH_FLAG_HAVE_CATCH ? 1 : 0),
                         (long) (DUK_DEC_A(ins) & DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY ? 1 : 0),
                         (long) (DUK_DEC_A(ins) & DUK_BC_TRYCATCH_FLAG_CATCH_BINDING ? 1 : 0),
                         (long) (DUK_DEC_A(ins) & DUK_BC_TRYCATCH_FLAG_WITH_BINDING ? 1 : 0),
                         (unsigned long) DUK_DEC_A(ins)));

    a = DUK_DEC_A(ins);
    bc = DUK_DEC_BC(ins);

    /* Registers 'bc' and 'bc + 1' are written in longjmp handling
     * and if their previous values (which are temporaries) become
     * unreachable -and- have a finalizer, there'll be a function
     * call during error handling which is not supported now (GH-287).
     * Ensure that both 'bc' and 'bc + 1' have primitive values to
     * guarantee no finalizer calls in error handling.  Scrubbing also
     * ensures finalizers for the previous values run here rather than
     * later.  Error handling related values are also written to 'bc'
     * and 'bc + 1' but those values never become unreachable during
     * error handling, so there's no side effect problem even if the
     * error value has a finalizer.
     */
    duk_dup(thr, (duk_idx_t) bc);  /* Stabilize value. */
    duk_to_undefined(thr, (duk_idx_t) bc);
    duk_to_undefined(thr, (duk_idx_t) (bc + 1));

    /* Allocate catcher and populate it.  Doesn't have to
     * be fully atomic, but the catcher must be in a
     * consistent state if side effects (such as finalizer
     * calls) occur.
     */

    cat = duk_hthread_catcher_alloc(thr);
    DUK_ASSERT(cat != NULL);

    cat->flags = DUK_CAT_TYPE_TCF;
    cat->h_varname = NULL;
    cat->pc_base = (duk_instr_t *) curr_pc;  /* pre-incremented, points to first jump slot */
    cat->idx_base = (duk_size_t) (thr->valstack_bottom - thr->valstack) + bc;

    act = thr->callstack_curr;
    DUK_ASSERT(act != NULL);
    cat->parent = act->cat;
    act->cat = cat;

    if (a & DUK_BC_TRYCATCH_FLAG_HAVE_CATCH) {
        cat->flags |= DUK_CAT_FLAG_CATCH_ENABLED;
    }
    if (a & DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY) {
        cat->flags |= DUK_CAT_FLAG_FINALLY_ENABLED;
    }
    if (a & DUK_BC_TRYCATCH_FLAG_CATCH_BINDING) {
        DUK_DDD(DUK_DDDPRINT("catch binding flag set to catcher"));
        cat->flags |= DUK_CAT_FLAG_CATCH_BINDING_ENABLED;
        tv1 = DUK_GET_TVAL_NEGIDX(thr, -1);
        DUK_ASSERT(DUK_TVAL_IS_STRING(tv1));

        /* borrowed reference; although 'tv1' comes from a register,
         * its value was loaded using LDCONST so the constant will
         * also exist and be reachable.
         */
        cat->h_varname = DUK_TVAL_GET_STRING(tv1);
    } else if (a & DUK_BC_TRYCATCH_FLAG_WITH_BINDING) {
        duk_hobjenv *env;
        duk_hobject *target;

        /* Delayed env initialization for activation (if needed). */
        DUK_ASSERT(thr->callstack_top >= 1);
        DUK_ASSERT(act == thr->callstack_curr);
        DUK_ASSERT(act != NULL);
        if (act->lex_env == NULL) {
            DUK_DDD(DUK_DDDPRINT("delayed environment initialization"));
            DUK_ASSERT(act->var_env == NULL);

            duk_js_init_activation_environment_records_delayed(thr, act);
            DUK_ASSERT(act == thr->callstack_curr);
            DUK_UNREF(act);  /* 'act' is no longer accessed, scanbuild fix */
        }
        DUK_ASSERT(act->lex_env != NULL);
        DUK_ASSERT(act->var_env != NULL);

        /* Coerce 'with' target. */
        target = duk_to_hobject(thr, -1);
        DUK_ASSERT(target != NULL);

        /* Create an object environment; it is not pushed
         * so avoid side effects very carefully until it is
         * referenced.
         */
        env = duk_hobjenv_alloc(thr,
                                DUK_HOBJECT_FLAG_EXTENSIBLE |
                                DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV));
        DUK_ASSERT(env != NULL);
        DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) env) == NULL);
        env->target = target;  /* always provideThis=true */
        DUK_HOBJECT_INCREF(thr, target);
        env->has_this = 1;
        DUK_HOBJENV_ASSERT_VALID(env);
        DUK_DDD(DUK_DDDPRINT("environment for with binding: %!iO", env));

        DUK_ASSERT(act == thr->callstack_curr);
        DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) env) == NULL);
        DUK_ASSERT(act->lex_env != NULL);
        DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) env, act->lex_env);
        act->lex_env = (duk_hobject *) env;  /* Now reachable. */
        DUK_HOBJECT_INCREF(thr, (duk_hobject *) env);
        /* Net refcount change to act->lex_env is 0: incref for env's
         * prototype, decref for act->lex_env overwrite.
         */

        /* Set catcher lex_env active (affects unwind)
         * only when the whole setup is complete.
         */
        cat = act->cat;  /* XXX: better to relookup? not mandatory because 'cat' is stable */
        cat->flags |= DUK_CAT_FLAG_LEXENV_ACTIVE;
    } else {
        ;
    }

    DUK_DDD(DUK_DDDPRINT("TRYCATCH catcher: flags=0x%08lx, pc_base=%ld, "
                         "idx_base=%ld, h_varname=%!O",
                         (unsigned long) cat->flags,
                         (long) cat->pc_base, (long) cat->idx_base, (duk_heaphdr *) cat->h_varname));

    duk_pop_unsafe(thr);
}

DUK_LOCAL DUK_EXEC_NOINLINE_PERF duk_instr_t *duk__handle_op_endtry(duk_hthread *thr, duk_uint_fast32_t ins) {
    duk_activation *act;
    duk_catcher *cat;
    duk_tval *tv1;
    duk_instr_t *pc_base;

    DUK_UNREF(ins);

    DUK_ASSERT(thr->callstack_top >= 1);
    act = thr->callstack_curr;
    DUK_ASSERT(act != NULL);
    cat = act->cat;
    DUK_ASSERT(cat != NULL);
    DUK_ASSERT(DUK_CAT_GET_TYPE(act->cat) == DUK_CAT_TYPE_TCF);

    DUK_DDD(DUK_DDDPRINT("ENDTRY: clearing catch active flag (regardless of whether it was set or not)"));
    DUK_CAT_CLEAR_CATCH_ENABLED(cat);

    pc_base = cat->pc_base;

    if (DUK_CAT_HAS_FINALLY_ENABLED(cat)) {
        DUK_DDD(DUK_DDDPRINT("ENDTRY: finally part is active, jump through 2nd jump slot with 'normal continuation'"));

        tv1 = thr->valstack + cat->idx_base;
        DUK_ASSERT(tv1 >= thr->valstack && tv1 < thr->valstack_top);
        DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv1);  /* side effects */
        tv1 = NULL;

        tv1 = thr->valstack + cat->idx_base + 1;
        DUK_ASSERT(tv1 >= thr->valstack && tv1 < thr->valstack_top);
        DUK_TVAL_SET_U32_UPDREF(thr, tv1, (duk_uint32_t) DUK_LJ_TYPE_NORMAL);  /* side effects */
        tv1 = NULL;

        DUK_CAT_CLEAR_FINALLY_ENABLED(cat);
    } else {
        DUK_DDD(DUK_DDDPRINT("ENDTRY: no finally part, dismantle catcher, jump through 2nd jump slot (to end of statement)"));

        duk_hthread_catcher_unwind_norz(thr, act);  /* lexenv may be set for 'with' binding */
        /* no need to unwind callstack */
    }

    return pc_base + 1;  /* new curr_pc value */
}

DUK_LOCAL DUK_EXEC_NOINLINE_PERF duk_instr_t *duk__handle_op_endcatch(duk_hthread *thr, duk_uint_fast32_t ins) {
    duk_activation *act;
    duk_catcher *cat;
    duk_tval *tv1;
    duk_instr_t *pc_base;

    DUK_UNREF(ins);

    DUK_ASSERT(thr->callstack_top >= 1);
    act = thr->callstack_curr;
    DUK_ASSERT(act != NULL);
    cat = act->cat;
    DUK_ASSERT(cat != NULL);
    DUK_ASSERT(!DUK_CAT_HAS_CATCH_ENABLED(cat));  /* cleared before entering catch part */

    if (DUK_CAT_HAS_LEXENV_ACTIVE(cat)) {
        duk_hobject *prev_env;

        /* 'with' binding has no catch clause, so can't be here unless a normal try-catch */
        DUK_ASSERT(DUK_CAT_HAS_CATCH_BINDING_ENABLED(cat));
        DUK_ASSERT(act->lex_env != NULL);

        DUK_DDD(DUK_DDDPRINT("ENDCATCH: popping catcher part lexical environment"));

        prev_env = act->lex_env;
        DUK_ASSERT(prev_env != NULL);
        act->lex_env = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, prev_env);
        DUK_CAT_CLEAR_LEXENV_ACTIVE(cat);
        DUK_HOBJECT_INCREF(thr, act->lex_env);
        DUK_HOBJECT_DECREF(thr, prev_env);  /* side effects */

        DUK_ASSERT(act == thr->callstack_curr);
        DUK_ASSERT(act != NULL);
    }

    pc_base = cat->pc_base;

    if (DUK_CAT_HAS_FINALLY_ENABLED(cat)) {
        DUK_DDD(DUK_DDDPRINT("ENDCATCH: finally part is active, jump through 2nd jump slot with 'normal continuation'"));

        tv1 = thr->valstack + cat->idx_base;
        DUK_ASSERT(tv1 >= thr->valstack && tv1 < thr->valstack_top);
        DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv1);  /* side effects */
        tv1 = NULL;

        tv1 = thr->valstack + cat->idx_base + 1;
        DUK_ASSERT(tv1 >= thr->valstack && tv1 < thr->valstack_top);
        DUK_TVAL_SET_U32_UPDREF(thr, tv1, (duk_uint32_t) DUK_LJ_TYPE_NORMAL);  /* side effects */
        tv1 = NULL;

        DUK_CAT_CLEAR_FINALLY_ENABLED(cat);
    } else {
        DUK_DDD(DUK_DDDPRINT("ENDCATCH: no finally part, dismantle catcher, jump through 2nd jump slot (to end of statement)"));

        duk_hthread_catcher_unwind_norz(thr, act);
        /* no need to unwind callstack */
    }

    return pc_base + 1;  /* new curr_pc value */
}

DUK_LOCAL DUK_EXEC_NOINLINE_PERF duk_small_uint_t duk__handle_op_endfin(duk_hthread *thr, duk_uint_fast32_t ins, duk_activation *entry_act) {
    duk_activation *act;
    duk_tval *tv1;
    duk_uint_t reg_catch;
    duk_small_uint_t cont_type;
    duk_small_uint_t ret_result;

    DUK_ASSERT(thr->ptr_curr_pc == NULL);
    DUK_ASSERT(thr->callstack_top >= 1);
    act = thr->callstack_curr;
    DUK_ASSERT(act != NULL);
    reg_catch = DUK_DEC_ABC(ins);

    /* CATCH flag may be enabled or disabled here; it may be enabled if
     * the statement has a catch block but the try block does not throw
     * an error.
     */

    DUK_DDD(DUK_DDDPRINT("ENDFIN: completion value=%!T, type=%!T",
                         (duk_tval *) (thr->valstack_bottom + reg_catch + 0),
                         (duk_tval *) (thr->valstack_bottom + reg_catch + 1)));

    tv1 = thr->valstack_bottom + reg_catch + 1;  /* type */
    DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv1));
#if defined(DUK_USE_FASTINT)
    DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv1));
    cont_type = (duk_small_uint_t) DUK_TVAL_GET_FASTINT_U32(tv1);
#else
    cont_type = (duk_small_uint_t) DUK_TVAL_GET_NUMBER(tv1);
#endif

    tv1--;  /* value */

    switch (cont_type) {
    case DUK_LJ_TYPE_NORMAL: {
        DUK_DDD(DUK_DDDPRINT("ENDFIN: finally part finishing with 'normal' (non-abrupt) completion -> "
                             "dismantle catcher, resume execution after ENDFIN"));

        duk_hthread_catcher_unwind_norz(thr, act);
        /* no need to unwind callstack */
        return 0;  /* restart execution */
    }
    case DUK_LJ_TYPE_RETURN: {
        DUK_DDD(DUK_DDDPRINT("ENDFIN: finally part finishing with 'return' complation -> dismantle "
                             "catcher, handle return, lj.value1=%!T", tv1));

        /* Not necessary to unwind catch stack: return handling will
         * do it.  The finally flag of 'cat' is no longer set.  The
         * catch flag may be set, but it's not checked by return handling.
         */

        duk_push_tval(thr, tv1);
        ret_result = duk__handle_return(thr, entry_act);
        if (ret_result == DUK__RETHAND_RESTART) {
            return 0;  /* restart execution */
        }
        DUK_ASSERT(ret_result == DUK__RETHAND_FINISHED);

        DUK_DDD(DUK_DDDPRINT("exiting executor after ENDFIN and RETURN (pseudo) longjmp type"));
        return 1;  /* exit executor */
    }
    case DUK_LJ_TYPE_BREAK:
    case DUK_LJ_TYPE_CONTINUE: {
        duk_uint_t label_id;
        duk_small_uint_t lj_type;

        /* Not necessary to unwind catch stack: break/continue
         * handling will do it.  The finally flag of 'cat' is
         * no longer set.  The catch flag may be set, but it's
         * not checked by break/continue handling.
         */

        DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv1));
#if defined(DUK_USE_FASTINT)
        DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv1));
        label_id = (duk_small_uint_t) DUK_TVAL_GET_FASTINT_U32(tv1);
#else
        label_id = (duk_small_uint_t) DUK_TVAL_GET_NUMBER(tv1);
#endif
        lj_type = cont_type;
        duk__handle_break_or_continue(thr, label_id, lj_type);
        return 0;  /* restart execution */
    }
    default: {
        DUK_DDD(DUK_DDDPRINT("ENDFIN: finally part finishing with abrupt completion, lj_type=%ld -> "
                             "dismantle catcher, re-throw error",
                             (long) cont_type));

        duk_err_setup_ljstate1(thr, (duk_small_uint_t) cont_type, tv1);
        /* No debugger Throw notify check on purpose (rethrow). */

        DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL);  /* always in executor */
        duk_err_longjmp(thr);
        DUK_UNREACHABLE();
    }
    }

    DUK_UNREACHABLE();
    return 0;
}

DUK_LOCAL DUK_EXEC_NOINLINE_PERF void duk__handle_op_initenum(duk_hthread *thr, duk_uint_fast32_t ins) {
    duk_small_uint_t b;
    duk_small_uint_t c;

    /*
     *  Enumeration semantics come from for-in statement, E5 Section 12.6.4.
     *  If called with 'null' or 'undefined', this opcode returns 'null' as
     *  the enumerator, which is special cased in NEXTENUM.  This simplifies
     *  the compiler part
     */

    /* B -> register for writing enumerator object
     * C -> value to be enumerated (register)
     */
    b = DUK_DEC_B(ins);
    c = DUK_DEC_C(ins);

    if (duk_is_null_or_undefined(thr, (duk_idx_t) c)) {
        duk_push_null(thr);
        duk_replace(thr, (duk_idx_t) b);
    } else {
        duk_dup(thr, (duk_idx_t) c);
        duk_to_object(thr, -1);
        duk_hobject_enumerator_create(thr, 0 /*enum_flags*/);  /* [ ... val ] --> [ ... enum ] */
        duk_replace(thr, (duk_idx_t) b);
    }
}

DUK_LOCAL DUK_EXEC_NOINLINE_PERF duk_small_uint_t duk__handle_op_nextenum(duk_hthread *thr, duk_uint_fast32_t ins) {
    duk_small_uint_t b;
    duk_small_uint_t c;
    duk_small_uint_t pc_skip = 0;

    /*
     *  NEXTENUM checks whether the enumerator still has unenumerated
     *  keys.  If so, the next key is loaded to the target register
     *  and the next instruction is skipped.  Otherwise the next instruction
     *  will be executed, jumping out of the enumeration loop.
     */

    /* B -> target register for next key
     * C -> enum register
     */
    b = DUK_DEC_B(ins);
    c = DUK_DEC_C(ins);

    DUK_DDD(DUK_DDDPRINT("NEXTENUM: b->%!T, c->%!T",
                         (duk_tval *) duk_get_tval(thr, (duk_idx_t) b),
                         (duk_tval *) duk_get_tval(thr, (duk_idx_t) c)));

    if (duk_is_object(thr, (duk_idx_t) c)) {
        /* XXX: assert 'c' is an enumerator */
        duk_dup(thr, (duk_idx_t) c);
        if (duk_hobject_enumerator_next(thr, 0 /*get_value*/)) {
            /* [ ... enum ] -> [ ... next_key ] */
            DUK_DDD(DUK_DDDPRINT("enum active, next key is %!T, skip jump slot ",
                                 (duk_tval *) duk_get_tval(thr, -1)));
            pc_skip = 1;
        } else {
            /* [ ... enum ] -> [ ... ] */
            DUK_DDD(DUK_DDDPRINT("enum finished, execute jump slot"));
            DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top));  /* valstack policy */
            thr->valstack_top++;
        }
        duk_replace(thr, (duk_idx_t) b);
    } else {
        /* 'null' enumerator case -> behave as with an empty enumerator */
        DUK_ASSERT(duk_is_null(thr, (duk_idx_t) c));
        DUK_DDD(DUK_DDDPRINT("enum is null, execute jump slot"));
    }

    return pc_skip;
}

/*
 *  Call handling helpers.
 */

DUK_LOCAL duk_bool_t duk__executor_handle_call(duk_hthread *thr, duk_idx_t idx, duk_idx_t nargs, duk_small_uint_t call_flags) {
    duk_bool_t rc;

    duk_set_top_unsafe(thr, (duk_idx_t) (idx + nargs + 2));   /* [ ... func this arg1 ... argN ] */

    /* Attempt an Ecma-to-Ecma call setup.  If the call
     * target is (directly or indirectly) Reflect.construct(),
     * the call may change into a constructor call on the fly.
     */
    rc = (duk_bool_t) duk_handle_call_unprotected(thr, idx, call_flags);
    if (rc != 0) {
        /* Ecma-to-ecma call possible, may or may not
         * be a tail call.  Avoid C recursion by
         * reusing current executor instance.
         */
        DUK_DDD(DUK_DDDPRINT("ecma-to-ecma call setup possible, restart execution"));
        /* curr_pc synced by duk_handle_call_unprotected() */
        DUK_ASSERT(thr->ptr_curr_pc == NULL);
        return rc;
    } else {
        /* Call was handled inline. */
    }
    DUK_ASSERT(thr->ptr_curr_pc != NULL);
    return rc;
}

/*
 *  ECMAScript bytecode executor.
 *
 *  Resume execution for the current thread from its current activation.
 *  Returns when execution would return from the entry level activation,
 *  leaving a single return value on top of the stack.  Function calls
 *  and thread resumptions are handled internally.  If an error occurs,
 *  a longjmp() with type DUK_LJ_TYPE_THROW is called on the entry level
 *  setjmp() jmpbuf.
 *
 *  ECMAScript function calls and coroutine resumptions are handled
 *  internally (by the outer executor function) without recursive C calls.
 *  Other function calls are handled using duk_handle_call(), increasing
 *  C recursion depth.
 *
 *  Abrupt completions (= long control tranfers) are handled either
 *  directly by reconfiguring relevant stacks and restarting execution,
 *  or via a longjmp.  Longjmp-free handling is preferable for performance
 *  (especially Emscripten performance), and is used for: break, continue,
 *  and return.
 *
 *  For more detailed notes, see doc/execution.rst.
 *
 *  Also see doc/code-issues.rst for discussion of setjmp(), longjmp(),
 *  and volatile.
 */

/* Presence of 'fun' is config based, there's a marginal performance
 * difference and the best option is architecture dependent.
 */
#if defined(DUK_USE_EXEC_FUN_LOCAL)
#define DUK__FUN()          fun
#else
#define DUK__FUN()          ((duk_hcompfunc *) DUK_ACT_GET_FUNC((thr)->callstack_curr))
#endif

/* Strict flag. */
#define DUK__STRICT()       ((duk_small_uint_t) DUK_HOBJECT_HAS_STRICT((duk_hobject *) DUK__FUN()))

/* Reg/const access macros: these are very footprint and performance sensitive
 * so modify with care.  Arguments are sometimes evaluated multiple times which
 * is not ideal.
 */
#define DUK__REG(x)         (*(thr->valstack_bottom + (x)))
#define DUK__REGP(x)        (thr->valstack_bottom + (x))
#define DUK__CONST(x)       (*(consts + (x)))
#define DUK__CONSTP(x)      (consts + (x))

/* Reg/const access macros which take the 32-bit instruction and avoid an
 * explicit field decoding step by using shifts and masks.  These must be
 * kept in sync with duk_js_bytecode.h.  The shift/mask values are chosen
 * so that 'ins' can be shifted and masked and used as a -byte- offset
 * instead of a duk_tval offset which needs further shifting (which is an
 * issue on some, but not all, CPUs).
 */
#define DUK__RCBIT_B           DUK_BC_REGCONST_B
#define DUK__RCBIT_C           DUK_BC_REGCONST_C
#if defined(DUK_USE_EXEC_REGCONST_OPTIMIZE)
#if defined(DUK_USE_PACKED_TVAL)
#define DUK__TVAL_SHIFT        3  /* sizeof(duk_tval) == 8 */
#else
#define DUK__TVAL_SHIFT        4  /* sizeof(duk_tval) == 16; not always the case so also asserted for */
#endif
#define DUK__SHIFT_A           (DUK_BC_SHIFT_A - DUK__TVAL_SHIFT)
#define DUK__SHIFT_B           (DUK_BC_SHIFT_B - DUK__TVAL_SHIFT)
#define DUK__SHIFT_C           (DUK_BC_SHIFT_C - DUK__TVAL_SHIFT)
#define DUK__SHIFT_BC          (DUK_BC_SHIFT_BC - DUK__TVAL_SHIFT)
#define DUK__MASK_A            (DUK_BC_UNSHIFTED_MASK_A << DUK__TVAL_SHIFT)
#define DUK__MASK_B            (DUK_BC_UNSHIFTED_MASK_B << DUK__TVAL_SHIFT)
#define DUK__MASK_C            (DUK_BC_UNSHIFTED_MASK_C << DUK__TVAL_SHIFT)
#define DUK__MASK_BC           (DUK_BC_UNSHIFTED_MASK_BC << DUK__TVAL_SHIFT)
#define DUK__BYTEOFF_A(ins)    (((ins) >> DUK__SHIFT_A) & DUK__MASK_A)
#define DUK__BYTEOFF_B(ins)    (((ins) >> DUK__SHIFT_B) & DUK__MASK_B)
#define DUK__BYTEOFF_C(ins)    (((ins) >> DUK__SHIFT_C) & DUK__MASK_C)
#define DUK__BYTEOFF_BC(ins)   (((ins) >> DUK__SHIFT_BC) & DUK__MASK_BC)

#define DUK__REGP_A(ins)       ((duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_bottom + DUK__BYTEOFF_A((ins))))
#define DUK__REGP_B(ins)       ((duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_bottom + DUK__BYTEOFF_B((ins))))
#define DUK__REGP_C(ins)       ((duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_bottom + DUK__BYTEOFF_C((ins))))
#define DUK__REGP_BC(ins)      ((duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_bottom + DUK__BYTEOFF_BC((ins))))
#define DUK__CONSTP_A(ins)     ((duk_tval *) (void *) ((duk_uint8_t *) consts + DUK__BYTEOFF_A((ins))))
#define DUK__CONSTP_B(ins)     ((duk_tval *) (void *) ((duk_uint8_t *) consts + DUK__BYTEOFF_B((ins))))
#define DUK__CONSTP_C(ins)     ((duk_tval *) (void *) ((duk_uint8_t *) consts + DUK__BYTEOFF_C((ins))))
#define DUK__CONSTP_BC(ins)    ((duk_tval *) (void *) ((duk_uint8_t *) consts + DUK__BYTEOFF_BC((ins))))
#define DUK__REGCONSTP_B(ins)  ((duk_tval *) (void *) ((duk_uint8_t *) (((ins) & DUK__RCBIT_B) ? consts : thr->valstack_bottom) + DUK__BYTEOFF_B((ins))))
#define DUK__REGCONSTP_C(ins)  ((duk_tval *) (void *) ((duk_uint8_t *) (((ins) & DUK__RCBIT_C) ? consts : thr->valstack_bottom) + DUK__BYTEOFF_C((ins))))
#else  /* DUK_USE_EXEC_REGCONST_OPTIMIZE */
/* Safe alternatives, no assumption about duk_tval size. */
#define DUK__REGP_A(ins)       DUK__REGP(DUK_DEC_A((ins)))
#define DUK__REGP_B(ins)       DUK__REGP(DUK_DEC_B((ins)))
#define DUK__REGP_C(ins)       DUK__REGP(DUK_DEC_C((ins)))
#define DUK__REGP_BC(ins)      DUK__REGP(DUK_DEC_BC((ins)))
#define DUK__CONSTP_A(ins)     DUK__CONSTP(DUK_DEC_A((ins)))
#define DUK__CONSTP_B(ins)     DUK__CONSTP(DUK_DEC_B((ins)))
#define DUK__CONSTP_C(ins)     DUK__CONSTP(DUK_DEC_C((ins)))
#define DUK__CONSTP_BC(ins)    DUK__CONSTP(DUK_DEC_BC((ins)))
#define DUK__REGCONSTP_B(ins)  ((((ins) & DUK__RCBIT_B) ? consts : thr->valstack_bottom) + DUK_DEC_B((ins)))
#define DUK__REGCONSTP_C(ins)  ((((ins) & DUK__RCBIT_C) ? consts : thr->valstack_bottom) + DUK_DEC_C((ins)))
#endif  /* DUK_USE_EXEC_REGCONST_OPTIMIZE */

#if defined(DUK_USE_VERBOSE_EXECUTOR_ERRORS)
#define DUK__INTERNAL_ERROR(msg)  do { \
        DUK_ERROR_ERROR(thr, (msg)); \
        DUK_WO_NORETURN(return;); \
    } while (0)
#else
#define DUK__INTERNAL_ERROR(msg)  do { \
        goto internal_error; \
    } while (0)
#endif

#define DUK__SYNC_CURR_PC()  do { \
        duk_activation *duk__act; \
        duk__act = thr->callstack_curr; \
        duk__act->curr_pc = curr_pc; \
    } while (0)
#define DUK__SYNC_AND_NULL_CURR_PC()  do { \
        duk_activation *duk__act; \
        duk__act = thr->callstack_curr; \
        duk__act->curr_pc = curr_pc; \
        thr->ptr_curr_pc = NULL; \
    } while (0)

#if defined(DUK_USE_EXEC_PREFER_SIZE)
#define DUK__LOOKUP_INDIRECT(idx) do { \
        (idx) = (duk_uint_fast_t) duk_get_uint(thr, (duk_idx_t) (idx)); \
    } while (0)
#elif defined(DUK_USE_FASTINT)
#define DUK__LOOKUP_INDIRECT(idx) do { \
        duk_tval *tv_ind; \
        tv_ind = DUK__REGP((idx)); \
        DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_ind)); \
        DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv_ind));  /* compiler guarantees */ \
        (idx) = (duk_uint_fast_t) DUK_TVAL_GET_FASTINT_U32(tv_ind); \
    } while (0)
#else
#define DUK__LOOKUP_INDIRECT(idx) do { \
        duk_tval *tv_ind; \
        tv_ind = DUK__REGP(idx); \
        DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_ind)); \
        idx = (duk_uint_fast_t) DUK_TVAL_GET_NUMBER(tv_ind); \
    } while (0)
#endif

DUK_LOCAL void duk__handle_executor_error(duk_heap *heap,
                                          duk_activation *entry_act,
                                          duk_int_t entry_call_recursion_depth,
                                          duk_jmpbuf *entry_jmpbuf_ptr,
                                          volatile duk_bool_t *out_delayed_catch_setup) {
    duk_small_uint_t lj_ret;

    /* Longjmp callers are required to sync-and-null thr->ptr_curr_pc
     * before longjmp.
     */
    DUK_ASSERT(heap->curr_thread != NULL);
    DUK_ASSERT(heap->curr_thread->ptr_curr_pc == NULL);

    /* XXX: signalling the need to shrink check (only if unwound) */

    /* Must be restored here to handle e.g. yields properly. */
    heap->call_recursion_depth = entry_call_recursion_depth;

    /* Switch to caller's setjmp() catcher so that if an error occurs
     * during error handling, it is always propagated outwards instead
     * of causing an infinite loop in our own handler.
     */
    heap->lj.jmpbuf_ptr = (duk_jmpbuf *) entry_jmpbuf_ptr;

    lj_ret = duk__handle_longjmp(heap->curr_thread, entry_act, out_delayed_catch_setup);

    /* Error handling complete, remove side effect protections.
     */
#if defined(DUK_USE_ASSERTIONS)
    DUK_ASSERT(heap->error_not_allowed == 1);
    heap->error_not_allowed = 0;
#endif
    DUK_ASSERT(heap->pf_prevent_count > 0);
    heap->pf_prevent_count--;
    DUK_DD(DUK_DDPRINT("executor error handled, pf_prevent_count updated to %ld", (long) heap->pf_prevent_count));

    if (lj_ret == DUK__LONGJMP_RESTART) {
        /* Restart bytecode execution, possibly with a changed thread. */
        DUK_REFZERO_CHECK_SLOW(heap->curr_thread);
    } else {
        /* If an error is propagated, don't run refzero checks here.
         * The next catcher will deal with that.  Pf_prevent_count
         * will be re-bumped by the longjmp.
         */

        DUK_ASSERT(lj_ret == DUK__LONGJMP_RETHROW);  /* Rethrow error to calling state. */
        DUK_ASSERT(heap->lj.jmpbuf_ptr == entry_jmpbuf_ptr);  /* Longjmp handling has restored jmpbuf_ptr. */

        /* Thread may have changed, e.g. YIELD converted to THROW. */
        duk_err_longjmp(heap->curr_thread);
        DUK_UNREACHABLE();
    }
}

/* Outer executor with setjmp/longjmp handling. */
DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
    /* Entry level info. */
    duk_hthread *entry_thread;
    duk_activation *entry_act;
    duk_int_t entry_call_recursion_depth;
    duk_jmpbuf *entry_jmpbuf_ptr;
    duk_jmpbuf our_jmpbuf;
    duk_heap *heap;
    volatile duk_bool_t delayed_catch_setup = 0;

    DUK_ASSERT(exec_thr != NULL);
    DUK_ASSERT(exec_thr->heap != NULL);
    DUK_ASSERT(exec_thr->heap->curr_thread != NULL);
    DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR((duk_heaphdr *) exec_thr);
    DUK_ASSERT(exec_thr->callstack_top >= 1);  /* at least one activation, ours */
    DUK_ASSERT(exec_thr->callstack_curr != NULL);
    DUK_ASSERT(DUK_ACT_GET_FUNC(exec_thr->callstack_curr) != NULL);
    DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(exec_thr->callstack_curr)));

    DUK_GC_TORTURE(exec_thr->heap);

    entry_thread = exec_thr;
    heap = entry_thread->heap;
    entry_act = entry_thread->callstack_curr;
    DUK_ASSERT(entry_act != NULL);
    entry_call_recursion_depth = entry_thread->heap->call_recursion_depth;
    entry_jmpbuf_ptr = entry_thread->heap->lj.jmpbuf_ptr;

    /*
     *  Note: we currently assume that the setjmp() catchpoint is
     *  not re-entrant (longjmp() cannot be called more than once
     *  for a single setjmp()).
     *
     *  See doc/code-issues.rst for notes on variable assignment
     *  before and after setjmp().
     */

    for (;;) {
        heap->lj.jmpbuf_ptr = &our_jmpbuf;
        DUK_ASSERT(heap->lj.jmpbuf_ptr != NULL);

#if defined(DUK_USE_CPP_EXCEPTIONS)
        try {
#else
        DUK_ASSERT(heap->lj.jmpbuf_ptr == &our_jmpbuf);
        if (DUK_SETJMP(our_jmpbuf.jb) == 0) {
#endif
            DUK_DDD(DUK_DDDPRINT("after setjmp, delayed catch setup: %ld\n", (long) delayed_catch_setup));

            if (DUK_UNLIKELY(delayed_catch_setup != 0)) {
                duk_hthread *thr = entry_thread->heap->curr_thread;

                delayed_catch_setup = 0;
                duk__handle_catch_part2(thr);
                DUK_ASSERT(delayed_catch_setup == 0);
                DUK_DDD(DUK_DDDPRINT("top after delayed catch setup: %ld", (long) duk_get_top(entry_thread)));
            }

            /* Execute bytecode until returned or longjmp(). */
            duk__js_execute_bytecode_inner(entry_thread, entry_act);

            /* Successful return: restore jmpbuf and return to caller. */
            heap->lj.jmpbuf_ptr = entry_jmpbuf_ptr;

            return;
#if defined(DUK_USE_CPP_EXCEPTIONS)
        } catch (duk_internal_exception &exc) {
#else
        } else {
#endif
#if defined(DUK_USE_CPP_EXCEPTIONS)
            DUK_UNREF(exc);
#endif
            DUK_DDD(DUK_DDDPRINT("longjmp caught by bytecode executor"));
            DUK_STATS_INC(exec_thr->heap, stats_exec_throw);

            duk__handle_executor_error(heap,
                                       entry_act,
                                       entry_call_recursion_depth,
                                       entry_jmpbuf_ptr,
                           &delayed_catch_setup);
        }
#if defined(DUK_USE_CPP_EXCEPTIONS)
        catch (duk_fatal_exception &exc) {
            DUK_D(DUK_DPRINT("rethrow duk_fatal_exception"));
            DUK_UNREF(exc);
            throw;
        } catch (std::exception &exc) {
            const char *what = exc.what();
            if (!what) {
                what = "unknown";
            }
            DUK_D(DUK_DPRINT("unexpected c++ std::exception (perhaps thrown by user code)"));
            DUK_STATS_INC(exec_thr->heap, stats_exec_throw);
            try {
                DUK_ASSERT(heap->curr_thread != NULL);
                DUK_ERROR_FMT1(heap->curr_thread, DUK_ERR_TYPE_ERROR, "caught invalid c++ std::exception '%s' (perhaps thrown by user code)", what);
                DUK_WO_NORETURN(return;);
            } catch (duk_internal_exception exc) {
                DUK_D(DUK_DPRINT("caught api error thrown from unexpected c++ std::exception"));
                DUK_UNREF(exc);
                duk__handle_executor_error(heap,
                                           entry_act,
                                           entry_call_recursion_depth,
                                           entry_jmpbuf_ptr,
                               &delayed_catch_setup);
            }
        } catch (...) {
            DUK_D(DUK_DPRINT("unexpected c++ exception (perhaps thrown by user code)"));
            DUK_STATS_INC(exec_thr->heap, stats_exec_throw);
            try {
                DUK_ASSERT(heap->curr_thread != NULL);
                DUK_ERROR_TYPE(heap->curr_thread, "caught invalid c++ exception (perhaps thrown by user code)");
                DUK_WO_NORETURN(return;);
            } catch (duk_internal_exception exc) {
                DUK_D(DUK_DPRINT("caught api error thrown from unexpected c++ exception"));
                DUK_UNREF(exc);
                duk__handle_executor_error(heap,
                                           entry_act,
                                           entry_call_recursion_depth,
                                           entry_jmpbuf_ptr,
                               &delayed_catch_setup);
            }
        }
#endif
    }

    DUK_WO_NORETURN(return;);
}

/* Inner executor, performance critical. */
DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread *entry_thread, duk_activation *entry_act) {
    /* Current PC, accessed by other functions through thr->ptr_to_curr_pc.
     * Critical for performance.  It would be safest to make this volatile,
     * but that eliminates performance benefits; aliasing guarantees
     * should be enough though.
     */
    duk_instr_t *curr_pc;         /* bytecode has a stable pointer */

    /* Hot variables for interpretation.  Critical for performance,
     * but must add sparingly to minimize register shuffling.
     */
    duk_hthread *thr;             /* stable */
    duk_tval *consts;             /* stable */
    duk_uint_fast32_t ins;
    /* 'funcs' is quite rarely used, so no local for it */
#if defined(DUK_USE_EXEC_FUN_LOCAL)
    duk_hcompfunc *fun;
#else
    /* 'fun' is quite rarely used, so no local for it */
#endif

#if defined(DUK_USE_INTERRUPT_COUNTER)
    duk_int_t int_ctr;
#endif

#if defined(DUK_USE_ASSERTIONS)
    duk_size_t valstack_top_base;    /* valstack top, should match before interpreting each op (no leftovers) */
#endif

    /* Optimized reg/const access macros assume sizeof(duk_tval) to be
     * either 8 or 16.  Heap allocation checks this even without asserts
     * enabled now because it can't be autodetected in duk_config.h.
     */
#if 1
#if defined(DUK_USE_PACKED_TVAL)
    DUK_ASSERT(sizeof(duk_tval) == 8);
#else
    DUK_ASSERT(sizeof(duk_tval) == 16);
#endif
#endif

    DUK_GC_TORTURE(entry_thread->heap);

    /*
     *  Restart execution by reloading thread state.
     *
     *  Note that 'thr' and any thread configuration may have changed,
     *  so all local variables are suspect and we need to reinitialize.
     *
     *  The number of local variables should be kept to a minimum: if
     *  the variables are spilled, they will need to be loaded from
     *  memory anyway.
     *
     *  Any 'goto restart_execution;' code path in opcode dispatch must
     *  ensure 'curr_pc' is synced back to act->curr_pc before the goto
     *  takes place.
     *
     *  The interpreter must be very careful with memory pointers, as
     *  many pointers are not guaranteed to be 'stable' and may be
     *  reallocated and relocated on-the-fly quite easily (e.g. by a
     *  memory allocation or a property access).
     *
     *  The following are assumed to have stable pointers:
     *    - the current thread
     *    - the current function
     *    - the bytecode, constant table, inner function table of the
     *      current function (as they are a part of the function allocation)
     *
     *  The following are assumed to have semi-stable pointers:
     *    - the current activation entry: stable as long as callstack
     *      is not changed (reallocated by growing or shrinking), or
     *      by any garbage collection invocation (through finalizers)
     *    - Note in particular that ANY DECREF can invalidate the
     *      activation pointer, so for the most part a fresh lookup
     *      is required
     *
     *  The following are not assumed to have stable pointers at all:
     *    - the value stack (registers) of the current thread
     *
     *  See execution.rst for discussion.
     */

 restart_execution:

    /* Lookup current thread; use the stable 'entry_thread' for this to
     * avoid clobber warnings.  Any valid, reachable 'thr' value would be
     * fine for this, so using 'entry_thread' is just to silence warnings.
     */
    thr = entry_thread->heap->curr_thread;
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(thr->callstack_top >= 1);
    DUK_ASSERT(thr->callstack_curr != NULL);
    DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL);
    DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr)));

    DUK_GC_TORTURE(thr->heap);

    thr->ptr_curr_pc = &curr_pc;

    /* Relookup and initialize dispatch loop variables.  Debugger check. */
    {
        duk_activation *act;
#if !defined(DUK_USE_EXEC_FUN_LOCAL)
        duk_hcompfunc *fun;
#endif

        /* Assume interrupt init/counter are properly initialized here. */
        /* Assume that thr->valstack_bottom has been set-up before getting here. */

        act = thr->callstack_curr;
        DUK_ASSERT(act != NULL);
        fun = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act);
        DUK_ASSERT(fun != NULL);
        DUK_ASSERT(thr->valstack_top - thr->valstack_bottom == fun->nregs);
        consts = DUK_HCOMPFUNC_GET_CONSTS_BASE(thr->heap, fun);
        DUK_ASSERT(consts != NULL);

#if defined(DUK_USE_DEBUGGER_SUPPORT)
        if (DUK_UNLIKELY(duk_debug_is_attached(thr->heap) && !thr->heap->dbg_processing)) {
            duk__executor_recheck_debugger(thr, act, fun);
            DUK_ASSERT(act == thr->callstack_curr);
            DUK_ASSERT(act != NULL);
        }
#endif  /* DUK_USE_DEBUGGER_SUPPORT */

#if defined(DUK_USE_ASSERTIONS)
        valstack_top_base = (duk_size_t) (thr->valstack_top - thr->valstack);
#endif

        /* Set up curr_pc for opcode dispatch. */
        curr_pc = act->curr_pc;
    }

    DUK_DD(DUK_DDPRINT("restarting execution, thr %p, act idx %ld, fun %p,"
                       "consts %p, funcs %p, lev %ld, regbot %ld, regtop %ld, "
                       "preventcount=%ld",
                       (void *) thr,
                       (long) (thr->callstack_top - 1),
                       (void *) DUK__FUN(),
                       (void *) DUK_HCOMPFUNC_GET_CONSTS_BASE(thr->heap, DUK__FUN()),
                       (void *) DUK_HCOMPFUNC_GET_FUNCS_BASE(thr->heap, DUK__FUN()),
                       (long) (thr->callstack_top - 1),
                       (long) (thr->valstack_bottom - thr->valstack),
                       (long) (thr->valstack_top - thr->valstack),
                       (long) thr->callstack_preventcount));

    /* Dispatch loop. */

    for (;;) {
        duk_uint8_t op;

        DUK_ASSERT(thr->callstack_top >= 1);
        DUK_ASSERT(thr->valstack_top - thr->valstack_bottom == DUK__FUN()->nregs);
        DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack) == valstack_top_base);

        /* Executor interrupt counter check, used to implement breakpoints,
         * debugging interface, execution timeouts, etc.  The counter is heap
         * specific but is maintained in the current thread to make the check
         * as fast as possible.  The counter is copied back to the heap struct
         * whenever a thread switch occurs by the DUK_HEAP_SWITCH_THREAD() macro.
         */
#if defined(DUK_USE_INTERRUPT_COUNTER)
        int_ctr = thr->interrupt_counter;
        if (DUK_LIKELY(int_ctr > 0)) {
            thr->interrupt_counter = int_ctr - 1;
        } else {
            /* Trigger at zero or below */
            duk_small_uint_t exec_int_ret;

            DUK_STATS_INC(thr->heap, stats_exec_interrupt);

            /* Write curr_pc back for the debugger. */
            {
                duk_activation *act;
                DUK_ASSERT(thr->callstack_top > 0);
                act = thr->callstack_curr;
                DUK_ASSERT(act != NULL);
                act->curr_pc = (duk_instr_t *) curr_pc;
            }

            /* Forced restart caused by a function return; must recheck
             * debugger breakpoints before checking line transitions,
             * see GH-303.  Restart and then handle interrupt_counter
             * zero again.
             */
#if defined(DUK_USE_DEBUGGER_SUPPORT)
            if (thr->heap->dbg_force_restart) {
                DUK_DD(DUK_DDPRINT("dbg_force_restart flag forced restart execution"));  /* GH-303 */
                thr->heap->dbg_force_restart = 0;
                goto restart_execution;
            }
#endif

            exec_int_ret = duk__executor_interrupt(thr);
            if (exec_int_ret == DUK__INT_RESTART) {
                /* curr_pc synced back above */
                goto restart_execution;
            }
        }
#endif  /* DUK_USE_INTERRUPT_COUNTER */
#if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG)
        /* For cross-checking during development: ensure dispatch count
         * matches cumulative interrupt counter init value sums.
         */
        thr->heap->inst_count_exec++;
#endif

#if defined(DUK_USE_ASSERTIONS) || defined(DUK_USE_DEBUG)
        {
            duk_activation *act;
            act = thr->callstack_curr;
            DUK_ASSERT(curr_pc >= DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, DUK__FUN()));
            DUK_ASSERT(curr_pc < DUK_HCOMPFUNC_GET_CODE_END(thr->heap, DUK__FUN()));
            DUK_UNREF(act);  /* if debugging disabled */

            DUK_DDD(DUK_DDDPRINT("executing bytecode: pc=%ld, ins=0x%08lx, op=%ld, valstack_top=%ld/%ld, nregs=%ld  -->  %!I",
                                 (long) (curr_pc - DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, DUK__FUN())),
                                 (unsigned long) *curr_pc,
                                 (long) DUK_DEC_OP(*curr_pc),
                                 (long) (thr->valstack_top - thr->valstack),
                                 (long) (thr->valstack_end - thr->valstack),
                                 (long) (DUK__FUN() ? DUK__FUN()->nregs : -1),
                                 (duk_instr_t) *curr_pc));
        }
#endif

#if defined(DUK_USE_ASSERTIONS)
        /* Quite heavy assert: check valstack policy.  Improper
         * shuffle instructions can write beyond valstack_top/end
         * so this check catches them in the act.
         */
        {
            duk_tval *tv;
            tv = thr->valstack_top;
            while (tv != thr->valstack_end) {
                DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv));
                tv++;
            }
        }
#endif

        ins = *curr_pc++;
        DUK_STATS_INC(thr->heap, stats_exec_opcodes);

        /* Typing: use duk_small_(u)int_fast_t when decoding small
         * opcode fields (op, A, B, C, BC) which fit into 16 bits
         * and duk_(u)int_fast_t when decoding larger fields (e.g.
         * ABC).  Use unsigned variant by default, signed when the
         * value is used in signed arithmetic.  Using variable names
         * such as 'a', 'b', 'c', 'bc', etc makes it easier to spot
         * typing mismatches.
         */

        /* Switch based on opcode.  Cast to 8-bit unsigned value and
         * use a fully populated case clauses so that the compiler
         * will (at least usually) omit a bounds check.
         */
        op = (duk_uint8_t) DUK_DEC_OP(ins);
        switch (op) {

        /* Some useful macros.  These access inner executor variables
         * directly so they only apply within the executor.
         */
#if defined(DUK_USE_EXEC_PREFER_SIZE)
#define DUK__REPLACE_TOP_A_BREAK() { goto replace_top_a; }
#define DUK__REPLACE_TOP_BC_BREAK() { goto replace_top_bc; }
#define DUK__REPLACE_BOOL_A_BREAK(bval) { \
        duk_bool_t duk__bval; \
        duk__bval = (bval); \
        DUK_ASSERT(duk__bval == 0 || duk__bval == 1); \
        duk_push_boolean(thr, duk__bval); \
        DUK__REPLACE_TOP_A_BREAK(); \
    }
#else
#define DUK__REPLACE_TOP_A_BREAK() { DUK__REPLACE_TO_TVPTR(thr, DUK__REGP_A(ins)); break; }
#define DUK__REPLACE_TOP_BC_BREAK() { DUK__REPLACE_TO_TVPTR(thr, DUK__REGP_BC(ins)); break; }
#define DUK__REPLACE_BOOL_A_BREAK(bval) { \
        duk_bool_t duk__bval; \
        duk_tval *duk__tvdst; \
        duk__bval = (bval); \
        DUK_ASSERT(duk__bval == 0 || duk__bval == 1); \
        duk__tvdst = DUK__REGP_A(ins); \
        DUK_TVAL_SET_BOOLEAN_UPDREF(thr, duk__tvdst, duk__bval); \
        break; \
    }
#endif

        /* XXX: 12 + 12 bit variant might make sense too, for both reg and
         * const loads.
         */

        /* For LDREG, STREG, LDCONST footprint optimized variants would just
         * duk_dup() + duk_replace(), but because they're used quite a lot
         * they're currently intentionally not size optimized.
         */
        case DUK_OP_LDREG: {
            duk_tval *tv1, *tv2;

            tv1 = DUK__REGP_A(ins);
            tv2 = DUK__REGP_BC(ins);
            DUK_TVAL_SET_TVAL_UPDREF_FAST(thr, tv1, tv2);  /* side effects */
            break;
        }

        case DUK_OP_STREG: {
            duk_tval *tv1, *tv2;

            tv1 = DUK__REGP_A(ins);
            tv2 = DUK__REGP_BC(ins);
            DUK_TVAL_SET_TVAL_UPDREF_FAST(thr, tv2, tv1);  /* side effects */
            break;
        }

        case DUK_OP_LDCONST: {
            duk_tval *tv1, *tv2;

            tv1 = DUK__REGP_A(ins);
            tv2 = DUK__CONSTP_BC(ins);
            DUK_TVAL_SET_TVAL_UPDREF_FAST(thr, tv1, tv2);  /* side effects */
            break;
        }

        /* LDINT and LDINTX are intended to load an arbitrary signed
         * 32-bit value.  Only an LDINT+LDINTX sequence is supported.
         * This also guarantees all values remain fastints.
         */
#if defined(DUK_USE_EXEC_PREFER_SIZE)
        case DUK_OP_LDINT: {
            duk_int32_t val;

            val = (duk_int32_t) DUK_DEC_BC(ins) - (duk_int32_t) DUK_BC_LDINT_BIAS;
            duk_push_int(thr, val);
            DUK__REPLACE_TOP_A_BREAK();
        }
        case DUK_OP_LDINTX: {
            duk_int32_t val;

            val = (duk_int32_t) duk_get_int(thr, DUK_DEC_A(ins));
            val = (val << DUK_BC_LDINTX_SHIFT) + (duk_int32_t) DUK_DEC_BC(ins);  /* no bias */
            duk_push_int(thr, val);
            DUK__REPLACE_TOP_A_BREAK();
        }
#else  /* DUK_USE_EXEC_PREFER_SIZE */
        case DUK_OP_LDINT: {
            duk_tval *tv1;
            duk_int32_t val;

            val = (duk_int32_t) DUK_DEC_BC(ins) - (duk_int32_t) DUK_BC_LDINT_BIAS;
            tv1 = DUK__REGP_A(ins);
            DUK_TVAL_SET_I32_UPDREF(thr, tv1, val);  /* side effects */
            break;
        }
        case DUK_OP_LDINTX: {
            duk_tval *tv1;
            duk_int32_t val;

            tv1 = DUK__REGP_A(ins);
            DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv1));
#if defined(DUK_USE_FASTINT)
            DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv1));
            val = DUK_TVAL_GET_FASTINT_I32(tv1);
#else
            /* XXX: fast double-to-int conversion, we know number is integer in [-0x80000000,0xffffffff]. */
            val = (duk_int32_t) DUK_TVAL_GET_NUMBER(tv1);
#endif
            val = (duk_int32_t) ((duk_uint32_t) val << DUK_BC_LDINTX_SHIFT) + (duk_int32_t) DUK_DEC_BC(ins);  /* no bias */
            DUK_TVAL_SET_I32_UPDREF(thr, tv1, val);  /* side effects */
            break;
        }
#endif  /* DUK_USE_EXEC_PREFER_SIZE */

#if defined(DUK_USE_EXEC_PREFER_SIZE)
        case DUK_OP_LDTHIS: {
            duk_push_this(thr);
            DUK__REPLACE_TOP_BC_BREAK();
        }
        case DUK_OP_LDUNDEF: {
            duk_to_undefined(thr, (duk_idx_t) DUK_DEC_BC(ins));
            break;
        }
        case DUK_OP_LDNULL: {
            duk_to_null(thr, (duk_idx_t) DUK_DEC_BC(ins));
            break;
        }
        case DUK_OP_LDTRUE: {
            duk_push_true(thr);
            DUK__REPLACE_TOP_BC_BREAK();
        }
        case DUK_OP_LDFALSE: {
            duk_push_false(thr);
            DUK__REPLACE_TOP_BC_BREAK();
        }
#else  /* DUK_USE_EXEC_PREFER_SIZE */
        case DUK_OP_LDTHIS: {
            /* Note: 'this' may be bound to any value, not just an object */
            duk_tval *tv1, *tv2;

            tv1 = DUK__REGP_BC(ins);
            tv2 = thr->valstack_bottom - 1;  /* 'this binding' is just under bottom */
            DUK_ASSERT(tv2 >= thr->valstack);
            DUK_TVAL_SET_TVAL_UPDREF_FAST(thr, tv1, tv2);  /* side effects */
            break;
        }
        case DUK_OP_LDUNDEF: {
            duk_tval *tv1;

            tv1 = DUK__REGP_BC(ins);
            DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv1);  /* side effects */
            break;
        }
        case DUK_OP_LDNULL: {
            duk_tval *tv1;

            tv1 = DUK__REGP_BC(ins);
            DUK_TVAL_SET_NULL_UPDREF(thr, tv1);  /* side effects */
            break;
        }
        case DUK_OP_LDTRUE: {
            duk_tval *tv1;

            tv1 = DUK__REGP_BC(ins);
            DUK_TVAL_SET_BOOLEAN_UPDREF(thr, tv1, 1);  /* side effects */
            break;
        }
        case DUK_OP_LDFALSE: {
            duk_tval *tv1;

            tv1 = DUK__REGP_BC(ins);
            DUK_TVAL_SET_BOOLEAN_UPDREF(thr, tv1, 0);  /* side effects */
            break;
        }
#endif  /* DUK_USE_EXEC_PREFER_SIZE */

        case DUK_OP_BNOT: {
            duk__vm_bitwise_not(thr, DUK_DEC_BC(ins), DUK_DEC_A(ins));
            break;
        }

        case DUK_OP_LNOT: {
            duk__vm_logical_not(thr, DUK_DEC_BC(ins), DUK_DEC_A(ins));
            break;
        }

#if defined(DUK_USE_EXEC_PREFER_SIZE)
        case DUK_OP_UNM:
        case DUK_OP_UNP: {
            duk__vm_arith_unary_op(thr, DUK_DEC_BC(ins), DUK_DEC_A(ins), op);
            break;
        }
#else  /* DUK_USE_EXEC_PREFER_SIZE */
        case DUK_OP_UNM: {
            duk__vm_arith_unary_op(thr, DUK_DEC_BC(ins), DUK_DEC_A(ins), DUK_OP_UNM);
            break;
        }
        case DUK_OP_UNP: {
            duk__vm_arith_unary_op(thr, DUK_DEC_BC(ins), DUK_DEC_A(ins), DUK_OP_UNP);
            break;
        }
#endif  /* DUK_USE_EXEC_PREFER_SIZE */

#if defined(DUK_USE_EXEC_PREFER_SIZE)
        case DUK_OP_TYPEOF: {
            duk_small_uint_t stridx;

            stridx = duk_js_typeof_stridx(DUK__REGP_BC(ins));
            DUK_ASSERT_STRIDX_VALID(stridx);
            duk_push_hstring_stridx(thr, stridx);
            DUK__REPLACE_TOP_A_BREAK();
        }
#else  /* DUK_USE_EXEC_PREFER_SIZE */
        case DUK_OP_TYPEOF: {
            duk_tval *tv;
            duk_small_uint_t stridx;
            duk_hstring *h_str;

            tv = DUK__REGP_BC(ins);
            stridx = duk_js_typeof_stridx(tv);
            DUK_ASSERT_STRIDX_VALID(stridx);
            h_str = DUK_HTHREAD_GET_STRING(thr, stridx);
            tv = DUK__REGP_A(ins);
            DUK_TVAL_SET_STRING_UPDREF(thr, tv, h_str);
            break;
        }
#endif  /* DUK_USE_EXEC_PREFER_SIZE */

        case DUK_OP_TYPEOFID: {
            duk_small_uint_t stridx;
#if !defined(DUK_USE_EXEC_PREFER_SIZE)
            duk_hstring *h_str;
#endif
            duk_activation *act;
            duk_hstring *name;
            duk_tval *tv;

            /* A -> target register
             * BC -> constant index of identifier name
             */

            tv = DUK__CONSTP_BC(ins);
            DUK_ASSERT(DUK_TVAL_IS_STRING(tv));
            name = DUK_TVAL_GET_STRING(tv);
            tv = NULL;  /* lookup has side effects */
            act = thr->callstack_curr;
            if (duk_js_getvar_activation(thr, act, name, 0 /*throw*/)) {
                /* -> [... val this] */
                tv = DUK_GET_TVAL_NEGIDX(thr, -2);
                stridx = duk_js_typeof_stridx(tv);
                tv = NULL;  /* no longer needed */
                duk_pop_2_unsafe(thr);
            } else {
                /* unresolvable, no stack changes */
                stridx = DUK_STRIDX_LC_UNDEFINED;
            }
            DUK_ASSERT_STRIDX_VALID(stridx);
#if defined(DUK_USE_EXEC_PREFER_SIZE)
            duk_push_hstring_stridx(thr, stridx);
            DUK__REPLACE_TOP_A_BREAK();
#else  /* DUK_USE_EXEC_PREFER_SIZE */
            h_str = DUK_HTHREAD_GET_STRING(thr, stridx);
            tv = DUK__REGP_A(ins);
            DUK_TVAL_SET_STRING_UPDREF(thr, tv, h_str);
            break;
#endif  /* DUK_USE_EXEC_PREFER_SIZE */
        }

        /* Equality: E5 Sections 11.9.1, 11.9.3 */

#define DUK__EQ_BODY(barg,carg) { \
        duk_bool_t tmp; \
        tmp = duk_js_equals(thr, (barg), (carg)); \
        DUK_ASSERT(tmp == 0 || tmp == 1); \
        DUK__REPLACE_BOOL_A_BREAK(tmp); \
    }
#define DUK__NEQ_BODY(barg,carg) { \
        duk_bool_t tmp; \
        tmp = duk_js_equals(thr, (barg), (carg)); \
        DUK_ASSERT(tmp == 0 || tmp == 1); \
        tmp ^= 1; \
        DUK__REPLACE_BOOL_A_BREAK(tmp); \
    }
#define DUK__SEQ_BODY(barg,carg) { \
        duk_bool_t tmp; \
        tmp = duk_js_strict_equals((barg), (carg)); \
        DUK_ASSERT(tmp == 0 || tmp == 1); \
        DUK__REPLACE_BOOL_A_BREAK(tmp); \
    }
#define DUK__SNEQ_BODY(barg,carg) { \
        duk_bool_t tmp; \
        tmp = duk_js_strict_equals((barg), (carg)); \
        DUK_ASSERT(tmp == 0 || tmp == 1); \
        tmp ^= 1; \
        DUK__REPLACE_BOOL_A_BREAK(tmp); \
    }
#if defined(DUK_USE_EXEC_PREFER_SIZE)
        case DUK_OP_EQ_RR:
        case DUK_OP_EQ_CR:
        case DUK_OP_EQ_RC:
        case DUK_OP_EQ_CC:
            DUK__EQ_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins));
        case DUK_OP_NEQ_RR:
        case DUK_OP_NEQ_CR:
        case DUK_OP_NEQ_RC:
        case DUK_OP_NEQ_CC:
            DUK__NEQ_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins));
        case DUK_OP_SEQ_RR:
        case DUK_OP_SEQ_CR:
        case DUK_OP_SEQ_RC:
        case DUK_OP_SEQ_CC:
            DUK__SEQ_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins));
        case DUK_OP_SNEQ_RR:
        case DUK_OP_SNEQ_CR:
        case DUK_OP_SNEQ_RC:
        case DUK_OP_SNEQ_CC:
            DUK__SNEQ_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins));
#else  /* DUK_USE_EXEC_PREFER_SIZE */
        case DUK_OP_EQ_RR:
            DUK__EQ_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_EQ_CR:
            DUK__EQ_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_EQ_RC:
            DUK__EQ_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins));
        case DUK_OP_EQ_CC:
            DUK__EQ_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins));
        case DUK_OP_NEQ_RR:
            DUK__NEQ_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_NEQ_CR:
            DUK__NEQ_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_NEQ_RC:
            DUK__NEQ_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins));
        case DUK_OP_NEQ_CC:
            DUK__NEQ_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins));
        case DUK_OP_SEQ_RR:
            DUK__SEQ_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_SEQ_CR:
            DUK__SEQ_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_SEQ_RC:
            DUK__SEQ_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins));
        case DUK_OP_SEQ_CC:
            DUK__SEQ_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins));
        case DUK_OP_SNEQ_RR:
            DUK__SNEQ_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_SNEQ_CR:
            DUK__SNEQ_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_SNEQ_RC:
            DUK__SNEQ_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins));
        case DUK_OP_SNEQ_CC:
            DUK__SNEQ_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins));
#endif  /* DUK_USE_EXEC_PREFER_SIZE */

#define DUK__COMPARE_BODY(arg1,arg2,flags) { \
        duk_bool_t tmp; \
        tmp = duk_js_compare_helper(thr, (arg1), (arg2), (flags)); \
        DUK_ASSERT(tmp == 0 || tmp == 1); \
        DUK__REPLACE_BOOL_A_BREAK(tmp); \
    }
#define DUK__GT_BODY(barg,carg) DUK__COMPARE_BODY((carg), (barg), 0)
#define DUK__GE_BODY(barg,carg) DUK__COMPARE_BODY((barg), (carg), DUK_COMPARE_FLAG_EVAL_LEFT_FIRST | DUK_COMPARE_FLAG_NEGATE)
#define DUK__LT_BODY(barg,carg) DUK__COMPARE_BODY((barg), (carg), DUK_COMPARE_FLAG_EVAL_LEFT_FIRST)
#define DUK__LE_BODY(barg,carg) DUK__COMPARE_BODY((carg), (barg), DUK_COMPARE_FLAG_NEGATE)
#if defined(DUK_USE_EXEC_PREFER_SIZE)
        case DUK_OP_GT_RR:
        case DUK_OP_GT_CR:
        case DUK_OP_GT_RC:
        case DUK_OP_GT_CC:
            DUK__GT_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins));
        case DUK_OP_GE_RR:
        case DUK_OP_GE_CR:
        case DUK_OP_GE_RC:
        case DUK_OP_GE_CC:
            DUK__GE_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins));
        case DUK_OP_LT_RR:
        case DUK_OP_LT_CR:
        case DUK_OP_LT_RC:
        case DUK_OP_LT_CC:
            DUK__LT_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins));
        case DUK_OP_LE_RR:
        case DUK_OP_LE_CR:
        case DUK_OP_LE_RC:
        case DUK_OP_LE_CC:
            DUK__LE_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins));
#else  /* DUK_USE_EXEC_PREFER_SIZE */
        case DUK_OP_GT_RR:
            DUK__GT_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_GT_CR:
            DUK__GT_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_GT_RC:
            DUK__GT_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins));
        case DUK_OP_GT_CC:
            DUK__GT_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins));
        case DUK_OP_GE_RR:
            DUK__GE_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_GE_CR:
            DUK__GE_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_GE_RC:
            DUK__GE_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins));
        case DUK_OP_GE_CC:
            DUK__GE_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins));
        case DUK_OP_LT_RR:
            DUK__LT_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_LT_CR:
            DUK__LT_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_LT_RC:
            DUK__LT_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins));
        case DUK_OP_LT_CC:
            DUK__LT_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins));
        case DUK_OP_LE_RR:
            DUK__LE_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_LE_CR:
            DUK__LE_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_LE_RC:
            DUK__LE_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins));
        case DUK_OP_LE_CC:
            DUK__LE_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins));
#endif  /* DUK_USE_EXEC_PREFER_SIZE */

        /* No size optimized variant at present for IF. */
        case DUK_OP_IFTRUE_R: {
            if (duk_js_toboolean(DUK__REGP_BC(ins)) != 0) {
                curr_pc++;
            }
            break;
        }
        case DUK_OP_IFTRUE_C: {
            if (duk_js_toboolean(DUK__CONSTP_BC(ins)) != 0) {
                curr_pc++;
            }
            break;
        }
        case DUK_OP_IFFALSE_R: {
            if (duk_js_toboolean(DUK__REGP_BC(ins)) == 0) {
                curr_pc++;
            }
            break;
        }
        case DUK_OP_IFFALSE_C: {
            if (duk_js_toboolean(DUK__CONSTP_BC(ins)) == 0) {
                curr_pc++;
            }
            break;
        }

#if defined(DUK_USE_EXEC_PREFER_SIZE)
        case DUK_OP_ADD_RR:
        case DUK_OP_ADD_CR:
        case DUK_OP_ADD_RC:
        case DUK_OP_ADD_CC: {
            /* XXX: could leave value on stack top and goto replace_top_a; */
            duk__vm_arith_add(thr, DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins), DUK_DEC_A(ins));
            break;
        }
#else  /* DUK_USE_EXEC_PREFER_SIZE */
        case DUK_OP_ADD_RR: {
            duk__vm_arith_add(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins));
            break;
        }
        case DUK_OP_ADD_CR: {
            duk__vm_arith_add(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins));
            break;
        }
        case DUK_OP_ADD_RC: {
            duk__vm_arith_add(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins));
            break;
        }
        case DUK_OP_ADD_CC: {
            duk__vm_arith_add(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins));
            break;
        }
#endif  /* DUK_USE_EXEC_PREFER_SIZE */

#if defined(DUK_USE_EXEC_PREFER_SIZE)
        case DUK_OP_SUB_RR:
        case DUK_OP_SUB_CR:
        case DUK_OP_SUB_RC:
        case DUK_OP_SUB_CC:
        case DUK_OP_MUL_RR:
        case DUK_OP_MUL_CR:
        case DUK_OP_MUL_RC:
        case DUK_OP_MUL_CC:
        case DUK_OP_DIV_RR:
        case DUK_OP_DIV_CR:
        case DUK_OP_DIV_RC:
        case DUK_OP_DIV_CC:
        case DUK_OP_MOD_RR:
        case DUK_OP_MOD_CR:
        case DUK_OP_MOD_RC:
        case DUK_OP_MOD_CC:
#if defined(DUK_USE_ES7_EXP_OPERATOR)
        case DUK_OP_EXP_RR:
        case DUK_OP_EXP_CR:
        case DUK_OP_EXP_RC:
        case DUK_OP_EXP_CC:
#endif  /* DUK_USE_ES7_EXP_OPERATOR */
        {
            /* XXX: could leave value on stack top and goto replace_top_a; */
            duk__vm_arith_binary_op(thr, DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins), DUK_DEC_A(ins), op);
            break;
        }
#else  /* DUK_USE_EXEC_PREFER_SIZE */
        case DUK_OP_SUB_RR: {
            duk__vm_arith_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_SUB);
            break;
        }
        case DUK_OP_SUB_CR: {
            duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_SUB);
            break;
        }
        case DUK_OP_SUB_RC: {
            duk__vm_arith_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_SUB);
            break;
        }
        case DUK_OP_SUB_CC: {
            duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_SUB);
            break;
        }
        case DUK_OP_MUL_RR: {
            duk__vm_arith_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_MUL);
            break;
        }
        case DUK_OP_MUL_CR: {
            duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_MUL);
            break;
        }
        case DUK_OP_MUL_RC: {
            duk__vm_arith_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_MUL);
            break;
        }
        case DUK_OP_MUL_CC: {
            duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_MUL);
            break;
        }
        case DUK_OP_DIV_RR: {
            duk__vm_arith_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_DIV);
            break;
        }
        case DUK_OP_DIV_CR: {
            duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_DIV);
            break;
        }
        case DUK_OP_DIV_RC: {
            duk__vm_arith_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_DIV);
            break;
        }
        case DUK_OP_DIV_CC: {
            duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_DIV);
            break;
        }
        case DUK_OP_MOD_RR: {
            duk__vm_arith_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_MOD);
            break;
        }
        case DUK_OP_MOD_CR: {
            duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_MOD);
            break;
        }
        case DUK_OP_MOD_RC: {
            duk__vm_arith_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_MOD);
            break;
        }
        case DUK_OP_MOD_CC: {
            duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_MOD);
            break;
        }
#if defined(DUK_USE_ES7_EXP_OPERATOR)
        case DUK_OP_EXP_RR: {
            duk__vm_arith_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_EXP);
            break;
        }
        case DUK_OP_EXP_CR: {
            duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_EXP);
            break;
        }
        case DUK_OP_EXP_RC: {
            duk__vm_arith_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_EXP);
            break;
        }
        case DUK_OP_EXP_CC: {
            duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_EXP);
            break;
        }
#endif  /* DUK_USE_ES7_EXP_OPERATOR */
#endif  /* DUK_USE_EXEC_PREFER_SIZE */

#if defined(DUK_USE_EXEC_PREFER_SIZE)
        case DUK_OP_BAND_RR:
        case DUK_OP_BAND_CR:
        case DUK_OP_BAND_RC:
        case DUK_OP_BAND_CC:
        case DUK_OP_BOR_RR:
        case DUK_OP_BOR_CR:
        case DUK_OP_BOR_RC:
        case DUK_OP_BOR_CC:
        case DUK_OP_BXOR_RR:
        case DUK_OP_BXOR_CR:
        case DUK_OP_BXOR_RC:
        case DUK_OP_BXOR_CC:
        case DUK_OP_BASL_RR:
        case DUK_OP_BASL_CR:
        case DUK_OP_BASL_RC:
        case DUK_OP_BASL_CC:
        case DUK_OP_BLSR_RR:
        case DUK_OP_BLSR_CR:
        case DUK_OP_BLSR_RC:
        case DUK_OP_BLSR_CC:
        case DUK_OP_BASR_RR:
        case DUK_OP_BASR_CR:
        case DUK_OP_BASR_RC:
        case DUK_OP_BASR_CC: {
            /* XXX: could leave value on stack top and goto replace_top_a; */
            duk__vm_bitwise_binary_op(thr, DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins), DUK_DEC_A(ins), op);
            break;
        }
#else  /* DUK_USE_EXEC_PREFER_SIZE */
        case DUK_OP_BAND_RR: {
            duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BAND);
            break;
        }
        case DUK_OP_BAND_CR: {
            duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BAND);
            break;
        }
        case DUK_OP_BAND_RC: {
            duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BAND);
            break;
        }
        case DUK_OP_BAND_CC: {
            duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BAND);
            break;
        }
        case DUK_OP_BOR_RR: {
            duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BOR);
            break;
        }
        case DUK_OP_BOR_CR: {
            duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BOR);
            break;
        }
        case DUK_OP_BOR_RC: {
            duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BOR);
            break;
        }
        case DUK_OP_BOR_CC: {
            duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BOR);
            break;
        }
        case DUK_OP_BXOR_RR: {
            duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BXOR);
            break;
        }
        case DUK_OP_BXOR_CR: {
            duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BXOR);
            break;
        }
        case DUK_OP_BXOR_RC: {
            duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BXOR);
            break;
        }
        case DUK_OP_BXOR_CC: {
            duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BXOR);
            break;
        }
        case DUK_OP_BASL_RR: {
            duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BASL);
            break;
        }
        case DUK_OP_BASL_CR: {
            duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BASL);
            break;
        }
        case DUK_OP_BASL_RC: {
            duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BASL);
            break;
        }
        case DUK_OP_BASL_CC: {
            duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BASL);
            break;
        }
        case DUK_OP_BLSR_RR: {
            duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BLSR);
            break;
        }
        case DUK_OP_BLSR_CR: {
            duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BLSR);
            break;
        }
        case DUK_OP_BLSR_RC: {
            duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BLSR);
            break;
        }
        case DUK_OP_BLSR_CC: {
            duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BLSR);
            break;
        }
        case DUK_OP_BASR_RR: {
            duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BASR);
            break;
        }
        case DUK_OP_BASR_CR: {
            duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BASR);
            break;
        }
        case DUK_OP_BASR_RC: {
            duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BASR);
            break;
        }
        case DUK_OP_BASR_CC: {
            duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BASR);
            break;
        }
#endif  /* DUK_USE_EXEC_PREFER_SIZE */

        /* For INSTOF and IN, B is always a register. */
#define DUK__INSTOF_BODY(barg,carg) { \
        duk_bool_t tmp; \
        tmp = duk_js_instanceof(thr, (barg), (carg)); \
        DUK_ASSERT(tmp == 0 || tmp == 1); \
        DUK__REPLACE_BOOL_A_BREAK(tmp); \
    }
#define DUK__IN_BODY(barg,carg) { \
        duk_bool_t tmp; \
        tmp = duk_js_in(thr, (barg), (carg)); \
        DUK_ASSERT(tmp == 0 || tmp == 1); \
        DUK__REPLACE_BOOL_A_BREAK(tmp); \
    }
#if defined(DUK_USE_EXEC_PREFER_SIZE)
        case DUK_OP_INSTOF_RR:
        case DUK_OP_INSTOF_CR:
        case DUK_OP_INSTOF_RC:
        case DUK_OP_INSTOF_CC:
            DUK__INSTOF_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins));
        case DUK_OP_IN_RR:
        case DUK_OP_IN_CR:
        case DUK_OP_IN_RC:
        case DUK_OP_IN_CC:
            DUK__IN_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins));
#else  /* DUK_USE_EXEC_PREFER_SIZE */
        case DUK_OP_INSTOF_RR:
            DUK__INSTOF_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_INSTOF_CR:
            DUK__INSTOF_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_INSTOF_RC:
            DUK__INSTOF_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins));
        case DUK_OP_INSTOF_CC:
            DUK__INSTOF_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins));
        case DUK_OP_IN_RR:
            DUK__IN_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_IN_CR:
            DUK__IN_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_IN_RC:
            DUK__IN_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins));
        case DUK_OP_IN_CC:
            DUK__IN_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins));
#endif  /* DUK_USE_EXEC_PREFER_SIZE */

        /* Pre/post inc/dec for register variables, important for loops. */
#if defined(DUK_USE_EXEC_PREFER_SIZE)
        case DUK_OP_PREINCR:
        case DUK_OP_PREDECR:
        case DUK_OP_POSTINCR:
        case DUK_OP_POSTDECR: {
            duk__prepost_incdec_reg_helper(thr, DUK__REGP_A(ins), DUK__REGP_BC(ins), op);
            break;
        }
        case DUK_OP_PREINCV:
        case DUK_OP_PREDECV:
        case DUK_OP_POSTINCV:
        case DUK_OP_POSTDECV: {
            duk__prepost_incdec_var_helper(thr, DUK_DEC_A(ins), DUK__CONSTP_BC(ins), op, DUK__STRICT());
            break;
        }
#else  /* DUK_USE_EXEC_PREFER_SIZE */
        case DUK_OP_PREINCR: {
            duk__prepost_incdec_reg_helper(thr, DUK__REGP_A(ins), DUK__REGP_BC(ins), DUK_OP_PREINCR);
            break;
        }
        case DUK_OP_PREDECR: {
            duk__prepost_incdec_reg_helper(thr, DUK__REGP_A(ins), DUK__REGP_BC(ins), DUK_OP_PREDECR);
            break;
        }
        case DUK_OP_POSTINCR: {
            duk__prepost_incdec_reg_helper(thr, DUK__REGP_A(ins), DUK__REGP_BC(ins), DUK_OP_POSTINCR);
            break;
        }
        case DUK_OP_POSTDECR: {
            duk__prepost_incdec_reg_helper(thr, DUK__REGP_A(ins), DUK__REGP_BC(ins), DUK_OP_POSTDECR);
            break;
        }
        case DUK_OP_PREINCV: {
            duk__prepost_incdec_var_helper(thr, DUK_DEC_A(ins), DUK__CONSTP_BC(ins), DUK_OP_PREINCV, DUK__STRICT());
            break;
        }
        case DUK_OP_PREDECV: {
            duk__prepost_incdec_var_helper(thr, DUK_DEC_A(ins), DUK__CONSTP_BC(ins), DUK_OP_PREDECV, DUK__STRICT());
            break;
        }
        case DUK_OP_POSTINCV: {
            duk__prepost_incdec_var_helper(thr, DUK_DEC_A(ins), DUK__CONSTP_BC(ins), DUK_OP_POSTINCV, DUK__STRICT());
            break;
        }
        case DUK_OP_POSTDECV: {
            duk__prepost_incdec_var_helper(thr, DUK_DEC_A(ins), DUK__CONSTP_BC(ins), DUK_OP_POSTDECV, DUK__STRICT());
            break;
        }
#endif  /* DUK_USE_EXEC_PREFER_SIZE */

        /* XXX: Move to separate helper, optimize for perf/size separately. */
        /* Preinc/predec for object properties. */
        case DUK_OP_PREINCP_RR:
        case DUK_OP_PREINCP_CR:
        case DUK_OP_PREINCP_RC:
        case DUK_OP_PREINCP_CC:
        case DUK_OP_PREDECP_RR:
        case DUK_OP_PREDECP_CR:
        case DUK_OP_PREDECP_RC:
        case DUK_OP_PREDECP_CC:
        case DUK_OP_POSTINCP_RR:
        case DUK_OP_POSTINCP_CR:
        case DUK_OP_POSTINCP_RC:
        case DUK_OP_POSTINCP_CC:
        case DUK_OP_POSTDECP_RR:
        case DUK_OP_POSTDECP_CR:
        case DUK_OP_POSTDECP_RC:
        case DUK_OP_POSTDECP_CC: {
            duk_tval *tv_obj;
            duk_tval *tv_key;
            duk_tval *tv_val;
            duk_bool_t rc;
            duk_double_t x, y, z;
#if !defined(DUK_USE_EXEC_PREFER_SIZE)
            duk_tval *tv_dst;
#endif  /* DUK_USE_EXEC_PREFER_SIZE */

            /* A -> target reg
             * B -> object reg/const (may be const e.g. in "'foo'[1]")
             * C -> key reg/const
             */

            /* Opcode bits 0-1 are used to distinguish reg/const variants.
             * Opcode bits 2-3 are used to distinguish inc/dec variants:
             * Bit 2 = inc(0)/dec(1), bit 3 = pre(0)/post(1).
             */
            DUK_ASSERT((DUK_OP_PREINCP_RR & 0x0c) == 0x00);
            DUK_ASSERT((DUK_OP_PREDECP_RR & 0x0c) == 0x04);
            DUK_ASSERT((DUK_OP_POSTINCP_RR & 0x0c) == 0x08);
            DUK_ASSERT((DUK_OP_POSTDECP_RR & 0x0c) == 0x0c);

            tv_obj = DUK__REGCONSTP_B(ins);
            tv_key = DUK__REGCONSTP_C(ins);
            rc = duk_hobject_getprop(thr, tv_obj, tv_key);  /* -> [val] */
            DUK_UNREF(rc);  /* ignore */
            tv_obj = NULL;  /* invalidated */
            tv_key = NULL;  /* invalidated */

            /* XXX: Fastint fast path would be useful here.  Also fastints
             * now lose their fastint status in current handling which is
             * not intuitive.
             */

            x = duk_to_number_m1(thr);
            duk_pop_unsafe(thr);
            if (ins & DUK_BC_INCDECP_FLAG_DEC) {
                y = x - 1.0;
            } else {
                y = x + 1.0;
            }

            duk_push_number(thr, y);
            tv_val = DUK_GET_TVAL_NEGIDX(thr, -1);
            DUK_ASSERT(tv_val != NULL);
            tv_obj = DUK__REGCONSTP_B(ins);
            tv_key = DUK__REGCONSTP_C(ins);
            rc = duk_hobject_putprop(thr, tv_obj, tv_key, tv_val, DUK__STRICT());
            DUK_UNREF(rc);  /* ignore */
            tv_obj = NULL;  /* invalidated */
            tv_key = NULL;  /* invalidated */
            duk_pop_unsafe(thr);

            z = (ins & DUK_BC_INCDECP_FLAG_POST) ? x : y;
#if defined(DUK_USE_EXEC_PREFER_SIZE)
            duk_push_number(thr, z);
            DUK__REPLACE_TOP_A_BREAK();
#else
            tv_dst = DUK__REGP_A(ins);
            DUK_TVAL_SET_NUMBER_UPDREF(thr, tv_dst, z);
            break;
#endif
        }

        /* XXX: GETPROP where object is 'this', GETPROPT?
         * Occurs relatively often in object oriented code.
         */

#define DUK__GETPROP_BODY(barg,carg) { \
        /* A -> target reg \
         * B -> object reg/const (may be const e.g. in "'foo'[1]") \
         * C -> key reg/const \
         */ \
        (void) duk_hobject_getprop(thr, (barg), (carg)); \
        DUK__REPLACE_TOP_A_BREAK(); \
    }
#define DUK__GETPROPC_BODY(barg,carg) { \
        /* Same as GETPROP but callability check for property-based calls. */ \
        duk_tval *tv__targ; \
        (void) duk_hobject_getprop(thr, (barg), (carg)); \
        DUK_GC_TORTURE(thr->heap); \
        tv__targ = DUK_GET_TVAL_NEGIDX(thr, -1); \
        if (DUK_UNLIKELY(!duk_is_callable_tval(thr, tv__targ))) { \
            /* Here we intentionally re-evaluate the macro \
             * arguments to deal with potentially changed \
             * valstack base pointer! \
             */ \
            duk_call_setup_propcall_error(thr, (barg), (carg)); \
        } \
        DUK__REPLACE_TOP_A_BREAK(); \
    }
#define DUK__PUTPROP_BODY(aarg,barg,carg) { \
        /* A -> object reg \
         * B -> key reg/const \
         * C -> value reg/const \
         * \
         * Note: intentional difference to register arrangement \
         * of e.g. GETPROP; 'A' must contain a register-only value. \
         */ \
        (void) duk_hobject_putprop(thr, (aarg), (barg), (carg), DUK__STRICT()); \
        break; \
    }
#define DUK__DELPROP_BODY(barg,carg) { \
        /* A -> result reg \
         * B -> object reg \
         * C -> key reg/const \
         */ \
        duk_bool_t rc; \
        rc = duk_hobject_delprop(thr, (barg), (carg), DUK__STRICT()); \
        DUK_ASSERT(rc == 0 || rc == 1); \
        DUK__REPLACE_BOOL_A_BREAK(rc); \
    }
#if defined(DUK_USE_EXEC_PREFER_SIZE)
        case DUK_OP_GETPROP_RR:
        case DUK_OP_GETPROP_CR:
        case DUK_OP_GETPROP_RC:
        case DUK_OP_GETPROP_CC:
            DUK__GETPROP_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins));
#if defined(DUK_USE_VERBOSE_ERRORS)
        case DUK_OP_GETPROPC_RR:
        case DUK_OP_GETPROPC_CR:
        case DUK_OP_GETPROPC_RC:
        case DUK_OP_GETPROPC_CC:
            DUK__GETPROPC_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins));
#endif
        case DUK_OP_PUTPROP_RR:
        case DUK_OP_PUTPROP_CR:
        case DUK_OP_PUTPROP_RC:
        case DUK_OP_PUTPROP_CC:
            DUK__PUTPROP_BODY(DUK__REGP_A(ins), DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins));
        case DUK_OP_DELPROP_RR:
        case DUK_OP_DELPROP_RC:  /* B is always reg */
            DUK__DELPROP_BODY(DUK__REGP_B(ins), DUK__REGCONSTP_C(ins));
#else  /* DUK_USE_EXEC_PREFER_SIZE */
        case DUK_OP_GETPROP_RR:
            DUK__GETPROP_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_GETPROP_CR:
            DUK__GETPROP_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_GETPROP_RC:
            DUK__GETPROP_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins));
        case DUK_OP_GETPROP_CC:
            DUK__GETPROP_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins));
#if defined(DUK_USE_VERBOSE_ERRORS)
        case DUK_OP_GETPROPC_RR:
            DUK__GETPROPC_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_GETPROPC_CR:
            DUK__GETPROPC_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_GETPROPC_RC:
            DUK__GETPROPC_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins));
        case DUK_OP_GETPROPC_CC:
            DUK__GETPROPC_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins));
#endif
        case DUK_OP_PUTPROP_RR:
            DUK__PUTPROP_BODY(DUK__REGP_A(ins), DUK__REGP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_PUTPROP_CR:
            DUK__PUTPROP_BODY(DUK__REGP_A(ins), DUK__CONSTP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_PUTPROP_RC:
            DUK__PUTPROP_BODY(DUK__REGP_A(ins), DUK__REGP_B(ins), DUK__CONSTP_C(ins));
        case DUK_OP_PUTPROP_CC:
            DUK__PUTPROP_BODY(DUK__REGP_A(ins), DUK__CONSTP_B(ins), DUK__CONSTP_C(ins));
        case DUK_OP_DELPROP_RR:  /* B is always reg */
            DUK__DELPROP_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins));
        case DUK_OP_DELPROP_RC:
            DUK__DELPROP_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins));
#endif  /* DUK_USE_EXEC_PREFER_SIZE */

        /* No fast path for DECLVAR now, it's quite a rare instruction. */
        case DUK_OP_DECLVAR_RR:
        case DUK_OP_DECLVAR_CR:
        case DUK_OP_DECLVAR_RC:
        case DUK_OP_DECLVAR_CC: {
            duk_activation *act;
            duk_small_uint_fast_t a = DUK_DEC_A(ins);
            duk_tval *tv1;
            duk_hstring *name;
            duk_small_uint_t prop_flags;
            duk_bool_t is_func_decl;

            tv1 = DUK__REGCONSTP_B(ins);
            DUK_ASSERT(DUK_TVAL_IS_STRING(tv1));
            name = DUK_TVAL_GET_STRING(tv1);
            DUK_ASSERT(name != NULL);

            is_func_decl = ((a & DUK_BC_DECLVAR_FLAG_FUNC_DECL) != 0);

            /* XXX: declvar takes an duk_tval pointer, which is awkward and
             * should be reworked.
             */

            /* Compiler is responsible for selecting property flags (configurability,
             * writability, etc).
             */
            prop_flags = a & DUK_PROPDESC_FLAGS_MASK;

            if (is_func_decl) {
                duk_push_tval(thr, DUK__REGCONSTP_C(ins));
            } else {
                DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top));  /* valstack policy */
                thr->valstack_top++;
            }
            tv1 = DUK_GET_TVAL_NEGIDX(thr, -1);

            act = thr->callstack_curr;
            if (duk_js_declvar_activation(thr, act, name, tv1, prop_flags, is_func_decl)) {
                if (is_func_decl) {
                    /* Already declared, update value. */
                    tv1 = DUK_GET_TVAL_NEGIDX(thr, -1);
                    duk_js_putvar_activation(thr, act, name, tv1, DUK__STRICT());
                } else {
                    /* Already declared but no initializer value
                     * (e.g. 'var xyz;'), no-op.
                     */
                }
            }

            duk_pop_unsafe(thr);
            break;
        }

#if defined(DUK_USE_REGEXP_SUPPORT)
        /* The compiler should never emit DUK_OP_REGEXP if there is no
         * regexp support.
         */
        case DUK_OP_REGEXP_RR:
        case DUK_OP_REGEXP_CR:
        case DUK_OP_REGEXP_RC:
        case DUK_OP_REGEXP_CC: {
            /* A -> target register
             * B -> bytecode (also contains flags)
             * C -> escaped source
             */

            duk_push_tval(thr, DUK__REGCONSTP_C(ins));
            duk_push_tval(thr, DUK__REGCONSTP_B(ins));  /* -> [ ... escaped_source bytecode ] */
            duk_regexp_create_instance(thr);   /* -> [ ... regexp_instance ] */
            DUK__REPLACE_TOP_A_BREAK();
        }
#endif  /* DUK_USE_REGEXP_SUPPORT */

        /* XXX: 'c' is unused, use whole BC, etc. */
        case DUK_OP_CSVAR_RR:
        case DUK_OP_CSVAR_CR:
        case DUK_OP_CSVAR_RC:
        case DUK_OP_CSVAR_CC: {
            /* The speciality of calling through a variable binding is that the
             * 'this' value may be provided by the variable lookup: E5 Section 6.b.i.
             *
             * The only (standard) case where the 'this' binding is non-null is when
             *   (1) the variable is found in an object environment record, and
             *   (2) that object environment record is a 'with' block.
             */

            duk_activation *act;
            duk_uint_fast_t idx;
            duk_tval *tv1;
            duk_hstring *name;

            /* A -> target registers (A, A + 1) for call setup
             * B -> identifier name, usually constant but can be a register due to shuffling
             */

            tv1 = DUK__REGCONSTP_B(ins);
            DUK_ASSERT(DUK_TVAL_IS_STRING(tv1));
            name = DUK_TVAL_GET_STRING(tv1);
            DUK_ASSERT(name != NULL);
            act = thr->callstack_curr;
            (void) duk_js_getvar_activation(thr, act, name, 1 /*throw*/);  /* -> [... val this] */

            idx = (duk_uint_fast_t) DUK_DEC_A(ins);

            /* Could add direct value stack handling. */
            duk_replace(thr, (duk_idx_t) (idx + 1));  /* 'this' binding */
            duk_replace(thr, (duk_idx_t) idx);        /* variable value (function, we hope, not checked here) */
            break;
        }

        case DUK_OP_CLOSURE: {
            duk_activation *act;
            duk_hcompfunc *fun_act;
            duk_small_uint_fast_t bc = DUK_DEC_BC(ins);
            duk_hobject *fun_temp;

            /* A -> target reg
             * BC -> inner function index
             */

            DUK_DDD(DUK_DDDPRINT("CLOSURE to target register %ld, fnum %ld (count %ld)",
                                 (long) DUK_DEC_A(ins), (long) DUK_DEC_BC(ins), (long) DUK_HCOMPFUNC_GET_FUNCS_COUNT(thr->heap, DUK__FUN())));

            DUK_ASSERT_DISABLE(bc >= 0); /* unsigned */
            DUK_ASSERT((duk_uint_t) bc < (duk_uint_t) DUK_HCOMPFUNC_GET_FUNCS_COUNT(thr->heap, DUK__FUN()));

            act = thr->callstack_curr;
            fun_act = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act);
            fun_temp = DUK_HCOMPFUNC_GET_FUNCS_BASE(thr->heap, fun_act)[bc];
            DUK_ASSERT(fun_temp != NULL);
            DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(fun_temp));

            DUK_DDD(DUK_DDDPRINT("CLOSURE: function template is: %p -> %!O",
                                 (void *) fun_temp, (duk_heaphdr *) fun_temp));

            if (act->lex_env == NULL) {
                DUK_ASSERT(act->var_env == NULL);
                duk_js_init_activation_environment_records_delayed(thr, act);
                act = thr->callstack_curr;
            }
            DUK_ASSERT(act->lex_env != NULL);
            DUK_ASSERT(act->var_env != NULL);

            /* functions always have a NEWENV flag, i.e. they get a
             * new variable declaration environment, so only lex_env
             * matters here.
             */
            duk_js_push_closure(thr,
                                (duk_hcompfunc *) fun_temp,
                                act->var_env,
                                act->lex_env,
                                1 /*add_auto_proto*/);
            DUK__REPLACE_TOP_A_BREAK();
        }

        case DUK_OP_GETVAR: {
            duk_activation *act;
            duk_tval *tv1;
            duk_hstring *name;

            tv1 = DUK__CONSTP_BC(ins);
            DUK_ASSERT(DUK_TVAL_IS_STRING(tv1));
            name = DUK_TVAL_GET_STRING(tv1);
            DUK_ASSERT(name != NULL);
            act = thr->callstack_curr;
            DUK_ASSERT(act != NULL);
            (void) duk_js_getvar_activation(thr, act, name, 1 /*throw*/);  /* -> [... val this] */
            duk_pop_unsafe(thr);  /* 'this' binding is not needed here */
            DUK__REPLACE_TOP_A_BREAK();
        }

        case DUK_OP_PUTVAR: {
            duk_activation *act;
            duk_tval *tv1;
            duk_hstring *name;

            tv1 = DUK__CONSTP_BC(ins);
            DUK_ASSERT(DUK_TVAL_IS_STRING(tv1));
            name = DUK_TVAL_GET_STRING(tv1);
            DUK_ASSERT(name != NULL);

            /* XXX: putvar takes a duk_tval pointer, which is awkward and
             * should be reworked.
             */

            tv1 = DUK__REGP_A(ins);  /* val */
            act = thr->callstack_curr;
            duk_js_putvar_activation(thr, act, name, tv1, DUK__STRICT());
            break;
        }

        case DUK_OP_DELVAR: {
            duk_activation *act;
            duk_tval *tv1;
            duk_hstring *name;
            duk_bool_t rc;

            tv1 = DUK__CONSTP_BC(ins);
            DUK_ASSERT(DUK_TVAL_IS_STRING(tv1));
            name = DUK_TVAL_GET_STRING(tv1);
            DUK_ASSERT(name != NULL);
            act = thr->callstack_curr;
            rc = duk_js_delvar_activation(thr, act, name);
            DUK__REPLACE_BOOL_A_BREAK(rc);
        }

        case DUK_OP_JUMP: {
            /* Note: without explicit cast to signed, MSVC will
             * apparently generate a large positive jump when the
             * bias-corrected value would normally be negative.
             */
            curr_pc += (duk_int_fast_t) DUK_DEC_ABC(ins) - (duk_int_fast_t) DUK_BC_JUMP_BIAS;
            break;
        }

#define DUK__RETURN_SHARED() do { \
        duk_small_uint_t ret_result; \
        /* duk__handle_return() is guaranteed never to throw, except \
         * for potential out-of-memory situations which will then \
         * propagate out of the executor longjmp handler. \
         */ \
        DUK_ASSERT(thr->ptr_curr_pc == NULL); \
        ret_result = duk__handle_return(thr, entry_act); \
        if (ret_result == DUK__RETHAND_RESTART) { \
            goto restart_execution; \
        } \
        DUK_ASSERT(ret_result == DUK__RETHAND_FINISHED); \
        return; \
    } while (0)
#if defined(DUK_USE_EXEC_PREFER_SIZE)
        case DUK_OP_RETREG:
        case DUK_OP_RETCONST:
        case DUK_OP_RETCONSTN:
        case DUK_OP_RETUNDEF: {
             /* BC -> return value reg/const */

            DUK__SYNC_AND_NULL_CURR_PC();

            if (op == DUK_OP_RETREG) {
                duk_push_tval(thr, DUK__REGP_BC(ins));
            } else if (op == DUK_OP_RETUNDEF) {
                DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top));  /* valstack policy */
                thr->valstack_top++;
            } else {
                DUK_ASSERT(op == DUK_OP_RETCONST || op == DUK_OP_RETCONSTN);
                duk_push_tval(thr, DUK__CONSTP_BC(ins));
            }

            DUK__RETURN_SHARED();
        }
#else  /* DUK_USE_EXEC_PREFER_SIZE */
        case DUK_OP_RETREG: {
            duk_tval *tv;

            DUK__SYNC_AND_NULL_CURR_PC();
            tv = DUK__REGP_BC(ins);
            DUK_TVAL_SET_TVAL(thr->valstack_top, tv);
            DUK_TVAL_INCREF(thr, tv);
            thr->valstack_top++;
            DUK__RETURN_SHARED();
        }
        /* This will be unused without refcounting. */
        case DUK_OP_RETCONST: {
            duk_tval *tv;

            DUK__SYNC_AND_NULL_CURR_PC();
            tv = DUK__CONSTP_BC(ins);
            DUK_TVAL_SET_TVAL(thr->valstack_top, tv);
            DUK_TVAL_INCREF(thr, tv);
            thr->valstack_top++;
            DUK__RETURN_SHARED();
        }
        case DUK_OP_RETCONSTN: {
            duk_tval *tv;

            DUK__SYNC_AND_NULL_CURR_PC();
            tv = DUK__CONSTP_BC(ins);
            DUK_TVAL_SET_TVAL(thr->valstack_top, tv);
#if defined(DUK_USE_REFERENCE_COUNTING)
            /* Without refcounting only RETCONSTN is used. */
            DUK_ASSERT(!DUK_TVAL_IS_HEAP_ALLOCATED(tv));  /* no INCREF for this constant */
#endif
            thr->valstack_top++;
            DUK__RETURN_SHARED();
        }
        case DUK_OP_RETUNDEF: {
            DUK__SYNC_AND_NULL_CURR_PC();
            thr->valstack_top++;  /* value at valstack top is already undefined by valstack policy */
            DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top));
            DUK__RETURN_SHARED();
        }
#endif  /* DUK_USE_EXEC_PREFER_SIZE */

        case DUK_OP_LABEL: {
            duk_activation *act;
            duk_catcher *cat;
            duk_small_uint_fast_t bc = DUK_DEC_BC(ins);

            /* Allocate catcher and populate it (must be atomic). */

            cat = duk_hthread_catcher_alloc(thr);
            DUK_ASSERT(cat != NULL);

            cat->flags = (duk_uint32_t) (DUK_CAT_TYPE_LABEL | (bc << DUK_CAT_LABEL_SHIFT));
            cat->pc_base = (duk_instr_t *) curr_pc;  /* pre-incremented, points to first jump slot */
            cat->idx_base = 0;  /* unused for label */
            cat->h_varname = NULL;

            act = thr->callstack_curr;
            DUK_ASSERT(act != NULL);
            cat->parent = act->cat;
            act->cat = cat;

            DUK_DDD(DUK_DDDPRINT("LABEL catcher: flags=0x%08lx, pc_base=%ld, "
                                 "idx_base=%ld, h_varname=%!O, label_id=%ld",
                                 (long) cat->flags, (long) cat->pc_base,
                                 (long) cat->idx_base, (duk_heaphdr *) cat->h_varname, (long) DUK_CAT_GET_LABEL(cat)));

            curr_pc += 2;  /* skip jump slots */
            break;
        }

        case DUK_OP_ENDLABEL: {
            duk_activation *act;
#if (defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)) || defined(DUK_USE_ASSERTIONS)
            duk_small_uint_fast_t bc = DUK_DEC_BC(ins);
#endif
#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)
            DUK_DDD(DUK_DDDPRINT("ENDLABEL %ld", (long) bc));
#endif

            act = thr->callstack_curr;
            DUK_ASSERT(act->cat != NULL);
            DUK_ASSERT(DUK_CAT_GET_TYPE(act->cat) == DUK_CAT_TYPE_LABEL);
            DUK_ASSERT((duk_uint_fast_t) DUK_CAT_GET_LABEL(act->cat) == bc);
            duk_hthread_catcher_unwind_nolexenv_norz(thr, act);

            /* no need to unwind callstack */
            break;
        }

        case DUK_OP_BREAK: {
            duk_small_uint_fast_t bc = DUK_DEC_BC(ins);

            DUK__SYNC_AND_NULL_CURR_PC();
            duk__handle_break_or_continue(thr, (duk_uint_t) bc, DUK_LJ_TYPE_BREAK);
            goto restart_execution;
        }

        case DUK_OP_CONTINUE: {
            duk_small_uint_fast_t bc = DUK_DEC_BC(ins);

            DUK__SYNC_AND_NULL_CURR_PC();
            duk__handle_break_or_continue(thr, (duk_uint_t) bc, DUK_LJ_TYPE_CONTINUE);
            goto restart_execution;
        }

        /* XXX: move to helper, too large to be inline here */
        case DUK_OP_TRYCATCH: {
            duk__handle_op_trycatch(thr, ins, curr_pc);
            curr_pc += 2;  /* skip jump slots */
            break;
        }

        case DUK_OP_ENDTRY: {
            curr_pc = duk__handle_op_endtry(thr, ins);
            break;
        }

        case DUK_OP_ENDCATCH: {
            duk__handle_op_endcatch(thr, ins);
            break;
        }

        case DUK_OP_ENDFIN: {
            /* Sync and NULL early. */
            DUK__SYNC_AND_NULL_CURR_PC();

            if (duk__handle_op_endfin(thr, ins, entry_act) != 0) {
                return;
            }

            /* Must restart because we NULLed out curr_pc. */
            goto restart_execution;
        }

        case DUK_OP_THROW: {
            duk_small_uint_fast_t bc = DUK_DEC_BC(ins);

            /* Note: errors are augmented when they are created, not
             * when they are thrown.  So, don't augment here, it would
             * break re-throwing for instance.
             */

            /* Sync so that augmentation sees up-to-date activations, NULL
             * thr->ptr_curr_pc so that it's not used if side effects occur
             * in augmentation or longjmp handling.
             */
            DUK__SYNC_AND_NULL_CURR_PC();

            duk_dup(thr, (duk_idx_t) bc);
            DUK_DDD(DUK_DDDPRINT("THROW ERROR (BYTECODE): %!dT (before throw augment)",
                                 (duk_tval *) duk_get_tval(thr, -1)));
#if defined(DUK_USE_AUGMENT_ERROR_THROW)
            duk_err_augment_error_throw(thr);
            DUK_DDD(DUK_DDDPRINT("THROW ERROR (BYTECODE): %!dT (after throw augment)",
                                 (duk_tval *) duk_get_tval(thr, -1)));
#endif

            duk_err_setup_ljstate1(thr, DUK_LJ_TYPE_THROW, DUK_GET_TVAL_NEGIDX(thr, -1));
#if defined(DUK_USE_DEBUGGER_SUPPORT)
            duk_err_check_debugger_integration(thr);
#endif

            DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL);  /* always in executor */
            duk_err_longjmp(thr);
            DUK_UNREACHABLE();
            break;
        }

        case DUK_OP_CSREG: {
            /*
             *  Assuming a register binds to a variable declared within this
             *  function (a declarative binding), the 'this' for the call
             *  setup is always 'undefined'.  E5 Section 10.2.1.1.6.
             */

            duk_small_uint_fast_t a = DUK_DEC_A(ins);
            duk_small_uint_fast_t bc = DUK_DEC_BC(ins);

            /* A -> register containing target function (not type checked here)
             * BC -> target registers (BC, BC + 1) for call setup
             */

#if defined(DUK_USE_PREFER_SIZE)
            duk_dup(thr, (duk_idx_t) a);
            duk_replace(thr, (duk_idx_t) bc);
            duk_to_undefined(thr, (duk_idx_t) (bc + 1));
#else
            duk_tval *tv1;
            duk_tval *tv2;
            duk_tval *tv3;
            duk_tval tv_tmp1;
            duk_tval tv_tmp2;

            tv1 = DUK__REGP(bc);
            tv2 = tv1 + 1;
            DUK_TVAL_SET_TVAL(&tv_tmp1, tv1);
            DUK_TVAL_SET_TVAL(&tv_tmp2, tv2);
            tv3 = DUK__REGP(a);
            DUK_TVAL_SET_TVAL(tv1, tv3);
            DUK_TVAL_INCREF(thr, tv1);  /* no side effects */
            DUK_TVAL_SET_UNDEFINED(tv2);  /* no need for incref */
            DUK_TVAL_DECREF(thr, &tv_tmp1);
            DUK_TVAL_DECREF(thr, &tv_tmp2);
#endif
            break;
        }


        /* XXX: in some cases it's faster NOT to reuse the value
         * stack but rather copy the arguments on top of the stack
         * (mainly when the calling value stack is large and the value
         * stack resize would be large).
         */

        case DUK_OP_CALL0:
        case DUK_OP_CALL1:
        case DUK_OP_CALL2:
        case DUK_OP_CALL3:
        case DUK_OP_CALL4:
        case DUK_OP_CALL5:
        case DUK_OP_CALL6:
        case DUK_OP_CALL7: {
            /* Opcode packs 4 flag bits: 1 for indirect, 3 map
             * 1:1 to three lowest call handling flags.
             *
             * A -> nargs or register with nargs (indirect)
             * BC -> base register for call (base -> func, base+1 -> this, base+2 -> arg1 ... base+2+N-1 -> argN)
             */

            duk_idx_t nargs;
            duk_idx_t idx;
            duk_small_uint_t call_flags;
#if !defined(DUK_USE_EXEC_FUN_LOCAL)
            duk_hcompfunc *fun;
#endif

            DUK_ASSERT((DUK_OP_CALL0 & 0x0fU) == 0);
            DUK_ASSERT((ins & DUK_BC_CALL_FLAG_INDIRECT) == 0);

            nargs = (duk_idx_t) DUK_DEC_A(ins);
            call_flags = (ins & 0x07U) | DUK_CALL_FLAG_ALLOW_ECMATOECMA;
            idx = (duk_idx_t) DUK_DEC_BC(ins);

            if (duk__executor_handle_call(thr, idx, nargs, call_flags)) {
                /* curr_pc synced by duk_handle_call_unprotected() */
                DUK_ASSERT(thr->ptr_curr_pc == NULL);
                goto restart_execution;
            }
            DUK_ASSERT(thr->ptr_curr_pc != NULL);

            /* duk_js_call.c is required to restore the stack reserve
             * so we only need to reset the top.
             */
#if !defined(DUK_USE_EXEC_FUN_LOCAL)
            fun = DUK__FUN();
#endif
            duk_set_top_unsafe(thr, (duk_idx_t) fun->nregs);

            /* No need to reinit setjmp() catchpoint, as call handling
             * will store and restore our state.
             *
             * When debugger is enabled, we need to recheck the activation
             * status after returning.  This is now handled by call handling
             * and heap->dbg_force_restart.
             */
            break;
        }

        case DUK_OP_CALL8:
        case DUK_OP_CALL9:
        case DUK_OP_CALL10:
        case DUK_OP_CALL11:
        case DUK_OP_CALL12:
        case DUK_OP_CALL13:
        case DUK_OP_CALL14:
        case DUK_OP_CALL15: {
            /* Indirect variant. */
            duk_uint_fast_t nargs;
            duk_idx_t idx;
            duk_small_uint_t call_flags;
#if !defined(DUK_USE_EXEC_FUN_LOCAL)
            duk_hcompfunc *fun;
#endif

            DUK_ASSERT((DUK_OP_CALL0 & 0x0fU) == 0);
            DUK_ASSERT((ins & DUK_BC_CALL_FLAG_INDIRECT) != 0);

            nargs = (duk_uint_fast_t) DUK_DEC_A(ins);
            DUK__LOOKUP_INDIRECT(nargs);
            call_flags = (ins & 0x07U) | DUK_CALL_FLAG_ALLOW_ECMATOECMA;
            idx = (duk_idx_t) DUK_DEC_BC(ins);

            if (duk__executor_handle_call(thr, idx, (duk_idx_t) nargs, call_flags)) {
                DUK_ASSERT(thr->ptr_curr_pc == NULL);
                goto restart_execution;
            }
            DUK_ASSERT(thr->ptr_curr_pc != NULL);

#if !defined(DUK_USE_EXEC_FUN_LOCAL)
            fun = DUK__FUN();
#endif
            duk_set_top_unsafe(thr, (duk_idx_t) fun->nregs);
            break;
        }

        case DUK_OP_NEWOBJ: {
            duk_push_object(thr);
#if defined(DUK_USE_ASSERTIONS)
            {
                duk_hobject *h;
                h = duk_require_hobject(thr, -1);
                DUK_ASSERT(DUK_HOBJECT_GET_ESIZE(h) == 0);
                DUK_ASSERT(DUK_HOBJECT_GET_ENEXT(h) == 0);
                DUK_ASSERT(DUK_HOBJECT_GET_ASIZE(h) == 0);
                DUK_ASSERT(DUK_HOBJECT_GET_HSIZE(h) == 0);
            }
#endif
#if !defined(DUK_USE_PREFER_SIZE)
            /* XXX: could do a direct props realloc, but need hash size */
            duk_hobject_resize_entrypart(thr, duk_known_hobject(thr, -1), DUK_DEC_A(ins));
#endif
            DUK__REPLACE_TOP_BC_BREAK();
        }

        case DUK_OP_NEWARR: {
            duk_push_array(thr);
#if defined(DUK_USE_ASSERTIONS)
            {
                duk_hobject *h;
                h = duk_require_hobject(thr, -1);
                DUK_ASSERT(DUK_HOBJECT_GET_ESIZE(h) == 0);
                DUK_ASSERT(DUK_HOBJECT_GET_ENEXT(h) == 0);
                DUK_ASSERT(DUK_HOBJECT_GET_ASIZE(h) == 0);
                DUK_ASSERT(DUK_HOBJECT_GET_HSIZE(h) == 0);
                DUK_ASSERT(DUK_HOBJECT_HAS_ARRAY_PART(h));
            }
#endif
#if !defined(DUK_USE_PREFER_SIZE)
            duk_hobject_realloc_props(thr,
                                      duk_known_hobject(thr, -1),
                                      0 /*new_e_size*/,
                                      DUK_DEC_A(ins) /*new_a_size*/,
                                      0 /*new_h_size*/,
                                      0 /*abandon_array*/);
#if 0
            duk_hobject_resize_arraypart(thr, duk_known_hobject(thr, -1), DUK_DEC_A(ins));
#endif
#endif
            DUK__REPLACE_TOP_BC_BREAK();
        }

        case DUK_OP_MPUTOBJ:
        case DUK_OP_MPUTOBJI: {
            duk_idx_t obj_idx;
            duk_uint_fast_t idx, idx_end;
            duk_small_uint_fast_t count;

            /* A -> register of target object
             * B -> first register of key/value pair list
             *      or register containing first register number if indirect
             * C -> number of key/value pairs * 2
             *      (= number of value stack indices used starting from 'B')
             */

            obj_idx = DUK_DEC_A(ins);
            DUK_ASSERT(duk_is_object(thr, obj_idx));

            idx = (duk_uint_fast_t) DUK_DEC_B(ins);
            if (DUK_DEC_OP(ins) == DUK_OP_MPUTOBJI) {
                DUK__LOOKUP_INDIRECT(idx);
            }

            count = (duk_small_uint_fast_t) DUK_DEC_C(ins);
            DUK_ASSERT(count > 0);  /* compiler guarantees */
            idx_end = idx + count;

#if defined(DUK_USE_EXEC_INDIRECT_BOUND_CHECK)
            if (DUK_UNLIKELY(idx_end > (duk_uint_fast_t) duk_get_top(thr))) {
                /* XXX: use duk_is_valid_index() instead? */
                /* XXX: improve check; check against nregs, not against top */
                DUK__INTERNAL_ERROR("MPUTOBJ out of bounds");
            }
#endif

            /* Use 'force' flag to duk_def_prop() to ensure that any
             * inherited properties don't prevent the operation.
             * With ES2015 duplicate properties are allowed, so that we
             * must overwrite any previous data or accessor property.
             *
             * With ES2015 computed property names the literal keys
             * may be arbitrary values and need to be ToPropertyKey()
             * coerced at runtime.
             */
            do {
                /* XXX: faster initialization (direct access or better primitives) */
                duk_dup(thr, (duk_idx_t) idx);
                duk_dup(thr, (duk_idx_t) (idx + 1));
                duk_def_prop(thr, obj_idx, DUK_DEFPROP_HAVE_VALUE |
                                           DUK_DEFPROP_FORCE |
                                           DUK_DEFPROP_SET_WRITABLE |
                                           DUK_DEFPROP_SET_ENUMERABLE |
                                           DUK_DEFPROP_SET_CONFIGURABLE);
                idx += 2;
            } while (idx < idx_end);
            break;
        }

        case DUK_OP_INITSET:
        case DUK_OP_INITGET: {
            duk__handle_op_initset_initget(thr, ins);
            break;
        }

        case DUK_OP_MPUTARR:
        case DUK_OP_MPUTARRI: {
            duk_idx_t obj_idx;
            duk_uint_fast_t idx, idx_end;
            duk_small_uint_fast_t count;
            duk_tval *tv1;
            duk_uint32_t arr_idx;

            /* A -> register of target object
             * B -> first register of value data (start_index, value1, value2, ..., valueN)
             *      or register containing first register number if indirect
             * C -> number of key/value pairs (N)
             */

            obj_idx = DUK_DEC_A(ins);
            DUK_ASSERT(duk_is_object(thr, obj_idx));

            idx = (duk_uint_fast_t) DUK_DEC_B(ins);
            if (DUK_DEC_OP(ins) == DUK_OP_MPUTARRI) {
                DUK__LOOKUP_INDIRECT(idx);
            }

            count = (duk_small_uint_fast_t) DUK_DEC_C(ins);
            DUK_ASSERT(count > 0 + 1);  /* compiler guarantees */
            idx_end = idx + count;

#if defined(DUK_USE_EXEC_INDIRECT_BOUND_CHECK)
            if (idx_end > (duk_uint_fast_t) duk_get_top(thr)) {
                /* XXX: use duk_is_valid_index() instead? */
                /* XXX: improve check; check against nregs, not against top */
                DUK__INTERNAL_ERROR("MPUTARR out of bounds");
            }
#endif

            tv1 = DUK__REGP(idx);
            DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv1));
#if defined(DUK_USE_FASTINT)
            DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv1));
            arr_idx = (duk_uint32_t) DUK_TVAL_GET_FASTINT_U32(tv1);
#else
            arr_idx = (duk_uint32_t) DUK_TVAL_GET_NUMBER(tv1);
#endif
            idx++;

            do {
                /* duk_xdef_prop() will define an own property without any array
                 * special behaviors.  We'll need to set the array length explicitly
                 * in the end.  For arrays with elisions, the compiler will emit an
                 * explicit SETALEN which will update the length.
                 */

                /* XXX: because we're dealing with 'own' properties of a fresh array,
                 * the array initializer should just ensure that the array has a large
                 * enough array part and write the values directly into array part,
                 * and finally set 'length' manually in the end (as already happens now).
                 */

                duk_dup(thr, (duk_idx_t) idx);
                duk_xdef_prop_index_wec(thr, obj_idx, arr_idx);

                idx++;
                arr_idx++;
            } while (idx < idx_end);

            /* XXX: E5.1 Section 11.1.4 coerces the final length through
             * ToUint32() which is odd but happens now as a side effect of
             * 'arr_idx' type.
             */
            duk_set_length(thr, obj_idx, (duk_size_t) (duk_uarridx_t) arr_idx);
            break;
        }

        case DUK_OP_SETALEN: {
            duk_tval *tv1;
            duk_hobject *h;
            duk_uint32_t len;

            tv1 = DUK__REGP_A(ins);
            DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv1));
            h = DUK_TVAL_GET_OBJECT(tv1);
            DUK_ASSERT(DUK_HOBJECT_IS_ARRAY(h));

            tv1 = DUK__REGP_BC(ins);
            DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv1));
#if defined(DUK_USE_FASTINT)
            DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv1));
            len = (duk_uint32_t) DUK_TVAL_GET_FASTINT_U32(tv1);
#else
            len = (duk_uint32_t) DUK_TVAL_GET_NUMBER(tv1);
#endif
            ((duk_harray *) h)->length = len;
            break;
        }

        case DUK_OP_INITENUM: {
            duk__handle_op_initenum(thr, ins);
            break;
        }

        case DUK_OP_NEXTENUM: {
            curr_pc += duk__handle_op_nextenum(thr, ins);
            break;
        }

        case DUK_OP_INVLHS: {
            DUK_ERROR_REFERENCE(thr, DUK_STR_INVALID_LVALUE);
            DUK_WO_NORETURN(return;);
            break;
        }

        case DUK_OP_DEBUGGER: {
            /* Opcode only emitted by compiler when debugger
             * support is enabled.  Ignore it silently without
             * debugger support, in case it has been loaded
             * from precompiled bytecode.
             */
#if defined(DUK_USE_DEBUGGER_SUPPORT)
            if (duk_debug_is_attached(thr->heap)) {
                DUK_D(DUK_DPRINT("DEBUGGER statement encountered, halt execution"));
                DUK__SYNC_AND_NULL_CURR_PC();
                duk_debug_halt_execution(thr, 1 /*use_prev_pc*/);
                DUK_D(DUK_DPRINT("DEBUGGER statement finished, resume execution"));
                goto restart_execution;
            } else {
                DUK_D(DUK_DPRINT("DEBUGGER statement ignored, debugger not attached"));
            }
#else
            DUK_D(DUK_DPRINT("DEBUGGER statement ignored, no debugger support"));
#endif
            break;
        }

        case DUK_OP_NOP: {
            /* Nop, ignored, but ABC fields may carry a value e.g.
             * for indirect opcode handling.
             */
            break;
        }

        case DUK_OP_INVALID: {
            DUK_ERROR_FMT1(thr, DUK_ERR_ERROR, "INVALID opcode (%ld)", (long) DUK_DEC_ABC(ins));
            DUK_WO_NORETURN(return;);
            break;
        }

#if defined(DUK_USE_ES6)
        case DUK_OP_NEWTARGET: {
            duk_push_new_target(thr);
            DUK__REPLACE_TOP_BC_BREAK();
        }
#endif  /* DUK_USE_ES6 */

#if !defined(DUK_USE_EXEC_PREFER_SIZE)
#if !defined(DUK_USE_ES7_EXP_OPERATOR)
        case DUK_OP_EXP_RR:
        case DUK_OP_EXP_CR:
        case DUK_OP_EXP_RC:
        case DUK_OP_EXP_CC:
#endif
#if !defined(DUK_USE_ES6)
        case DUK_OP_NEWTARGET:
#endif
#if !defined(DUK_USE_VERBOSE_ERRORS)
        case DUK_OP_GETPROPC_RR:
        case DUK_OP_GETPROPC_CR:
        case DUK_OP_GETPROPC_RC:
        case DUK_OP_GETPROPC_CC:
#endif
        case DUK_OP_UNUSED207:
        case DUK_OP_UNUSED212:
        case DUK_OP_UNUSED213:
        case DUK_OP_UNUSED214:
        case DUK_OP_UNUSED215:
        case DUK_OP_UNUSED216:
        case DUK_OP_UNUSED217:
        case DUK_OP_UNUSED218:
        case DUK_OP_UNUSED219:
        case DUK_OP_UNUSED220:
        case DUK_OP_UNUSED221:
        case DUK_OP_UNUSED222:
        case DUK_OP_UNUSED223:
        case DUK_OP_UNUSED224:
        case DUK_OP_UNUSED225:
        case DUK_OP_UNUSED226:
        case DUK_OP_UNUSED227:
        case DUK_OP_UNUSED228:
        case DUK_OP_UNUSED229:
        case DUK_OP_UNUSED230:
        case DUK_OP_UNUSED231:
        case DUK_OP_UNUSED232:
        case DUK_OP_UNUSED233:
        case DUK_OP_UNUSED234:
        case DUK_OP_UNUSED235:
        case DUK_OP_UNUSED236:
        case DUK_OP_UNUSED237:
        case DUK_OP_UNUSED238:
        case DUK_OP_UNUSED239:
        case DUK_OP_UNUSED240:
        case DUK_OP_UNUSED241:
        case DUK_OP_UNUSED242:
        case DUK_OP_UNUSED243:
        case DUK_OP_UNUSED244:
        case DUK_OP_UNUSED245:
        case DUK_OP_UNUSED246:
        case DUK_OP_UNUSED247:
        case DUK_OP_UNUSED248:
        case DUK_OP_UNUSED249:
        case DUK_OP_UNUSED250:
        case DUK_OP_UNUSED251:
        case DUK_OP_UNUSED252:
        case DUK_OP_UNUSED253:
        case DUK_OP_UNUSED254:
        case DUK_OP_UNUSED255:
        /* Force all case clauses to map to an actual handler
         * so that the compiler can emit a jump without a bounds
         * check: the switch argument is a duk_uint8_t so that
         * the compiler may be able to figure it out.  This is
         * a small detail and obviously compiler dependent.
         */
        /* default: clause omitted on purpose */
#else  /* DUK_USE_EXEC_PREFER_SIZE */
        default:
#endif  /* DUK_USE_EXEC_PREFER_SIZE */
        {
            /* Default case catches invalid/unsupported opcodes. */
            DUK_D(DUK_DPRINT("invalid opcode: %ld - %!I", (long) op, ins));
            DUK__INTERNAL_ERROR("invalid opcode");
            break;
        }

        }  /* end switch */

        continue;

        /* Some shared exit paths for opcode handling below.  These
         * are mostly useful to reduce code footprint when multiple
         * opcodes have a similar epilogue (like replacing stack top
         * with index 'a').
         */

#if defined(DUK_USE_EXEC_PREFER_SIZE)
     replace_top_a:
        DUK__REPLACE_TO_TVPTR(thr, DUK__REGP_A(ins));
        continue;
     replace_top_bc:
        DUK__REPLACE_TO_TVPTR(thr, DUK__REGP_BC(ins));
        continue;
#endif
    }
    DUK_WO_NORETURN(return;);

#if !defined(DUK_USE_VERBOSE_EXECUTOR_ERRORS)
 internal_error:
    DUK_ERROR_INTERNAL(thr);
    DUK_WO_NORETURN(return;);
#endif
}

/* automatic undefs */
#undef DUK__BYTEOFF_A
#undef DUK__BYTEOFF_B
#undef DUK__BYTEOFF_BC
#undef DUK__BYTEOFF_C
#undef DUK__COMPARE_BODY
#undef DUK__CONST
#undef DUK__CONSTP
#undef DUK__CONSTP_A
#undef DUK__CONSTP_B
#undef DUK__CONSTP_BC
#undef DUK__CONSTP_C
#undef DUK__DELPROP_BODY
#undef DUK__EQ_BODY
#undef DUK__FUN
#undef DUK__GETPROPC_BODY
#undef DUK__GETPROP_BODY
#undef DUK__GE_BODY
#undef DUK__GT_BODY
#undef DUK__INSTOF_BODY
#undef DUK__INTERNAL_ERROR
#undef DUK__INT_NOACTION
#undef DUK__INT_RESTART
#undef DUK__IN_BODY
#undef DUK__LE_BODY
#undef DUK__LONGJMP_RESTART
#undef DUK__LONGJMP_RETHROW
#undef DUK__LOOKUP_INDIRECT
#undef DUK__LT_BODY
#undef DUK__MASK_A
#undef DUK__MASK_B
#undef DUK__MASK_BC
#undef DUK__MASK_C
#undef DUK__NEQ_BODY
#undef DUK__PUTPROP_BODY
#undef DUK__RCBIT_B
#undef DUK__RCBIT_C
#undef DUK__REG
#undef DUK__REGCONSTP_B
#undef DUK__REGCONSTP_C
#undef DUK__REGP
#undef DUK__REGP_A
#undef DUK__REGP_B
#undef DUK__REGP_BC
#undef DUK__REGP_C
#undef DUK__REPLACE_BOOL_A_BREAK
#undef DUK__REPLACE_TOP_A_BREAK
#undef DUK__REPLACE_TOP_BC_BREAK
#undef DUK__REPLACE_TO_TVPTR
#undef DUK__RETHAND_FINISHED
#undef DUK__RETHAND_RESTART
#undef DUK__RETURN_SHARED
#undef DUK__SEQ_BODY
#undef DUK__SHIFT_A
#undef DUK__SHIFT_B
#undef DUK__SHIFT_BC
#undef DUK__SHIFT_C
#undef DUK__SNEQ_BODY
#undef DUK__STRICT
#undef DUK__SYNC_AND_NULL_CURR_PC
#undef DUK__SYNC_CURR_PC
#undef DUK__TVAL_SHIFT
//#line 1 "duk_js_ops.c"
/*
 *  ECMAScript specification algorithm and conversion helpers.
 *
 *  These helpers encapsulate the primitive ECMAScript operation semantics,
 *  and are used by the bytecode executor and the API (among other places).
 *  Some primitives are only implemented as part of the API and have no
 *  "internal" helper.  This is the case when an internal helper would not
 *  really be useful; e.g. the operation is rare, uses value stack heavily,
 *  etc.
 *
 *  The operation arguments depend on what is required to implement
 *  the operation:
 *
 *    - If an operation is simple and stateless, and has no side
 *      effects, it won't take an duk_hthread argument and its
 *      arguments may be duk_tval pointers (which are safe as long
 *      as no side effects take place).
 *
 *    - If complex coercions are required (e.g. a "ToNumber" coercion)
 *      or errors may be thrown, the operation takes an duk_hthread
 *      argument.  This also implies that the operation may have
 *      arbitrary side effects, invalidating any duk_tval pointers.
 *
 *    - For operations with potential side effects, arguments can be
 *      taken in several ways:
 *
 *      a) as duk_tval pointers, which makes sense if the "common case"
 *         can be resolved without side effects (e.g. coercion); the
 *         arguments are pushed to the valstack for coercion if
 *         necessary
 *
 *      b) as duk_tval values
 *
 *      c) implicitly on value stack top
 *
 *      d) as indices to the value stack
 *
 *  Future work:
 *
 *     - Argument styles may not be the most sensible in every case now.
 *
 *     - In-place coercions might be useful for several operations, if
 *       in-place coercion is OK for the bytecode executor and the API.
 */

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

/*
 *  ToPrimitive()  (E5 Section 9.1)
 *
 *  ==> implemented in the API.
 */

/*
 *  ToBoolean()  (E5 Section 9.2)
 */

DUK_INTERNAL duk_bool_t duk_js_toboolean(duk_tval *tv) {
    switch (DUK_TVAL_GET_TAG(tv)) {
    case DUK_TAG_UNDEFINED:
    case DUK_TAG_NULL:
        return 0;
    case DUK_TAG_BOOLEAN:
        DUK_ASSERT(DUK_TVAL_GET_BOOLEAN(tv) == 0 || DUK_TVAL_GET_BOOLEAN(tv) == 1);
        return DUK_TVAL_GET_BOOLEAN(tv);
    case DUK_TAG_STRING: {
        /* Symbols ToBoolean() coerce to true, regardless of their
         * description.  This happens with no explicit check because
         * of the symbol representation byte prefix.
         */
        duk_hstring *h = DUK_TVAL_GET_STRING(tv);
        DUK_ASSERT(h != NULL);
        return (DUK_HSTRING_GET_BYTELEN(h) > 0 ? 1 : 0);
    }
    case DUK_TAG_OBJECT: {
        return 1;
    }
    case DUK_TAG_BUFFER: {
        /* Mimic Uint8Array semantics: objects coerce true, regardless
         * of buffer length (zero or not) or context.
         */
        return 1;
    }
    case DUK_TAG_POINTER: {
        void *p = DUK_TVAL_GET_POINTER(tv);
        return (p != NULL ? 1 : 0);
    }
    case DUK_TAG_LIGHTFUNC: {
        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();
    DUK_WO_UNREACHABLE(return 0;);
}

/*
 *  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).  However,
 *      while '' coerces to 0, '+' and '-' coerce to 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();
    DUK_WO_UNREACHABLE(return 0.0;);
}

/*
 *  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[ */
        }
    }

    return x;
}

DUK_INTERNAL duk_int32_t duk_js_toint32(duk_hthread *thr, duk_tval *tv) {
    duk_double_t d;

#if defined(DUK_USE_FASTINT)
    if (DUK_TVAL_IS_FASTINT(tv)) {
        return DUK_TVAL_GET_FASTINT_I32(tv);
    }
#endif

    d = duk_js_tonumber(thr, tv);  /* invalidates tv */
    d = duk__toint32_touint32_helper(d, 1);
    DUK_ASSERT(DUK_FPCLASSIFY(d) == DUK_FP_ZERO || DUK_FPCLASSIFY(d) == DUK_FP_NORMAL);
    DUK_ASSERT(d >= -2147483648.0 && d <= 2147483647.0);  /* [-0x80000000,0x7fffffff] */
    DUK_ASSERT(duk_double_equals(d, (duk_double_t) ((duk_int32_t) d)));  /* whole, won't clip */
    return (duk_int32_t) d;
}


DUK_INTERNAL duk_uint32_t duk_js_touint32(duk_hthread *thr, duk_tval *tv) {
    duk_double_t d;

#if defined(DUK_USE_FASTINT)
    if (DUK_TVAL_IS_FASTINT(tv)) {
        return DUK_TVAL_GET_FASTINT_U32(tv);
    }
#endif

    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(duk_double_equals(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 (duk_double_equals(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 (duk_double_equals(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);
        }
        case DUK_TAG_STRING:
        case DUK_TAG_OBJECT: {
            /* Heap pointer comparison suffices for strings and objects.
             * Symbols compare equal if they have the same internal
             * representation; again heap pointer comparison suffices.
             */
            return DUK_TVAL_GET_HEAPHDR(tv_x) == DUK_TVAL_GET_HEAPHDR(tv_y);
        }
        case DUK_TAG_BUFFER: {
            /* In Duktape 2.x plain buffers mimic Uint8Array objects
             * so always compare by heap pointer.  In Duktape 1.x
             * strict comparison would compare heap pointers and
             * non-strict would compare contents.
             */
            return DUK_TVAL_GET_HEAPHDR(tv_x) == DUK_TVAL_GET_HEAPHDR(tv_y);
        }
        case DUK_TAG_LIGHTFUNC: {
            /* At least 'magic' has a significant impact on function
             * identity.
             */
            duk_small_uint_t lf_flags_x;
            duk_small_uint_t lf_flags_y;
            duk_c_function func_x;
            duk_c_function func_y;

            DUK_TVAL_GET_LIGHTFUNC(tv_x, func_x, lf_flags_x);
            DUK_TVAL_GET_LIGHTFUNC(tv_y, func_y, lf_flags_y);
            return ((func_x == func_y) && (lf_flags_x == lf_flags_y)) ? 1 : 0;
        }
#if defined(DUK_USE_FASTINT)
        case DUK_TAG_FASTINT:
#endif
        default: {
            DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv_x));
            DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv_y));
            DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_x));
            DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_y));
            DUK_UNREACHABLE();
            DUK_WO_UNREACHABLE(return 0;);
        }
        }
    }

    if ((flags & (DUK_EQUALS_FLAG_STRICT | DUK_EQUALS_FLAG_SAMEVALUE)) != 0) {
        return 0;
    }

    DUK_ASSERT(flags == 0);  /* non-strict equality from here on */

    /*
     *  Types are different; various cases for non-strict comparison
     *
     *  Since comparison is symmetric, we use a "swap trick" to reduce
     *  code size.
     */

    type_mask_x = duk_get_type_mask_tval(tv_x);
    type_mask_y = duk_get_type_mask_tval(tv_y);

    /* Undefined/null are considered equal (e.g. "null == undefined" -> true). */
    if ((type_mask_x & (DUK_TYPE_MASK_UNDEFINED | DUK_TYPE_MASK_NULL)) &&
        (type_mask_y & (DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_UNDEFINED))) {
        return 1;
    }

    /* Number/string -> coerce string to number (e.g. "'1.5' == 1.5" -> true). */
    if ((type_mask_x & DUK_TYPE_MASK_NUMBER) && (type_mask_y & DUK_TYPE_MASK_STRING)) {
        if (!DUK_TVAL_STRING_IS_SYMBOL(tv_y)) {
            duk_double_t d1, d2;
            d1 = DUK_TVAL_GET_NUMBER(tv_x);
            d2 = duk_to_number_tval(thr, tv_y);
            return duk__js_equals_number(d1, d2);
        }
    }
    if ((type_mask_x & DUK_TYPE_MASK_STRING) && (type_mask_y & DUK_TYPE_MASK_NUMBER)) {
        if (!DUK_TVAL_STRING_IS_SYMBOL(tv_x)) {
            duk_double_t d1, d2;
            d1 = DUK_TVAL_GET_NUMBER(tv_y);
            d2 = duk_to_number_tval(thr, tv_x);
            return duk__js_equals_number(d1, d2);
        }
    }

    /* Boolean/any -> coerce boolean to number and try again.  If boolean is
     * compared to a pointer, the final comparison after coercion now always
     * yields false (as pointer vs. number compares to false), but this is
     * not special cased.
     *
     * ToNumber(bool) is +1.0 or 0.0.  Tagged boolean value is always 0 or 1.
     */
    if (type_mask_x & DUK_TYPE_MASK_BOOLEAN) {
        DUK_ASSERT(DUK_TVAL_GET_BOOLEAN(tv_x) == 0 || DUK_TVAL_GET_BOOLEAN(tv_x) == 1);
        duk_push_uint(thr, DUK_TVAL_GET_BOOLEAN(tv_x));
        duk_push_tval(thr, tv_y);
        goto recursive_call;
    }
    if (type_mask_y & DUK_TYPE_MASK_BOOLEAN) {
        DUK_ASSERT(DUK_TVAL_GET_BOOLEAN(tv_y) == 0 || DUK_TVAL_GET_BOOLEAN(tv_y) == 1);
        duk_push_tval(thr, tv_x);
        duk_push_uint(thr, DUK_TVAL_GET_BOOLEAN(tv_y));
        goto recursive_call;
    }

    /* String-number-symbol/object -> coerce object to primitive (apparently without hint), then try again. */
    if ((type_mask_x & (DUK_TYPE_MASK_STRING | DUK_TYPE_MASK_NUMBER)) &&
        (type_mask_y & DUK_TYPE_MASK_OBJECT)) {
        /* No symbol check needed because symbols and strings are accepted. */
        duk_push_tval(thr, tv_x);
        duk_push_tval(thr, tv_y);
        duk_to_primitive(thr, -1, DUK_HINT_NONE);  /* apparently no hint? */
        goto recursive_call;
    }
    if ((type_mask_x & DUK_TYPE_MASK_OBJECT) &&
        (type_mask_y & (DUK_TYPE_MASK_STRING | DUK_TYPE_MASK_NUMBER))) {
        /* No symbol check needed because symbols and strings are accepted. */
        duk_push_tval(thr, tv_x);
        duk_push_tval(thr, tv_y);
        duk_to_primitive(thr, -2, DUK_HINT_NONE);  /* apparently no hint? */
        goto recursive_call;
    }

    /* Nothing worked -> not equal. */
    return 0;

 recursive_call:
    /* Shared code path to call the helper again with arguments on stack top. */
    {
        duk_bool_t rc;
        rc = duk_js_equals_helper(thr,
                                  DUK_GET_TVAL_NEGIDX(thr, -2),
                                  DUK_GET_TVAL_NEGIDX(thr, -1),
                                  0 /*flags:nonstrict*/);
        duk_pop_2_unsafe(thr);
        return rc;
    }
}

/*
 *  Comparisons (x >= y, x > y, x <= y, x < y)
 *
 *  E5 Section 11.8.5: implement 'x < y' and then use negate and eval_left_first
 *  flags to get the rest.
 */

/* XXX: this should probably just operate on the stack top, because it
 * needs to push stuff on the stack anyway...
 */

DUK_INTERNAL duk_small_int_t duk_js_data_compare(const duk_uint8_t *buf1, const duk_uint8_t *buf2, duk_size_t len1, duk_size_t len2) {
    duk_size_t prefix_len;
    duk_small_int_t rc;

    prefix_len = (len1 <= len2 ? len1 : len2);

    /* duk_memcmp() is guaranteed to return zero (equal) for zero length
     * inputs.
     */
    rc = duk_memcmp_unsafe((const void *) buf1,
                           (const void *) buf2,
                           (size_t) prefix_len);

    if (rc < 0) {
        return -1;
    } else if (rc > 0) {
        return 1;
    }

    /* prefix matches, lengths matter now */
    if (len1 < len2) {
        /* e.g. "x" < "xx" */
        return -1;
    } else if (len1 > len2) {
        return 1;
    }

    return 0;
}

DUK_INTERNAL duk_small_int_t duk_js_string_compare(duk_hstring *h1, duk_hstring *h2) {
    /*
     *  String comparison (E5 Section 11.8.5, step 4), which
     *  needs to compare codepoint by codepoint.
     *
     *  However, UTF-8 allows us to use strcmp directly: the shared
     *  prefix will be encoded identically (UTF-8 has unique encoding)
     *  and the first differing character can be compared with a simple
     *  unsigned byte comparison (which strcmp does).
     *
     *  This will not work properly for non-xutf-8 strings, but this
     *  is not an issue for compliance.
     */

    DUK_ASSERT(h1 != NULL);
    DUK_ASSERT(h2 != NULL);

    return duk_js_data_compare((const duk_uint8_t *) DUK_HSTRING_GET_DATA(h1),
                               (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h2),
                               (duk_size_t) DUK_HSTRING_GET_BYTELEN(h1),
                               (duk_size_t) DUK_HSTRING_GET_BYTELEN(h2));
}

#if 0  /* unused */
DUK_INTERNAL duk_small_int_t duk_js_buffer_compare(duk_heap *heap, duk_hbuffer *h1, duk_hbuffer *h2) {
    /* Similar to String comparison. */

    DUK_ASSERT(h1 != NULL);
    DUK_ASSERT(h2 != NULL);
    DUK_UNREF(heap);

    return duk_js_data_compare((const duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(heap, h1),
                               (const duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(heap, h2),
                               (duk_size_t) DUK_HBUFFER_GET_SIZE(h1),
                               (duk_size_t) DUK_HBUFFER_GET_SIZE(h2));
}
#endif

#if defined(DUK_USE_FASTINT)
DUK_LOCAL duk_bool_t duk__compare_fastint(duk_bool_t retval, duk_int64_t v1, duk_int64_t v2) {
    DUK_ASSERT(retval == 0 || retval == 1);
    if (v1 < v2) {
        return retval ^ 1;
    } else {
        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);

    if (DUK_TVAL_IS_STRING(tv_x) && DUK_TVAL_IS_STRING(tv_y)) {
        duk_hstring *h1 = DUK_TVAL_GET_STRING(tv_x);
        duk_hstring *h2 = DUK_TVAL_GET_STRING(tv_y);
        DUK_ASSERT(h1 != NULL);
        DUK_ASSERT(h2 != NULL);

        if (DUK_LIKELY(!DUK_HSTRING_HAS_SYMBOL(h1) && !DUK_HSTRING_HAS_SYMBOL(h2))) {
            rc = duk_js_string_compare(h1, h2);
            duk_pop_2_unsafe(thr);
            if (rc < 0) {
                return retval ^ 1;
            } else {
                return retval;
            }
        }

        /* One or both are Symbols: fall through to handle in the
         * generic path.  Concretely, ToNumber() will fail.
         */
    }

    /* Ordering should not matter (E5 Section 11.8.5, step 3.a). */
#if 0
    if (flags & DUK_COMPARE_FLAG_EVAL_LEFT_FIRST) {
        d1 = duk_to_number_m2(thr);
        d2 = duk_to_number_m1(thr);
    } else {
        d2 = duk_to_number_m1(thr);
        d1 = duk_to_number_m2(thr);
    }
#endif
    d1 = duk_to_number_m2(thr);
    d2 = duk_to_number_m1(thr);

    /* We want to duk_pop_2_unsafe(thr); because the values are numbers
     * no decref check is needed.
     */
#if defined(DUK_USE_PREFER_SIZE)
    duk_pop_2_nodecref_unsafe(thr);
#else
    DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(duk_get_tval(thr, -2)));
    DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(duk_get_tval(thr, -1)));
    DUK_ASSERT(duk_get_top(thr) >= 2);
    thr->valstack_top -= 2;
    tv_x = thr->valstack_top;
    tv_y = tv_x + 1;
    DUK_TVAL_SET_UNDEFINED(tv_x);  /* Value stack policy */
    DUK_TVAL_SET_UNDEFINED(tv_y);
#endif

    return duk__compare_number(retval, d1, d2);
}

/*
 *  instanceof
 */

/*
 *  ES2015 Section 7.3.19 describes the OrdinaryHasInstance() algorithm
 *  which covers both bound and non-bound functions; in effect the algorithm
 *  includes E5 Sections 11.8.6, 15.3.5.3, and 15.3.4.5.3.
 *
 *  ES2015 Section 12.9.4 describes the instanceof operator which first
 *  checks @@hasInstance well-known symbol and falls back to
 *  OrdinaryHasInstance().
 *
 *  Limited Proxy support: don't support 'getPrototypeOf' trap but
 *  continue lookup in Proxy target if the value is a Proxy.
 */

DUK_LOCAL duk_bool_t duk__js_instanceof_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_bool_t skip_sym_check) {
    duk_hobject *func;
    duk_hobject *val;
    duk_hobject *proto;
    duk_tval *tv;
    duk_bool_t skip_first;
    duk_uint_t sanity;

    /*
     *  Get the values onto the stack first.  It would be possible to cover
     *  some normal cases without resorting to the value stack.
     *
     *  The right hand side could be a light function (as they generally
     *  behave like objects).  Light functions never have a 'prototype'
     *  property so E5.1 Section 15.3.5.3 step 3 always throws a TypeError.
     *  Using duk_require_hobject() is thus correct (except for error msg).
     */

    duk_push_tval(thr, tv_x);
    duk_push_tval(thr, tv_y);
    func = duk_require_hobject(thr, -1);
    DUK_ASSERT(func != NULL);

#if defined(DUK_USE_SYMBOL_BUILTIN)
    /*
     *  @@hasInstance check, ES2015 Section 12.9.4, Steps 2-4.
     */
    if (!skip_sym_check) {
        if (duk_get_method_stridx(thr, -1, DUK_STRIDX_WELLKNOWN_SYMBOL_HAS_INSTANCE)) {
            /* [ ... lhs rhs func ] */
            duk_insert(thr, -3);    /* -> [ ... func lhs rhs ] */
            duk_swap_top(thr, -2);  /* -> [ ... func rhs(this) lhs ] */
            duk_call_method(thr, 1);
            return duk_to_boolean_top_pop(thr);
        }
    }
#else
    DUK_UNREF(skip_sym_check);
#endif

    /*
     *  For bound objects, [[HasInstance]] just calls the target function
     *  [[HasInstance]].  If that is again a bound object, repeat until
     *  we find a non-bound Function object.
     *
     *  The bound function chain is now "collapsed" so there can be only
     *  one bound function in the chain.
     */

    if (!DUK_HOBJECT_IS_CALLABLE(func)) {
        /*
         *  Note: of native ECMAScript objects, only Function instances
         *  have a [[HasInstance]] internal property.  Custom objects might
         *  also have it, but not in current implementation.
         *
         *  XXX: add a separate flag, DUK_HOBJECT_FLAG_ALLOW_INSTANCEOF?
         */
        goto error_invalid_rval;
    }

    if (DUK_HOBJECT_HAS_BOUNDFUNC(func)) {
        duk_push_tval(thr, &((duk_hboundfunc *) (void *) func)->target);
        duk_replace(thr, -2);
        func = duk_require_hobject(thr, -1);  /* lightfunc throws */

        /* Rely on Function.prototype.bind() never creating bound
         * functions whose target is not proper.
         */
        DUK_ASSERT(func != NULL);
        DUK_ASSERT(DUK_HOBJECT_IS_CALLABLE(func));
    }

    /*
     *  'func' is now a non-bound object which supports [[HasInstance]]
     *  (which here just means DUK_HOBJECT_FLAG_CALLABLE).  Move on
     *  to execute E5 Section 15.3.5.3.
     */

    DUK_ASSERT(func != NULL);
    DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func));
    DUK_ASSERT(DUK_HOBJECT_IS_CALLABLE(func));

    /* [ ... lval rval(func) ] */

    /* For lightfuncs, buffers, and pointers start the comparison directly
     * from the virtual prototype object.
     */
    skip_first = 0;
    tv = DUK_GET_TVAL_NEGIDX(thr, -2);
    switch (DUK_TVAL_GET_TAG(tv)) {
    case DUK_TAG_LIGHTFUNC:
        val = thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE];
        DUK_ASSERT(val != NULL);
        break;
    case DUK_TAG_BUFFER:
        val = thr->builtins[DUK_BIDX_UINT8ARRAY_PROTOTYPE];
        DUK_ASSERT(val != NULL);
        break;
    case DUK_TAG_POINTER:
        val = thr->builtins[DUK_BIDX_POINTER_PROTOTYPE];
        DUK_ASSERT(val != NULL);
        break;
    case DUK_TAG_OBJECT:
        skip_first = 1;  /* Ignore object itself on first round. */
        val = DUK_TVAL_GET_OBJECT(tv);
        DUK_ASSERT(val != NULL);
        break;
    default:
        goto pop2_and_false;
    }
    DUK_ASSERT(val != NULL);  /* Loop doesn't actually rely on this. */

    /* Look up .prototype of rval.  Leave it on the value stack in case it
     * has been virtualized (e.g. getter, Proxy trap).
     */
    duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_PROTOTYPE);  /* -> [ ... lval rval rval.prototype ] */
#if defined(DUK_USE_VERBOSE_ERRORS)
    proto = duk_get_hobject(thr, -1);
    if (proto == NULL) {
        goto error_invalid_rval_noproto;
    }
#else
    proto = duk_require_hobject(thr, -1);
#endif

    sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
    do {
        /*
         *  Note: prototype chain is followed BEFORE first comparison.  This
         *  means that the instanceof lval is never itself compared to the
         *  rval.prototype property.  This is apparently intentional, see E5
         *  Section 15.3.5.3, step 4.a.
         *
         *  Also note:
         *
         *      js> (function() {}) instanceof Function
         *      true
         *      js> Function instanceof Function
         *      true
         *
         *  For the latter, h_proto will be Function.prototype, which is the
         *  built-in Function prototype.  Because Function.[[Prototype]] is
         *  also the built-in Function prototype, the result is true.
         */

        if (!val) {
            goto pop3_and_false;
        }

        DUK_ASSERT(val != NULL);
#if defined(DUK_USE_ES6_PROXY)
        val = duk_hobject_resolve_proxy_target(val);
#endif

        if (skip_first) {
            skip_first = 0;
        } else if (val == proto) {
            goto pop3_and_true;
        }

        DUK_ASSERT(val != NULL);
        val = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, val);
    } while (--sanity > 0);

    DUK_ASSERT(sanity == 0);
    DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT);
    DUK_WO_NORETURN(return 0;);

 pop2_and_false:
    duk_pop_2_unsafe(thr);
    return 0;

 pop3_and_false:
    duk_pop_3_unsafe(thr);
    return 0;

 pop3_and_true:
    duk_pop_3_unsafe(thr);
    return 1;

 error_invalid_rval:
    DUK_ERROR_TYPE(thr, DUK_STR_INVALID_INSTANCEOF_RVAL);
    DUK_WO_NORETURN(return 0;);

#if defined(DUK_USE_VERBOSE_ERRORS)
 error_invalid_rval_noproto:
    DUK_ERROR_TYPE(thr, DUK_STR_INVALID_INSTANCEOF_RVAL_NOPROTO);
    DUK_WO_NORETURN(return 0;);
#endif
}

#if defined(DUK_USE_SYMBOL_BUILTIN)
DUK_INTERNAL duk_bool_t duk_js_instanceof_ordinary(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y) {
    return duk__js_instanceof_helper(thr, tv_x, tv_y, 1 /*skip_sym_check*/);
}
#endif

DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y) {
    return duk__js_instanceof_helper(thr, tv_x, tv_y, 0 /*skip_sym_check*/);
}

/*
 *  in
 */

/*
 *  E5 Sections 11.8.7, 8.12.6.
 *
 *  Basically just a property existence check using [[HasProperty]].
 */

DUK_INTERNAL duk_bool_t duk_js_in(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y) {
    duk_bool_t retval;

    /*
     *  Get the values onto the stack first.  It would be possible to cover
     *  some normal cases without resorting to the value stack (e.g. if
     *  lval is already a string).
     */

    /* XXX: The ES5/5.1/6 specifications require that the key in 'key in obj'
     * must be string coerced before the internal HasProperty() algorithm is
     * invoked.  A fast path skipping coercion could be safely implemented for
     * numbers (as number-to-string coercion has no side effects).  For ES2015
     * proxy behavior, the trap 'key' argument must be in a string coerced
     * form (which is a shame).
     */

    /* TypeError if rval is not an object or object like (e.g. lightfunc
     * or plain buffer).
     */
    duk_push_tval(thr, tv_x);
    duk_push_tval(thr, tv_y);
    duk_require_type_mask(thr, -1, DUK_TYPE_MASK_OBJECT | DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);

    (void) duk_to_property_key_hstring(thr, -2);

    retval = duk_hobject_hasprop(thr,
                                 DUK_GET_TVAL_NEGIDX(thr, -1),
                                 DUK_GET_TVAL_NEGIDX(thr, -2));

    duk_pop_2_unsafe(thr);
    return retval;
}

/*
 *  typeof
 *
 *  E5 Section 11.4.3.
 *
 *  Very straightforward.  The only question is what to return for our
 *  non-standard tag / object types.
 *
 *  There is an unfortunate string constant define naming problem with
 *  typeof return values for e.g. "Object" and "object"; careful with
 *  the built-in string defines.  The LC_XXX defines are used for the
 *  lowercase variants now.
 */

DUK_INTERNAL duk_small_uint_t duk_js_typeof_stridx(duk_tval *tv_x) {
    duk_small_uint_t stridx = 0;

    switch (DUK_TVAL_GET_TAG(tv_x)) {
    case DUK_TAG_UNDEFINED: {
        stridx = DUK_STRIDX_LC_UNDEFINED;
        break;
    }
    case DUK_TAG_NULL: {
        /* Note: not a typo, "object" is returned for a null value. */
        stridx = DUK_STRIDX_LC_OBJECT;
        break;
    }
    case DUK_TAG_BOOLEAN: {
        stridx = DUK_STRIDX_LC_BOOLEAN;
        break;
    }
    case DUK_TAG_POINTER: {
        /* Implementation specific. */
        stridx = DUK_STRIDX_LC_POINTER;
        break;
    }
    case DUK_TAG_STRING: {
        duk_hstring *str;

        /* All internal keys are identified as Symbols. */
        str = DUK_TVAL_GET_STRING(tv_x);
        DUK_ASSERT(str != NULL);
        if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(str))) {
            stridx = DUK_STRIDX_LC_SYMBOL;
        } else {
            stridx = DUK_STRIDX_LC_STRING;
        }
        break;
    }
    case DUK_TAG_OBJECT: {
        duk_hobject *obj = DUK_TVAL_GET_OBJECT(tv_x);
        DUK_ASSERT(obj != NULL);
        if (DUK_HOBJECT_IS_CALLABLE(obj)) {
            stridx = DUK_STRIDX_LC_FUNCTION;
        } else {
            stridx = DUK_STRIDX_LC_OBJECT;
        }
        break;
    }
    case DUK_TAG_BUFFER: {
        /* Implementation specific.  In Duktape 1.x this would be
         * 'buffer', in Duktape 2.x changed to 'object' because plain
         * buffers now mimic Uint8Array objects.
         */
        stridx = DUK_STRIDX_LC_OBJECT;
        break;
    }
    case DUK_TAG_LIGHTFUNC: {
        stridx = DUK_STRIDX_LC_FUNCTION;
        break;
    }
#if defined(DUK_USE_FASTINT)
    case DUK_TAG_FASTINT:
#endif
    default: {
        /* number */
        DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv_x));
        DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_x));
        stridx = DUK_STRIDX_LC_NUMBER;
        break;
    }
    }

    DUK_ASSERT_STRIDX_VALID(stridx);
    return stridx;
}

/*
 *  IsArray()
 */

DUK_INTERNAL duk_bool_t duk_js_isarray_hobject(duk_hobject *h) {
    DUK_ASSERT(h != NULL);
#if defined(DUK_USE_ES6_PROXY)
    h = duk_hobject_resolve_proxy_target(h);
#endif
    return (DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_ARRAY ? 1 : 0);
}

DUK_INTERNAL duk_bool_t duk_js_isarray(duk_tval *tv) {
    DUK_ASSERT(tv != NULL);
    if (DUK_TVAL_IS_OBJECT(tv)) {
        return duk_js_isarray_hobject(DUK_TVAL_GET_OBJECT(tv));
    }
    return 0;
}

/*
 *  Array index and length
 *
 *  Array index: E5 Section 15.4
 *  Array length: E5 Section 15.4.5.1 steps 3.c - 3.d (array length write)
 */

/* Compure array index from string context, or return a "not array index"
 * indicator.
 */
DUK_INTERNAL duk_uarridx_t duk_js_to_arrayindex_string(const duk_uint8_t *str, duk_uint32_t blen) {
    duk_uarridx_t res;

    /* Only strings with byte length 1-10 can be 32-bit array indices.
     * Leading zeroes (except '0' alone), plus/minus signs are not allowed.
     * We could do a lot of prechecks here, but since most strings won't
     * start with any digits, it's simpler to just parse the number and
     * fail quickly.
     */

    res = 0;
    if (blen == 0) {
        goto parse_fail;
    }
    do {
        duk_uarridx_t dig;
        dig = (duk_uarridx_t) (*str++) - DUK_ASC_0;

        if (dig <= 9U) {
            /* Careful overflow handling.  When multiplying by 10:
             * - 0x19999998 x 10 = 0xfffffff0: no overflow, and adding
             *   0...9 is safe.
             * - 0x19999999 x 10 = 0xfffffffa: no overflow, adding
             *   0...5 is safe, 6...9 overflows.
             * - 0x1999999a x 10 = 0x100000004: always overflow.
             */
            if (DUK_UNLIKELY(res >= 0x19999999UL)) {
                if (res >= 0x1999999aUL) {
                    /* Always overflow. */
                    goto parse_fail;
                }
                DUK_ASSERT(res == 0x19999999UL);
                if (dig >= 6U) {
                    goto parse_fail;
                }
                res = 0xfffffffaUL + dig;
                DUK_ASSERT(res >= 0xfffffffaUL);
                DUK_ASSERT_DISABLE(res <= 0xffffffffUL);  /* range */
            } else {
                res = res * 10U + dig;
                if (DUK_UNLIKELY(res == 0)) {
                    /* If 'res' is 0, previous 'res' must
                     * have been 0 and we scanned in a zero.
                     * This is only allowed if blen == 1,
                     * i.e. the exact string '0'.
                     */
                    if (blen == (duk_uint32_t) 1) {
                        return 0;
                    }
                    goto parse_fail;
                }
            }
        } else {
            /* Because 'dig' is unsigned, catches both values
             * above '9' and below '0'.
             */
            goto parse_fail;
        }
    } while (--blen > 0);

    return res;

 parse_fail:
    return DUK_HSTRING_NO_ARRAY_INDEX;
}

#if !defined(DUK_USE_HSTRING_ARRIDX)
/* Get array index for a string which is known to be an array index.  This helper
 * is needed when duk_hstring doesn't concretely store the array index, but strings
 * are flagged as array indices at intern time.
 */
DUK_INTERNAL duk_uarridx_t duk_js_to_arrayindex_hstring_fast_known(duk_hstring *h) {
    const duk_uint8_t *p;
    duk_uarridx_t res;
    duk_uint8_t t;

    DUK_ASSERT(h != NULL);
    DUK_ASSERT(DUK_HSTRING_HAS_ARRIDX(h));

    p = DUK_HSTRING_GET_DATA(h);
    res = 0;
    for (;;) {
        t = *p++;
        if (DUK_UNLIKELY(t == 0)) {
            /* Scanning to NUL is always safe for interned strings. */
            break;
        }
        DUK_ASSERT(t >= (duk_uint8_t) DUK_ASC_0 && t <= (duk_uint8_t) DUK_ASC_9);
        res = res * 10U + (duk_uarridx_t) t - (duk_uarridx_t) DUK_ASC_0;
    }
    return res;
}

DUK_INTERNAL duk_uarridx_t duk_js_to_arrayindex_hstring_fast(duk_hstring *h) {
    DUK_ASSERT(h != NULL);
    if (!DUK_HSTRING_HAS_ARRIDX(h)) {
        return DUK_HSTRING_NO_ARRAY_INDEX;
    }
    return duk_js_to_arrayindex_hstring_fast_known(h);
}
#endif  /* DUK_USE_HSTRING_ARRIDX */
//#line 1 "duk_js_var.c"
/*
 *  Identifier access and function closure handling.
 *
 *  Provides the primitives for slow path identifier accesses: GETVAR,
 *  PUTVAR, DELVAR, etc.  The fast path, direct register accesses, should
 *  be used for most identifier accesses.  Consequently, these slow path
 *  primitives should be optimized for maximum compactness.
 *
 *  ECMAScript environment records (declarative and object) are represented
 *  as internal objects with control keys.  Environment records have a
 *  parent record ("outer environment reference") which is represented by
 *  the implicit prototype for technical reasons (in other words, it is a
 *  convenient field).  The prototype chain is not followed in the ordinary
 *  sense for variable lookups.
 *
 *  See identifier-handling.rst for more details on the identifier algorithms
 *  and the internal representation.  See function-objects.rst for details on
 *  what function templates and instances are expected to look like.
 *
 *  Care must be taken to avoid duk_tval pointer invalidation caused by
 *  e.g. value stack or object resizing.
 *
 *  TODO: properties for function instances could be initialized much more
 *  efficiently by creating a property allocation for a certain size and
 *  filling in keys and values directly (and INCREFing both with "bulk incref"
 *  primitives.
 *
 *  XXX: duk_hobject_getprop() and duk_hobject_putprop() calls are a bit
 *  awkward (especially because they follow the prototype chain); rework
 *  if "raw" own property helpers are added.
 */

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

/*
 *  Local result type for duk__get_identifier_reference() lookup.
 */

typedef struct {
    duk_hobject *env;
    duk_hobject *holder;      /* for object-bound identifiers */
    duk_tval *value;          /* for register-bound and declarative env identifiers */
    duk_uint_t attrs;         /* property attributes for identifier (relevant if value != NULL) */
    duk_bool_t has_this;      /* for object-bound identifiers: provide 'this' binding */
} duk__id_lookup_result;

/*
 *  Create a new function object based on a "template function" which contains
 *  compiled bytecode, constants, etc, but lacks a lexical environment.
 *
 *  ECMAScript requires that each created closure is a separate object, with
 *  its own set of editable properties.  However, structured property values
 *  (such as the formal arguments list and the variable map) are shared.
 *  Also the bytecode, constants, and inner functions are shared.
 *
 *  See E5 Section 13.2 for detailed requirements on the function objects;
 *  there are no similar requirements for function "templates" which are an
 *  implementation dependent internal feature.  Also see function-objects.rst
 *  for a discussion on the function instance properties provided by this
 *  implementation.
 *
 *  Notes:
 *
 *   * Order of internal properties should match frequency of use, since the
 *     properties will be linearly scanned on lookup (functions usually don't
 *     have enough properties to warrant a hash part).
 *
 *   * The created closure is independent of its template; they do share the
 *     same 'data' buffer object, but the template object itself can be freed
 *     even if the closure object remains reachable.
 */

DUK_LOCAL void duk__inc_data_inner_refcounts(duk_hthread *thr, duk_hcompfunc *f) {
    duk_tval *tv, *tv_end;
    duk_hobject **funcs, **funcs_end;

    DUK_UNREF(thr);

    /* If function creation fails due to out-of-memory, the data buffer
     * pointer may be NULL in some cases.  That's actually possible for
     * GC code, but shouldn't be possible here because the incomplete
     * function will be unwound from the value stack and never instantiated.
     */
    DUK_ASSERT(DUK_HCOMPFUNC_GET_DATA(thr->heap, f) != NULL);

    tv = DUK_HCOMPFUNC_GET_CONSTS_BASE(thr->heap, f);
    tv_end = DUK_HCOMPFUNC_GET_CONSTS_END(thr->heap, f);
    while (tv < tv_end) {
        DUK_TVAL_INCREF(thr, tv);
        tv++;
    }

    funcs = DUK_HCOMPFUNC_GET_FUNCS_BASE(thr->heap, f);
    funcs_end = DUK_HCOMPFUNC_GET_FUNCS_END(thr->heap, f);
    while (funcs < funcs_end) {
        DUK_HEAPHDR_INCREF(thr, (duk_heaphdr *) *funcs);
        funcs++;
    }
}

/* Push a new closure on the stack.
 *
 * Note: if fun_temp has NEWENV, i.e. a new lexical and variable declaration
 * is created when the function is called, only outer_lex_env matters
 * (outer_var_env is ignored and may or may not be same as outer_lex_env).
 */

DUK_LOCAL const duk_uint16_t duk__closure_copy_proplist[] = {
    /* order: most frequent to least frequent */
    DUK_STRIDX_INT_VARMAP,
    DUK_STRIDX_INT_FORMALS,
#if defined(DUK_USE_PC2LINE)
    DUK_STRIDX_INT_PC2LINE,
#endif
#if defined(DUK_USE_FUNC_FILENAME_PROPERTY)
    DUK_STRIDX_FILE_NAME,
#endif
#if defined(DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY)
    DUK_STRIDX_INT_SOURCE
#endif
};

DUK_INTERNAL
void duk_js_push_closure(duk_hthread *thr,
                         duk_hcompfunc *fun_temp,
                         duk_hobject *outer_var_env,
                         duk_hobject *outer_lex_env,
                         duk_bool_t add_auto_proto) {
    duk_hcompfunc *fun_clos;
    duk_harray *formals;
    duk_small_uint_t i;
    duk_uint_t len_value;

    DUK_ASSERT(fun_temp != NULL);
    DUK_ASSERT(DUK_HCOMPFUNC_GET_DATA(thr->heap, fun_temp) != NULL);
    DUK_ASSERT(DUK_HCOMPFUNC_GET_FUNCS(thr->heap, fun_temp) != NULL);
    DUK_ASSERT(DUK_HCOMPFUNC_GET_BYTECODE(thr->heap, fun_temp) != NULL);
    DUK_ASSERT(outer_var_env != NULL);
    DUK_ASSERT(outer_lex_env != NULL);
    DUK_UNREF(len_value);

    DUK_STATS_INC(thr->heap, stats_envrec_pushclosure);

    fun_clos = duk_push_hcompfunc(thr);
    DUK_ASSERT(fun_clos != NULL);
    DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) fun_clos) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);

    duk_push_hobject(thr, &fun_temp->obj);  /* -> [ ... closure template ] */

    DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) fun_clos));
    DUK_ASSERT(DUK_HCOMPFUNC_GET_DATA(thr->heap, fun_clos) == NULL);
    DUK_ASSERT(DUK_HCOMPFUNC_GET_FUNCS(thr->heap, fun_clos) == NULL);
    DUK_ASSERT(DUK_HCOMPFUNC_GET_BYTECODE(thr->heap, fun_clos) == NULL);

    DUK_HCOMPFUNC_SET_DATA(thr->heap, fun_clos, DUK_HCOMPFUNC_GET_DATA(thr->heap, fun_temp));
    DUK_HCOMPFUNC_SET_FUNCS(thr->heap, fun_clos, DUK_HCOMPFUNC_GET_FUNCS(thr->heap, fun_temp));
    DUK_HCOMPFUNC_SET_BYTECODE(thr->heap, fun_clos, DUK_HCOMPFUNC_GET_BYTECODE(thr->heap, fun_temp));

    /* Note: all references inside 'data' need to get their refcounts
     * upped too.  This is the case because refcounts are decreased
     * through every function referencing 'data' independently.
     */

    DUK_HBUFFER_INCREF(thr, DUK_HCOMPFUNC_GET_DATA(thr->heap, fun_clos));
    duk__inc_data_inner_refcounts(thr, fun_temp);

    fun_clos->nregs = fun_temp->nregs;
    fun_clos->nargs = fun_temp->nargs;
#if defined(DUK_USE_DEBUGGER_SUPPORT)
    fun_clos->start_line = fun_temp->start_line;
    fun_clos->end_line = fun_temp->end_line;
#endif

    DUK_ASSERT(DUK_HCOMPFUNC_GET_DATA(thr->heap, fun_clos) != NULL);
    DUK_ASSERT(DUK_HCOMPFUNC_GET_FUNCS(thr->heap, fun_clos) != NULL);
    DUK_ASSERT(DUK_HCOMPFUNC_GET_BYTECODE(thr->heap, fun_clos) != NULL);

    /* XXX: Could also copy from template, but there's no way to have any
     * other value here now (used code has no access to the template).
     * Prototype is set by duk_push_hcompfunc().
     */
    DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, &fun_clos->obj) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
#if 0
    DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, &fun_clos->obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
#endif

    /* Copy duk_hobject flags as is from the template using a mask.
     * Leave out duk_heaphdr owned flags just in case (e.g. if there's
     * some GC flag or similar).  Some flags can then be adjusted
     * separately if necessary.
     */

    /* DUK_HEAPHDR_SET_FLAGS() masks changes to non-duk_heaphdr flags only. */
    DUK_HEAPHDR_SET_FLAGS((duk_heaphdr *) fun_clos, DUK_HEAPHDR_GET_FLAGS_RAW((duk_heaphdr *) fun_temp));
    DUK_DD(DUK_DDPRINT("fun_temp heaphdr flags: 0x%08lx, fun_clos heaphdr flags: 0x%08lx",
                       (unsigned long) DUK_HEAPHDR_GET_FLAGS_RAW((duk_heaphdr *) fun_temp),
                       (unsigned long) DUK_HEAPHDR_GET_FLAGS_RAW((duk_heaphdr *) fun_clos)));

    DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(&fun_clos->obj));
    DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(&fun_clos->obj));
    DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(&fun_clos->obj));
    DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(&fun_clos->obj));
    DUK_ASSERT(!DUK_HOBJECT_IS_THREAD(&fun_clos->obj));
    /* DUK_HOBJECT_FLAG_ARRAY_PART: don't care */
    /* DUK_HOBJECT_FLAG_NEWENV: handled below */
    DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARRAY(&fun_clos->obj));
    DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(&fun_clos->obj));
    DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(&fun_clos->obj));

    if (!DUK_HOBJECT_HAS_CONSTRUCTABLE(&fun_clos->obj)) {
        /* If the template is not constructable don't add an automatic
         * .prototype property.  This is the case for e.g. ES2015 object
         * literal getters/setters and method definitions.
         */
        add_auto_proto = 0;
    }

    /*
     *  Setup environment record properties based on the template and
     *  its flags.
     *
     *  If DUK_HOBJECT_HAS_NEWENV(fun_temp) is true, the environment
     *  records represent identifiers "outside" the function; the
     *  "inner" environment records are created on demand.  Otherwise,
     *  the environment records are those that will be directly used
     *  (e.g. for declarations).
     *
     *  _Lexenv is always set; _Varenv defaults to _Lexenv if missing,
     *  so _Varenv is only set if _Lexenv != _Varenv.
     *
     *  This is relatively complex, see doc/identifier-handling.rst.
     */

    if (DUK_HOBJECT_HAS_NEWENV(&fun_clos->obj)) {
#if defined(DUK_USE_FUNC_NAME_PROPERTY)
        if (DUK_HOBJECT_HAS_NAMEBINDING(&fun_clos->obj)) {
            duk_hobject *proto;
            duk_hdecenv *new_env;

            /*
             *  Named function expression, name needs to be bound
             *  in an intermediate environment record.  The "outer"
             *  lexical/variable environment will thus be:
             *
             *  a) { funcname: <func>, __prototype: outer_lex_env }
             *  b) { funcname: <func>, __prototype:  <globalenv> }  (if outer_lex_env missing)
             */

            if (outer_lex_env) {
                proto = outer_lex_env;
            } else {
                proto = thr->builtins[DUK_BIDX_GLOBAL_ENV];
            }

            /* -> [ ... closure template env ] */
            new_env = duk_hdecenv_alloc(thr,
                                        DUK_HOBJECT_FLAG_EXTENSIBLE |
                                        DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV));
            DUK_ASSERT(new_env != NULL);
            duk_push_hobject(thr, (duk_hobject *) new_env);

            DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) new_env) == NULL);
            DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) new_env, proto);
            DUK_HOBJECT_INCREF_ALLOWNULL(thr, proto);

            DUK_ASSERT(new_env->thread == NULL);  /* Closed. */
            DUK_ASSERT(new_env->varmap == NULL);

            /* It's important that duk_xdef_prop() is a 'raw define' so that any
             * properties in an ancestor are never an issue (they should never be
             * e.g. non-writable, but just in case).
             *
             * Because template objects are not visible to user code, the case
             * where .name is missing shouldn't happen in practice.  It it does,
             * the name 'undefined' gets bound and maps to the closure (which is
             * a bit odd, but safe).
             */
            (void) duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_NAME);
            /* -> [ ... closure template env funcname ] */
            duk_dup_m4(thr);                                           /* -> [ ... closure template env funcname closure ] */
            duk_xdef_prop(thr, -3, DUK_PROPDESC_FLAGS_NONE);           /* -> [ ... closure template env ] */
            /* env[funcname] = closure */

            /* [ ... closure template env ] */

            DUK_HCOMPFUNC_SET_LEXENV(thr->heap, fun_clos, (duk_hobject *) new_env);
            DUK_HCOMPFUNC_SET_VARENV(thr->heap, fun_clos, (duk_hobject *) new_env);
            DUK_HOBJECT_INCREF(thr, (duk_hobject *) new_env);
            DUK_HOBJECT_INCREF(thr, (duk_hobject *) new_env);
            duk_pop_unsafe(thr);

            /* [ ... closure template ] */
        }
        else
#endif  /* DUK_USE_FUNC_NAME_PROPERTY */
        {
            /*
             *  Other cases (function declaration, anonymous function expression,
             *  strict direct eval code).  The "outer" environment will be whatever
             *  the caller gave us.
             */

            DUK_HCOMPFUNC_SET_LEXENV(thr->heap, fun_clos, outer_lex_env);
            DUK_HCOMPFUNC_SET_VARENV(thr->heap, fun_clos, outer_lex_env);
            DUK_HOBJECT_INCREF(thr, outer_lex_env);
            DUK_HOBJECT_INCREF(thr, outer_lex_env);

            /* [ ... closure template ] */
        }
    } else {
        /*
         *  Function gets no new environment when called.  This is the
         *  case for global code, indirect eval code, and non-strict
         *  direct eval code.  There is no direct correspondence to the
         *  E5 specification, as global/eval code is not exposed as a
         *  function.
         */

        DUK_ASSERT(!DUK_HOBJECT_HAS_NAMEBINDING(&fun_temp->obj));

        DUK_HCOMPFUNC_SET_LEXENV(thr->heap, fun_clos, outer_lex_env);
        DUK_HCOMPFUNC_SET_VARENV(thr->heap, fun_clos, outer_var_env);
        DUK_HOBJECT_INCREF(thr, outer_lex_env);  /* NULLs not allowed; asserted on entry */
        DUK_HOBJECT_INCREF(thr, outer_var_env);
    }
    DUK_DDD(DUK_DDDPRINT("closure varenv -> %!ipO, lexenv -> %!ipO",
                         (duk_heaphdr *) fun_clos->var_env,
                         (duk_heaphdr *) fun_clos->lex_env));

    /* Call handling assumes this for all callable closures. */
    DUK_ASSERT(DUK_HCOMPFUNC_GET_LEXENV(thr->heap, fun_clos) != NULL);
    DUK_ASSERT(DUK_HCOMPFUNC_GET_VARENV(thr->heap, fun_clos) != NULL);

    /*
     *  Copy some internal properties directly
     *
     *  The properties will be non-writable and non-enumerable, but
     *  configurable.
     *
     *  Function templates are bare objects, so inheritance of internal
     *  Symbols is not an issue here even when using ordinary property
     *  reads.  The function instance created is not bare, so internal
     *  Symbols must be defined without inheritance checks.
     */

    /* [ ... closure template ] */

    DUK_DDD(DUK_DDDPRINT("copying properties: closure=%!iT, template=%!iT",
                         (duk_tval *) duk_get_tval(thr, -2),
                         (duk_tval *) duk_get_tval(thr, -1)));

    for (i = 0; i < (duk_small_uint_t) (sizeof(duk__closure_copy_proplist) / sizeof(duk_uint16_t)); i++) {
        duk_small_int_t stridx = (duk_small_int_t) duk__closure_copy_proplist[i];
        if (duk_xget_owndataprop_stridx_short(thr, -1, stridx)) {
            /* [ ... closure template val ] */
            DUK_DDD(DUK_DDDPRINT("copying property, stridx=%ld -> found", (long) stridx));
            duk_xdef_prop_stridx_short(thr, -3, stridx, DUK_PROPDESC_FLAGS_C);
        } else {
            DUK_DDD(DUK_DDDPRINT("copying property, stridx=%ld -> not found", (long) stridx));
            duk_pop_unsafe(thr);
        }
    }

    /*
     *  "length" maps to number of formals (E5 Section 13.2) for function
     *  declarations/expressions (non-bound functions).  Note that 'nargs'
     *  is NOT necessarily equal to the number of arguments.  Use length
     *  of _Formals; if missing, assume nargs matches .length.
     */

    /* [ ... closure template ] */

    formals = duk_hobject_get_formals(thr, (duk_hobject *) fun_temp);
    if (formals) {
        len_value = (duk_uint_t) formals->length;
        DUK_DD(DUK_DDPRINT("closure length from _Formals -> %ld", (long) len_value));
    } else {
        len_value = fun_temp->nargs;
        DUK_DD(DUK_DDPRINT("closure length defaulted from nargs -> %ld", (long) len_value));
    }

    duk_push_uint(thr, len_value);  /* [ ... closure template len_value ] */
    duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_C);

    /*
     *  "prototype" is, by default, a fresh object with the "constructor"
     *  property.
     *
     *  Note that this creates a circular reference for every function
     *  instance (closure) which prevents refcount-based collection of
     *  function instances.
     *
     *  XXX: Try to avoid creating the default prototype object, because
     *  many functions are not used as constructors and the default
     *  prototype is unnecessary.  Perhaps it could be created on-demand
     *  when it is first accessed?
     */

    /* [ ... closure template ] */

    if (add_auto_proto) {
        duk_push_object(thr);  /* -> [ ... closure template newobj ] */
        duk_dup_m3(thr);       /* -> [ ... closure template newobj closure ] */
        duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_CONSTRUCTOR, DUK_PROPDESC_FLAGS_WC);  /* -> [ ... closure template newobj ] */
        duk_compact(thr, -1);  /* compact the prototype */
        duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_PROTOTYPE, DUK_PROPDESC_FLAGS_W);     /* -> [ ... closure template ] */
    }

    /*
     *  "arguments" and "caller" must be mapped to throwers for strict
     *  mode and bound functions (E5 Section 15.3.5).
     *
     *  XXX: This is expensive to have for every strict function instance.
     *  Try to implement as virtual properties or on-demand created properties.
     */

    /* [ ... closure template ] */

    if (DUK_HOBJECT_HAS_STRICT(&fun_clos->obj)) {
        duk_xdef_prop_stridx_thrower(thr, -2, DUK_STRIDX_CALLER);
        duk_xdef_prop_stridx_thrower(thr, -2, DUK_STRIDX_LC_ARGUMENTS);
    } else {
#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
        DUK_DDD(DUK_DDDPRINT("function is non-strict and non-standard 'caller' property in use, add initial 'null' value"));
        duk_push_null(thr);
        duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_CALLER, DUK_PROPDESC_FLAGS_NONE);
#else
        DUK_DDD(DUK_DDDPRINT("function is non-strict and non-standard 'caller' property not used"));
#endif
    }

    /*
     *  "name" used to be non-standard but is now defined by ES2015.
     *  In ES2015/ES2016 the .name property is configurable.
     */

    /* [ ... closure template ] */

#if defined(DUK_USE_FUNC_NAME_PROPERTY)
    /* XXX: Look for own property only; doesn't matter much because
     * templates are bare objects.
     */
    if (duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_NAME)) {
        /* [ ... closure template name ] */
        DUK_ASSERT(duk_is_string(thr, -1));
        DUK_DD(DUK_DDPRINT("setting function instance name to %!T", duk_get_tval(thr, -1)));
        duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C);  /* -> [ ... closure template ] */
    } else {
        /* Anonymous functions don't have a .name in ES2015, so don't set
         * it on the instance either.  The instance will then inherit
         * it from Function.prototype.name.
         */
        DUK_DD(DUK_DDPRINT("not setting function instance .name"));
        duk_pop_unsafe(thr);
    }
#endif

    /*
     *  Compact the closure, in most cases no properties will be added later.
     *  Also, without this the closures end up having unused property slots
     *  (e.g. in Duktape 0.9.0, 8 slots would be allocated and only 7 used).
     *  A better future solution would be to allocate the closure directly
     *  to correct size (and setup the properties directly without going
     *  through the API).
     */

    duk_compact(thr, -2);

    /*
     *  Some assertions (E5 Section 13.2).
     */

    DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(&fun_clos->obj) == DUK_HOBJECT_CLASS_FUNCTION);
    DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, &fun_clos->obj) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
    DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(&fun_clos->obj));
    DUK_ASSERT(duk_has_prop_stridx(thr, -2, DUK_STRIDX_LENGTH) != 0);
    DUK_ASSERT(add_auto_proto == 0 || duk_has_prop_stridx(thr, -2, DUK_STRIDX_PROTOTYPE) != 0);
    /* May be missing .name */
    DUK_ASSERT(!DUK_HOBJECT_HAS_STRICT(&fun_clos->obj) ||
               duk_has_prop_stridx(thr, -2, DUK_STRIDX_CALLER) != 0);
    DUK_ASSERT(!DUK_HOBJECT_HAS_STRICT(&fun_clos->obj) ||
               duk_has_prop_stridx(thr, -2, DUK_STRIDX_LC_ARGUMENTS) != 0);

    /*
     *  Finish
     */

    /* [ ... closure template ] */

    DUK_DDD(DUK_DDDPRINT("created function instance: template=%!iT -> closure=%!iT",
                         (duk_tval *) duk_get_tval(thr, -1),
                         (duk_tval *) duk_get_tval(thr, -2)));

    duk_pop_unsafe(thr);

    /* [ ... closure ] */
}

/*
 *  Delayed activation environment record initialization (for functions
 *  with NEWENV).
 *
 *  The non-delayed initialization is handled by duk_handle_call().
 */

DUK_LOCAL void duk__preallocate_env_entries(duk_hthread *thr, duk_hobject *varmap, duk_hobject *env) {
    duk_uint_fast32_t i;

    for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(varmap); i++) {
        duk_hstring *key;

        key = DUK_HOBJECT_E_GET_KEY(thr->heap, varmap, i);
        DUK_ASSERT(key != NULL);   /* assume keys are compact in _Varmap */
        DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, varmap, i));  /* assume plain values */

        /* Predefine as 'undefined' to reserve a property slot.
         * This makes the unwind process (where register values
         * are copied to the env object) safe against throwing.
         *
         * XXX: This could be made much faster by creating the
         * property table directly.
         */
        duk_push_undefined(thr);
        DUK_DDD(DUK_DDDPRINT("preallocate env entry for key %!O", key));
        duk_hobject_define_property_internal(thr, env, key, DUK_PROPDESC_FLAGS_WE);
    }
}

/* shared helper */
DUK_INTERNAL
duk_hobject *duk_create_activation_environment_record(duk_hthread *thr,
                                                      duk_hobject *func,
                                                      duk_size_t bottom_byteoff) {
    duk_hdecenv *env;
    duk_hobject *parent;
    duk_hcompfunc *f;

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

    DUK_STATS_INC(thr->heap, stats_envrec_create);

    f = (duk_hcompfunc *) func;
    parent = DUK_HCOMPFUNC_GET_LEXENV(thr->heap, f);
    if (!parent) {
        parent = thr->builtins[DUK_BIDX_GLOBAL_ENV];
    }

    env = duk_hdecenv_alloc(thr,
                            DUK_HOBJECT_FLAG_EXTENSIBLE |
                            DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV));
    DUK_ASSERT(env != NULL);
    duk_push_hobject(thr, (duk_hobject *) env);

    DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) env) == NULL);
    DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) env, parent);
    DUK_HOBJECT_INCREF_ALLOWNULL(thr, parent);  /* parent env is the prototype */

    /* open scope information, for compiled functions only */

    DUK_ASSERT(env->thread == NULL);
    DUK_ASSERT(env->varmap == NULL);
    DUK_ASSERT(env->regbase_byteoff == 0);
    if (DUK_HOBJECT_IS_COMPFUNC(func)) {
        duk_hobject *varmap;

        varmap = duk_hobject_get_varmap(thr, func);
        if (varmap != NULL) {
            env->varmap = varmap;
            DUK_HOBJECT_INCREF(thr, varmap);
            env->thread = thr;
            DUK_HTHREAD_INCREF(thr, thr);
            env->regbase_byteoff = bottom_byteoff;

            /* Preallocate env property table to avoid potential
             * for out-of-memory on unwind when the env is closed.
             */
            duk__preallocate_env_entries(thr, varmap, (duk_hobject *) env);
        } else {
            /* If function has no _Varmap, leave the environment closed. */
            DUK_ASSERT(env->thread == NULL);
            DUK_ASSERT(env->varmap == NULL);
            DUK_ASSERT(env->regbase_byteoff == 0);
        }
    }

    return (duk_hobject *) env;
}

DUK_INTERNAL
void duk_js_init_activation_environment_records_delayed(duk_hthread *thr,
                                                        duk_activation *act) {
    duk_hobject *func;
    duk_hobject *env;

    DUK_ASSERT(thr != NULL);
    func = DUK_ACT_GET_FUNC(act);
    DUK_ASSERT(func != NULL);
    DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func));  /* bound functions are never in act 'func' */

    /*
     *  Delayed initialization only occurs for 'NEWENV' functions.
     */

    DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV(func));
    DUK_ASSERT(act->lex_env == NULL);
    DUK_ASSERT(act->var_env == NULL);

    DUK_STATS_INC(thr->heap, stats_envrec_delayedcreate);

    env = duk_create_activation_environment_record(thr, func, act->bottom_byteoff);
    DUK_ASSERT(env != NULL);
    /* 'act' is a stable pointer, so still OK. */

    DUK_DDD(DUK_DDDPRINT("created delayed fresh env: %!ipO", (duk_heaphdr *) env));
#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)
    {
        duk_hobject *p = env;
        while (p) {
            DUK_DDD(DUK_DDDPRINT("  -> %!ipO", (duk_heaphdr *) p));
            p = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, p);
        }
    }
#endif

    act->lex_env = env;
    act->var_env = env;
    DUK_HOBJECT_INCREF(thr, env);  /* XXX: incref by count (here 2 times) */
    DUK_HOBJECT_INCREF(thr, env);

    duk_pop_unsafe(thr);
}

/*
 *  Closing environment records.
 *
 *  The environment record MUST be closed with the thread where its activation
 *  is; i.e. if 'env' is open, 'thr' must match env->thread, and the regbase
 *  and varmap must still be valid.  On entry, 'env' must be reachable.
 */

DUK_INTERNAL void duk_js_close_environment_record(duk_hthread *thr, duk_hobject *env) {
    duk_uint_fast32_t i;
    duk_hobject *varmap;
    duk_hstring *key;
    duk_tval *tv;
    duk_uint_t regnum;

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

    if (DUK_UNLIKELY(!DUK_HOBJECT_IS_DECENV(env))) {
        DUK_DDD(DUK_DDDPRINT("env not a declarative record: %!iO", (duk_heaphdr *) env));
        return;
    }

    varmap = ((duk_hdecenv *) env)->varmap;
    if (varmap == NULL) {
        DUK_DDD(DUK_DDDPRINT("env already closed: %!iO", (duk_heaphdr *) env));

        return;
    }
    DUK_ASSERT(((duk_hdecenv *) env)->thread != NULL);
    DUK_HDECENV_ASSERT_VALID((duk_hdecenv *) env);

    DUK_DDD(DUK_DDDPRINT("closing env: %!iO", (duk_heaphdr *) env));
    DUK_DDD(DUK_DDDPRINT("varmap: %!O", (duk_heaphdr *) varmap));

    /* Env must be closed in the same thread as where it runs. */
    DUK_ASSERT(((duk_hdecenv *) env)->thread == thr);

    /* XXX: additional conditions when to close variables? we don't want to do it
     * unless the environment may have "escaped" (referenced in a function closure).
     * With delayed environments, the existence is probably good enough of a check.
     */

    /* Note: we rely on the _Varmap having a bunch of nice properties, like:
     *  - being compacted and unmodified during this process
     *  - not containing an array part
     *  - having correct value types
     */

    DUK_DDD(DUK_DDDPRINT("copying bound register values, %ld bound regs", (long) DUK_HOBJECT_GET_ENEXT(varmap)));

    /* Copy over current variable values from value stack to the
     * environment record.  The scope object is empty but may
     * inherit from another scope which has conflicting names.
     */

    /* XXX: Do this using a once allocated entry area, no side effects.
     * Hash part would need special treatment however (maybe copy, and
     * then realloc with hash part if large enough).
     */
    for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(varmap); i++) {
        duk_size_t regbase_byteoff;

        key = DUK_HOBJECT_E_GET_KEY(thr->heap, varmap, i);
        DUK_ASSERT(key != NULL);   /* assume keys are compact in _Varmap */
        DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, varmap, i));  /* assume plain values */

        tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, varmap, i);
        DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
        DUK_ASSERT(DUK_TVAL_GET_NUMBER(tv) <= (duk_double_t) DUK_UINT32_MAX);  /* limits */
#if defined(DUK_USE_FASTINT)
        DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv));
        regnum = (duk_uint_t) DUK_TVAL_GET_FASTINT_U32(tv);
#else
        regnum = (duk_uint_t) DUK_TVAL_GET_NUMBER(tv);
#endif

        regbase_byteoff = ((duk_hdecenv *) env)->regbase_byteoff;
        DUK_ASSERT((duk_uint8_t *) thr->valstack + regbase_byteoff + sizeof(duk_tval) * regnum >= (duk_uint8_t *) thr->valstack);
        DUK_ASSERT((duk_uint8_t *) thr->valstack + regbase_byteoff + sizeof(duk_tval) * regnum < (duk_uint8_t *) thr->valstack_top);

        /* Write register value into env as named properties.
         * If property already exists, overwrites silently.
         * Property is writable, but not deletable (not configurable
         * in terms of property attributes).
         *
         * This property write must not throw because we're unwinding
         * and unwind code is not allowed to throw at present.  The
         * call itself has no such guarantees, but we've preallocated
         * entries for each property when the env was created, so no
         * out-of-memory error should be possible.  If this guarantee
         * is not provided, problems like GH-476 may happen.
         */
        duk_push_tval(thr, (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + regbase_byteoff + sizeof(duk_tval) * regnum));
        DUK_DDD(DUK_DDDPRINT("closing identifier %!O -> reg %ld, value %!T",
                             (duk_heaphdr *) key,
                             (long) regnum,
                             (duk_tval *) duk_get_tval(thr, -1)));
        duk_hobject_define_property_internal(thr, env, key, DUK_PROPDESC_FLAGS_WE);
    }

    /* NULL atomically to avoid inconsistent state + side effects. */
    DUK_HOBJECT_DECREF_NORZ(thr, ((duk_hdecenv *) env)->thread);
    DUK_HOBJECT_DECREF_NORZ(thr, ((duk_hdecenv *) env)->varmap);
    ((duk_hdecenv *) env)->thread = NULL;
    ((duk_hdecenv *) env)->varmap = NULL;

    DUK_DDD(DUK_DDDPRINT("env after closing: %!O", (duk_heaphdr *) env));
}

/*
 *  GETIDREF: a GetIdentifierReference-like helper.
 *
 *  Provides a parent traversing lookup and a single level lookup
 *  (for HasBinding).
 *
 *  Instead of returning the value, returns a bunch of values allowing
 *  the caller to read, write, or delete the binding.  Value pointers
 *  are duk_tval pointers which can be mutated directly as long as
 *  refcounts are properly updated.  Note that any operation which may
 *  reallocate valstacks or compact objects may invalidate the returned
 *  duk_tval (but not object) pointers, so caller must be very careful.
 *
 *  If starting environment record 'env' is given, 'act' is ignored.
 *  However, if 'env' is NULL, the caller may identify, in 'act', an
 *  activation which hasn't had its declarative environment initialized
 *  yet.  The activation registers are then looked up, and its parent
 *  traversed normally.
 *
 *  The 'out' structure values are only valid if the function returns
 *  success (non-zero).
 */

/* lookup name from an open declarative record's registers */
DUK_LOCAL
duk_bool_t duk__getid_open_decl_env_regs(duk_hthread *thr,
                                         duk_hstring *name,
                                         duk_hdecenv *env,
                                         duk__id_lookup_result *out) {
    duk_tval *tv;
    duk_size_t reg_rel;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(name != NULL);
    DUK_ASSERT(env != NULL);
    DUK_ASSERT(out != NULL);

    DUK_ASSERT(DUK_HOBJECT_IS_DECENV((duk_hobject *) env));
    DUK_HDECENV_ASSERT_VALID(env);

    if (env->thread == NULL) {
        /* already closed */
        return 0;
    }
    DUK_ASSERT(env->varmap != NULL);

    tv = duk_hobject_find_entry_tval_ptr(thr->heap, env->varmap, name);
    if (DUK_UNLIKELY(tv == NULL)) {
        return 0;
    }

    DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
    DUK_ASSERT(DUK_TVAL_GET_NUMBER(tv) <= (duk_double_t) DUK_UINT32_MAX);  /* limits */
#if defined(DUK_USE_FASTINT)
    DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv));
    reg_rel = (duk_size_t) DUK_TVAL_GET_FASTINT_U32(tv);
#else
    reg_rel = (duk_size_t) DUK_TVAL_GET_NUMBER(tv);
#endif
    DUK_ASSERT_DISABLE(reg_rel >= 0);  /* unsigned */

    tv = (duk_tval *) (void *) ((duk_uint8_t *) env->thread->valstack + env->regbase_byteoff + sizeof(duk_tval) * reg_rel);
    DUK_ASSERT(tv >= env->thread->valstack && tv < env->thread->valstack_end);  /* XXX: more accurate? */

    out->value = tv;
    out->attrs = DUK_PROPDESC_FLAGS_W;  /* registers are mutable, non-deletable */
    out->env = (duk_hobject *) env;
    out->holder = NULL;
    out->has_this = 0;
    return 1;
}

/* lookup name from current activation record's functions' registers */
DUK_LOCAL
duk_bool_t duk__getid_activation_regs(duk_hthread *thr,
                                      duk_hstring *name,
                                      duk_activation *act,
                                      duk__id_lookup_result *out) {
    duk_tval *tv;
    duk_hobject *func;
    duk_hobject *varmap;
    duk_size_t reg_rel;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(name != NULL);
    DUK_ASSERT(act != NULL);
    DUK_ASSERT(out != NULL);

    func = DUK_ACT_GET_FUNC(act);
    DUK_ASSERT(func != NULL);
    DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV(func));

    if (!DUK_HOBJECT_IS_COMPFUNC(func)) {
        return 0;
    }

    /* XXX: move varmap to duk_hcompfunc struct field? */
    varmap = duk_hobject_get_varmap(thr, func);
    if (!varmap) {
        return 0;
    }

    tv = duk_hobject_find_entry_tval_ptr(thr->heap, varmap, name);
    if (!tv) {
        return 0;
    }
    DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
    reg_rel = (duk_size_t) DUK_TVAL_GET_NUMBER(tv);
    DUK_ASSERT_DISABLE(reg_rel >= 0);
    DUK_ASSERT(reg_rel < ((duk_hcompfunc *) func)->nregs);

    tv = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + act->bottom_byteoff);
    tv += reg_rel;

    out->value = tv;
    out->attrs = DUK_PROPDESC_FLAGS_W;  /* registers are mutable, non-deletable */
    out->env = NULL;
    out->holder = NULL;
    out->has_this = 0;
    return 1;
}

DUK_LOCAL
duk_bool_t duk__get_identifier_reference(duk_hthread *thr,
                                         duk_hobject *env,
                                         duk_hstring *name,
                                         duk_activation *act,
                                         duk_bool_t parents,
                                         duk__id_lookup_result *out) {
    duk_tval *tv;
    duk_uint_t sanity;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(env != NULL || act != NULL);
    DUK_ASSERT(name != NULL);
    DUK_ASSERT(out != NULL);

    DUK_ASSERT(!env || DUK_HOBJECT_IS_ENV(env));
    DUK_ASSERT(!env || !DUK_HOBJECT_HAS_ARRAY_PART(env));

    /*
     *  Conceptually, we look for the identifier binding by starting from
     *  'env' and following to chain of environment records (represented
     *  by the prototype chain).
     *
     *  If 'env' is NULL, the current activation does not yet have an
     *  allocated declarative environment record; this should be treated
     *  exactly as if the environment record existed but had no bindings
     *  other than register bindings.
     *
     *  Note: we assume that with the DUK_HOBJECT_FLAG_NEWENV cleared
     *  the environment will always be initialized immediately; hence
     *  a NULL 'env' should only happen with the flag set.  This is the
     *  case for: (1) function calls, and (2) strict, direct eval calls.
     */

    if (env == NULL && act != NULL) {
        duk_hobject *func;
        duk_hcompfunc *f;

        DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference: env is NULL, activation is non-NULL -> "
                             "delayed env case, look up activation regs first"));

        /*
         *  Try registers
         */

        if (duk__getid_activation_regs(thr, name, act, out)) {
            DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: "
                                 "name=%!O -> value=%!T, attrs=%ld, has_this=%ld, env=%!O, holder=%!O "
                                 "(found from register bindings when env=NULL)",
                                 (duk_heaphdr *) name, (duk_tval *) out->value,
                                 (long) out->attrs, (long) out->has_this,
                                 (duk_heaphdr *) out->env, (duk_heaphdr *) out->holder));
            return 1;
        }

        DUK_DDD(DUK_DDDPRINT("not found in current activation regs"));

        /*
         *  Not found in registers, proceed to the parent record.
         *  Here we need to determine what the parent would be,
         *  if 'env' was not NULL (i.e. same logic as when initializing
         *  the record).
         *
         *  Note that environment initialization is only deferred when
         *  DUK_HOBJECT_HAS_NEWENV is set, and this only happens for:
         *    - Function code
         *    - Strict eval code
         *
         *  We only need to check _Lexenv here; _Varenv exists only if it
         *  differs from _Lexenv (and thus _Lexenv will also be present).
         */

        if (!parents) {
            DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference failed, no parent traversal "
                                 "(not found from register bindings when env=NULL)"));
            goto fail_not_found;
        }

        func = DUK_ACT_GET_FUNC(act);
        DUK_ASSERT(func != NULL);
        DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV(func));
        f = (duk_hcompfunc *) func;

        env = DUK_HCOMPFUNC_GET_LEXENV(thr->heap, f);
        if (!env) {
            env = thr->builtins[DUK_BIDX_GLOBAL_ENV];
        }

        DUK_DDD(DUK_DDDPRINT("continue lookup from env: %!iO",
                             (duk_heaphdr *) env));
    }

    /*
     *  Prototype walking starting from 'env'.
     *
     *  ('act' is not needed anywhere here.)
     */

    sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
    while (env != NULL) {
        duk_small_uint_t cl;
        duk_uint_t attrs;

        DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference, name=%!O, considering env=%p -> %!iO",
                             (duk_heaphdr *) name,
                             (void *) env,
                             (duk_heaphdr *) env));

        DUK_ASSERT(env != NULL);
        DUK_ASSERT(DUK_HOBJECT_IS_ENV(env));
        DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(env));

        cl = DUK_HOBJECT_GET_CLASS_NUMBER(env);
        DUK_ASSERT(cl == DUK_HOBJECT_CLASS_OBJENV || cl == DUK_HOBJECT_CLASS_DECENV);
        if (cl == DUK_HOBJECT_CLASS_DECENV) {
            /*
             *  Declarative environment record.
             *
             *  Identifiers can never be stored in ancestors and are
             *  always plain values, so we can use an internal helper
             *  and access the value directly with an duk_tval ptr.
             *
             *  A closed environment is only indicated by it missing
             *  the "book-keeping" properties required for accessing
             *  register-bound variables.
             */

            DUK_HDECENV_ASSERT_VALID((duk_hdecenv *) env);
            if (duk__getid_open_decl_env_regs(thr, name, (duk_hdecenv *) env, out)) {
                DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: "
                                     "name=%!O -> value=%!T, attrs=%ld, has_this=%ld, env=%!O, holder=%!O "
                                     "(declarative environment record, scope open, found in regs)",
                                     (duk_heaphdr *) name, (duk_tval *) out->value,
                                     (long) out->attrs, (long) out->has_this,
                                     (duk_heaphdr *) out->env, (duk_heaphdr *) out->holder));
                return 1;
            }

            tv = duk_hobject_find_entry_tval_ptr_and_attrs(thr->heap, env, name, &attrs);
            if (tv) {
                out->value = tv;
                out->attrs = attrs;
                out->env = env;
                out->holder = env;
                out->has_this = 0;

                DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: "
                                     "name=%!O -> value=%!T, attrs=%ld, has_this=%ld, env=%!O, holder=%!O "
                                     "(declarative environment record, found in properties)",
                                     (duk_heaphdr *) name, (duk_tval *) out->value,
                                     (long) out->attrs, (long) out->has_this,
                                     (duk_heaphdr *) out->env, (duk_heaphdr *) out->holder));
                return 1;
            }
        } else {
            /*
             *  Object environment record.
             *
             *  Binding (target) object is an external, uncontrolled object.
             *  Identifier may be bound in an ancestor property, and may be
             *  an accessor.  Target can also be a Proxy which we must support
             *  here.
             */

            /* XXX: we could save space by using _Target OR _This.  If _Target, assume
             * this binding is undefined.  If _This, assumes this binding is _This, and
             * target is also _This.  One property would then be enough.
             */

            duk_hobject *target;
            duk_bool_t found;

            DUK_ASSERT(cl == DUK_HOBJECT_CLASS_OBJENV);
            DUK_HOBJENV_ASSERT_VALID((duk_hobjenv *) env);

            target = ((duk_hobjenv *) env)->target;
            DUK_ASSERT(target != NULL);

            /* Target may be a Proxy or property may be an accessor, so we must
             * use an actual, Proxy-aware hasprop check here.
             *
             * out->holder is NOT set to the actual duk_hobject where the
             * property is found, but rather the object binding target object.
             */

#if defined(DUK_USE_ES6_PROXY)
            if (DUK_UNLIKELY(DUK_HOBJECT_IS_PROXY(target))) {
                duk_tval tv_name;
                duk_tval tv_target_tmp;

                DUK_ASSERT(name != NULL);
                DUK_TVAL_SET_STRING(&tv_name, name);
                DUK_TVAL_SET_OBJECT(&tv_target_tmp, target);

                found = duk_hobject_hasprop(thr, &tv_target_tmp, &tv_name);
            } else
#endif  /* DUK_USE_ES6_PROXY */
            {
                /* XXX: duk_hobject_hasprop() would be correct for
                 * non-Proxy objects too, but it is about ~20-25%
                 * slower at present so separate code paths for
                 * Proxy and non-Proxy now.
                 */
                found = duk_hobject_hasprop_raw(thr, target, name);
            }

            if (found) {
                out->value = NULL;  /* can't get value, may be accessor */
                out->attrs = 0;     /* irrelevant when out->value == NULL */
                out->env = env;
                out->holder = target;
                out->has_this = ((duk_hobjenv *) env)->has_this;

                DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: "
                                     "name=%!O -> value=%!T, attrs=%ld, has_this=%ld, env=%!O, holder=%!O "
                                     "(object environment record)",
                                     (duk_heaphdr *) name, (duk_tval *) out->value,
                                     (long) out->attrs, (long) out->has_this,
                                     (duk_heaphdr *) out->env, (duk_heaphdr *) out->holder));
                return 1;
            }
        }

        if (!parents) {
            DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference failed, no parent traversal "
                                 "(not found from first traversed env)"));
            goto fail_not_found;
        }

                if (DUK_UNLIKELY(sanity-- == 0)) {
                        DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT);
            DUK_WO_NORETURN(return 0;);
                }
        env = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, env);
    }

    /*
     *  Not found (even in global object)
     */

 fail_not_found:
    return 0;
}

/*
 *  HASVAR: check identifier binding from a given environment record
 *  without traversing its parents.
 *
 *  This primitive is not exposed to user code as such, but is used
 *  internally for e.g. declaration binding instantiation.
 *
 *  See E5 Sections:
 *    10.2.1.1.1 HasBinding(N)
 *    10.2.1.2.1 HasBinding(N)
 *
 *  Note: strictness has no bearing on this check.  Hence we don't take
 *  a 'strict' parameter.
 */

#if 0  /*unused*/
DUK_INTERNAL
duk_bool_t duk_js_hasvar_envrec(duk_hthread *thr,
                                duk_hobject *env,
                                duk_hstring *name) {
    duk__id_lookup_result ref;
    duk_bool_t parents;

    DUK_DDD(DUK_DDDPRINT("hasvar: thr=%p, env=%p, name=%!O "
                         "(env -> %!dO)",
                         (void *) thr, (void *) env, (duk_heaphdr *) name,
                         (duk_heaphdr *) env));

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(env != NULL);
    DUK_ASSERT(name != NULL);

        DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(env);
        DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(name);

    DUK_ASSERT(DUK_HOBJECT_IS_ENV(env));
    DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(env));

    /* lookup results is ignored */
    parents = 0;
    return duk__get_identifier_reference(thr, env, name, NULL, parents, &ref);
}
#endif

/*
 *  GETVAR
 *
 *  See E5 Sections:
 *    11.1.2 Identifier Reference
 *    10.3.1 Identifier Resolution
 *    11.13.1 Simple Assignment  [example of where the Reference is GetValue'd]
 *    8.7.1 GetValue (V)
 *    8.12.1 [[GetOwnProperty]] (P)
 *    8.12.2 [[GetProperty]] (P)
 *    8.12.3 [[Get]] (P)
 *
 *  If 'throw' is true, always leaves two values on top of stack: [val this].
 *
 *  If 'throw' is false, returns 0 if identifier cannot be resolved, and the
 *  stack will be unaffected in this case.  If identifier is resolved, returns
 *  1 and leaves [val this] on top of stack.
 *
 *  Note: the 'strict' flag of a reference returned by GetIdentifierReference
 *  is ignored by GetValue.  Hence we don't take a 'strict' parameter.
 *
 *  The 'throw' flag is needed for implementing 'typeof' for an unreferenced
 *  identifier.  An unreference identifier in other contexts generates a
 *  ReferenceError.
 */

DUK_LOCAL
duk_bool_t duk__getvar_helper(duk_hthread *thr,
                              duk_hobject *env,
                              duk_activation *act,
                              duk_hstring *name,
                              duk_bool_t throw_flag) {
    duk__id_lookup_result ref;
    duk_tval tv_tmp_obj;
    duk_tval tv_tmp_key;
    duk_bool_t parents;

    DUK_DDD(DUK_DDDPRINT("getvar: thr=%p, env=%p, act=%p, name=%!O "
                         "(env -> %!dO)",
                         (void *) thr, (void *) env, (void *) act,
                         (duk_heaphdr *) name, (duk_heaphdr *) env));

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(name != NULL);
    /* env and act may be NULL */

    DUK_STATS_INC(thr->heap, stats_getvar_all);

        DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(env);
        DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(name);

    parents = 1;     /* follow parent chain */
    if (duk__get_identifier_reference(thr, env, name, act, parents, &ref)) {
        if (ref.value) {
            duk_push_tval(thr, ref.value);
            duk_push_undefined(thr);
        } else {
            DUK_ASSERT(ref.holder != NULL);

            /* ref.holder is safe across the getprop call (even
             * with side effects) because 'env' is reachable and
             * ref.holder is a direct heap pointer.
             */

            DUK_TVAL_SET_OBJECT(&tv_tmp_obj, ref.holder);
            DUK_TVAL_SET_STRING(&tv_tmp_key, name);
            (void) duk_hobject_getprop(thr, &tv_tmp_obj, &tv_tmp_key);  /* [value] */

            if (ref.has_this) {
                duk_push_hobject(thr, ref.holder);
            } else {
                duk_push_undefined(thr);
            }

            /* [value this] */
        }

        return 1;
    } else {
        if (throw_flag) {
            DUK_ERROR_FMT1(thr, DUK_ERR_REFERENCE_ERROR,
                           "identifier '%s' undefined",
                           (const char *) DUK_HSTRING_GET_DATA(name));
            DUK_WO_NORETURN(return 0;);
        }

        return 0;
    }
}

DUK_INTERNAL
duk_bool_t duk_js_getvar_envrec(duk_hthread *thr,
                                duk_hobject *env,
                                duk_hstring *name,
                                duk_bool_t throw_flag) {
    return duk__getvar_helper(thr, env, NULL, name, throw_flag);
}

DUK_INTERNAL
duk_bool_t duk_js_getvar_activation(duk_hthread *thr,
                                    duk_activation *act,
                                    duk_hstring *name,
                                    duk_bool_t throw_flag) {
    DUK_ASSERT(act != NULL);
    return duk__getvar_helper(thr, act->lex_env, act, name, throw_flag);
}

/*
 *  PUTVAR
 *
 *  See E5 Sections:
 *    11.1.2 Identifier Reference
 *    10.3.1 Identifier Resolution
 *    11.13.1 Simple Assignment  [example of where the Reference is PutValue'd]
 *    8.7.2 PutValue (V,W)  [see especially step 3.b, undefined -> automatic global in non-strict mode]
 *    8.12.4 [[CanPut]] (P)
 *    8.12.5 [[Put]] (P)
 *
 *  Note: may invalidate any valstack (or object) duk_tval pointers because
 *  putting a value may reallocate any object or any valstack.  Caller beware.
 */

DUK_LOCAL
void duk__putvar_helper(duk_hthread *thr,
                        duk_hobject *env,
                        duk_activation *act,
                        duk_hstring *name,
                        duk_tval *val,
                        duk_bool_t strict) {
    duk__id_lookup_result ref;
    duk_tval tv_tmp_val;
    duk_tval tv_tmp_obj;
    duk_tval tv_tmp_key;
    duk_bool_t parents;

    DUK_STATS_INC(thr->heap, stats_putvar_all);

    DUK_DDD(DUK_DDDPRINT("putvar: thr=%p, env=%p, act=%p, name=%!O, val=%p, strict=%ld "
                         "(env -> %!dO, val -> %!T)",
                         (void *) thr, (void *) env, (void *) act,
                         (duk_heaphdr *) name, (void *) val, (long) strict,
                         (duk_heaphdr *) env, (duk_tval *) val));

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(name != NULL);
    DUK_ASSERT(val != NULL);
    /* env and act may be NULL */

    DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(env);
    DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(name);
    DUK_ASSERT_REFCOUNT_NONZERO_TVAL(val);

    DUK_TVAL_SET_TVAL(&tv_tmp_val, val);  /* Stabilize. */
    val = NULL;

    /*
     *  In strict mode E5 protects 'eval' and 'arguments' from being
     *  assigned to (or even declared anywhere).  Attempt to do so
     *  should result in a compile time SyntaxError.  See the internal
     *  design documentation for details.
     *
     *  Thus, we should never come here, run-time, for strict code,
     *  and name 'eval' or 'arguments'.
     */

    DUK_ASSERT(!strict ||
               (name != DUK_HTHREAD_STRING_EVAL(thr) &&
                name != DUK_HTHREAD_STRING_LC_ARGUMENTS(thr)));

    /*
     *  Lookup variable and update in-place if found.
     */

    parents = 1;     /* follow parent chain */

    if (duk__get_identifier_reference(thr, env, name, act, parents, &ref)) {
        if (ref.value && (ref.attrs & DUK_PROPDESC_FLAG_WRITABLE)) {
            /* Update duk_tval in-place if pointer provided and the
             * property is writable.  If the property is not writable
             * (immutable binding), use duk_hobject_putprop() which
             * will respect mutability.
             */
            duk_tval *tv_val;

            tv_val = ref.value;
            DUK_ASSERT(tv_val != NULL);
            DUK_TVAL_SET_TVAL_UPDREF(thr, tv_val, &tv_tmp_val);  /* side effects */

            /* ref.value invalidated here */
        } else {
            DUK_ASSERT(ref.holder != NULL);

            DUK_TVAL_SET_OBJECT(&tv_tmp_obj, ref.holder);
            DUK_TVAL_SET_STRING(&tv_tmp_key, name);
            (void) duk_hobject_putprop(thr, &tv_tmp_obj, &tv_tmp_key, &tv_tmp_val, strict);

            /* ref.value invalidated here */
        }

        return;
    }

    /*
     *  Not found: write to global object (non-strict) or ReferenceError
     *  (strict); see E5 Section 8.7.2, step 3.
     */

    if (strict) {
        DUK_DDD(DUK_DDDPRINT("identifier binding not found, strict => reference error"));
        DUK_ERROR_FMT1(thr, DUK_ERR_REFERENCE_ERROR,
                       "identifier '%s' undefined",
                       (const char *) DUK_HSTRING_GET_DATA(name));
        DUK_WO_NORETURN(return;);
    }

    DUK_DDD(DUK_DDDPRINT("identifier binding not found, not strict => set to global"));

    DUK_TVAL_SET_OBJECT(&tv_tmp_obj, thr->builtins[DUK_BIDX_GLOBAL]);
    DUK_TVAL_SET_STRING(&tv_tmp_key, name);
    (void) duk_hobject_putprop(thr, &tv_tmp_obj, &tv_tmp_key, &tv_tmp_val, 0);  /* 0 = no throw */

    /* NB: 'val' may be invalidated here because put_value may realloc valstack,
     * caller beware.
     */
}

DUK_INTERNAL
void duk_js_putvar_envrec(duk_hthread *thr,
                          duk_hobject *env,
                          duk_hstring *name,
                          duk_tval *val,
                          duk_bool_t strict) {
    duk__putvar_helper(thr, env, NULL, name, val, strict);
}

DUK_INTERNAL
void duk_js_putvar_activation(duk_hthread *thr,
                              duk_activation *act,
                              duk_hstring *name,
                              duk_tval *val,
                              duk_bool_t strict) {
    DUK_ASSERT(act != NULL);
    duk__putvar_helper(thr, act->lex_env, act, name, val, strict);
}

/*
 *  DELVAR
 *
 *  See E5 Sections:
 *    11.4.1 The delete operator
 *    10.2.1.1.5 DeleteBinding (N)  [declarative environment record]
 *    10.2.1.2.5 DeleteBinding (N)  [object environment record]
 *
 *  Variable bindings established inside eval() are deletable (configurable),
 *  other bindings are not, including variables declared in global level.
 *  Registers are always non-deletable, and the deletion of other bindings
 *  is controlled by the configurable flag.
 *
 *  For strict mode code, the 'delete' operator should fail with a compile
 *  time SyntaxError if applied to identifiers.  Hence, no strict mode
 *  run-time deletion of identifiers should ever happen.  This function
 *  should never be called from strict mode code!
 */

DUK_LOCAL
duk_bool_t duk__delvar_helper(duk_hthread *thr,
                              duk_hobject *env,
                              duk_activation *act,
                              duk_hstring *name) {
    duk__id_lookup_result ref;
    duk_bool_t parents;

    DUK_DDD(DUK_DDDPRINT("delvar: thr=%p, env=%p, act=%p, name=%!O "
                         "(env -> %!dO)",
                         (void *) thr, (void *) env, (void *) act,
                         (duk_heaphdr *) name, (duk_heaphdr *) env));

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(name != NULL);
    /* env and act may be NULL */

        DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(name);

    parents = 1;     /* follow parent chain */

    if (duk__get_identifier_reference(thr, env, name, act, parents, &ref)) {
        if (ref.value && !(ref.attrs & DUK_PROPDESC_FLAG_CONFIGURABLE)) {
            /* Identifier found in registers (always non-deletable)
             * or declarative environment record and non-configurable.
             */
            return 0;
        }
        DUK_ASSERT(ref.holder != NULL);

        return duk_hobject_delprop_raw(thr, ref.holder, name, 0);
    }

    /*
     *  Not found (even in global object).
     *
     *  In non-strict mode this is a silent SUCCESS (!), see E5 Section 11.4.1,
     *  step 3.b.  In strict mode this case is a compile time SyntaxError so
     *  we should not come here.
     */

    DUK_DDD(DUK_DDDPRINT("identifier to be deleted not found: name=%!O "
                         "(treated as silent success)",
                         (duk_heaphdr *) name));
    return 1;
}

#if 0  /*unused*/
DUK_INTERNAL
duk_bool_t duk_js_delvar_envrec(duk_hthread *thr,
                                duk_hobject *env,
                                duk_hstring *name) {
    return duk__delvar_helper(thr, env, NULL, name);
}
#endif

DUK_INTERNAL
duk_bool_t duk_js_delvar_activation(duk_hthread *thr,
                                    duk_activation *act,
                                    duk_hstring *name) {
    DUK_ASSERT(act != NULL);
    return duk__delvar_helper(thr, act->lex_env, act, name);
}

/*
 *  DECLVAR
 *
 *  See E5 Sections:
 *    10.4.3 Entering Function Code
 *    10.5 Declaration Binding Instantion
 *    12.2 Variable Statement
 *    11.1.2 Identifier Reference
 *    10.3.1 Identifier Resolution
 *
 *  Variable declaration behavior is mainly discussed in Section 10.5,
 *  and is not discussed in the execution semantics (Sections 11-13).
 *
 *  Conceptually declarations happen when code (global, eval, function)
 *  is entered, before any user code is executed.  In practice, register-
 *  bound identifiers are 'declared' automatically (by virtue of being
 *  allocated to registers with the initial value 'undefined').  Other
 *  identifiers are declared in the function prologue with this primitive.
 *
 *  Since non-register bindings eventually back to an internal object's
 *  properties, the 'prop_flags' argument is used to specify binding
 *  type:
 *
 *    - Immutable binding: set DUK_PROPDESC_FLAG_WRITABLE to false
 *    - Non-deletable binding: set DUK_PROPDESC_FLAG_CONFIGURABLE to false
 *    - The flag DUK_PROPDESC_FLAG_ENUMERABLE should be set, although it
 *      doesn't really matter for internal objects
 *
 *  All bindings are non-deletable mutable bindings except:
 *
 *    - Declarations in eval code (mutable, deletable)
 *    - 'arguments' binding in strict function code (immutable)
 *    - Function name binding of a function expression (immutable)
 *
 *  Declarations may go to declarative environment records (always
 *  so for functions), but may also go to object environment records
 *  (e.g. global code).  The global object environment has special
 *  behavior when re-declaring a function (but not a variable); see
 *  E5.1 specification, Section 10.5, step 5.e.
 *
 *  Declarations always go to the 'top-most' environment record, i.e.
 *  we never check the record chain.  It's not an error even if a
 *  property (even an immutable or non-deletable one) of the same name
 *  already exists.
 *
 *  If a declared variable already exists, its value needs to be updated
 *  (if possible).  Returns 1 if a PUTVAR needs to be done by the caller;
 *  otherwise returns 0.
 */

DUK_LOCAL
duk_bool_t duk__declvar_helper(duk_hthread *thr,
                               duk_hobject *env,
                               duk_hstring *name,
                               duk_tval *val,
                               duk_small_uint_t prop_flags,
                               duk_bool_t is_func_decl) {
    duk_hobject *holder;
    duk_bool_t parents;
    duk__id_lookup_result ref;
    duk_tval *tv;

    DUK_DDD(DUK_DDDPRINT("declvar: thr=%p, env=%p, name=%!O, val=%!T, prop_flags=0x%08lx, is_func_decl=%ld "
                         "(env -> %!iO)",
                         (void *) thr, (void *) env, (duk_heaphdr *) name,
                         (duk_tval *) val, (unsigned long) prop_flags,
                         (unsigned int) is_func_decl, (duk_heaphdr *) env));

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(env != NULL);
    DUK_ASSERT(name != NULL);
    DUK_ASSERT(val != NULL);

    /* Note: in strict mode the compiler should reject explicit
     * declaration of 'eval' or 'arguments'.  However, internal
     * bytecode may declare 'arguments' in the function prologue.
     * We don't bother checking (or asserting) for these now.
     */

    /* Note: val is a stable duk_tval pointer.  The caller makes
     * a value copy into its stack frame, so 'tv_val' is not subject
     * to side effects here.
     */

    /*
     *  Check whether already declared.
     *
     *  We need to check whether the binding exists in the environment
     *  without walking its parents.  However, we still need to check
     *  register-bound identifiers and the prototype chain of an object
     *  environment target object.
     */

    parents = 0;  /* just check 'env' */
    if (duk__get_identifier_reference(thr, env, name, NULL, parents, &ref)) {
        duk_int_t e_idx;
        duk_int_t h_idx;
        duk_small_uint_t flags;

        /*
         *  Variable already declared, ignore re-declaration.
         *  The only exception is the updated behavior of E5.1 for
         *  global function declarations, E5.1 Section 10.5, step 5.e.
         *  This behavior does not apply to global variable declarations.
         */

        if (!(is_func_decl && env == thr->builtins[DUK_BIDX_GLOBAL_ENV])) {
            DUK_DDD(DUK_DDDPRINT("re-declare a binding, ignoring"));
            return 1;  /* 1 -> needs a PUTVAR */
        }

        /*
         *  Special behavior in E5.1.
         *
         *  Note that even though parents == 0, the conflicting property
         *  may be an inherited property (currently our global object's
         *  prototype is Object.prototype).  Step 5.e first operates on
         *  the existing property (which is potentially in an ancestor)
         *  and then defines a new property in the global object (and
         *  never modifies the ancestor).
         *
         *  Also note that this logic would become even more complicated
         *  if the conflicting property might be a virtual one.  Object
         *  prototype has no virtual properties, though.
         *
         *  XXX: this is now very awkward, rework.
         */

        DUK_DDD(DUK_DDDPRINT("re-declare a function binding in global object, "
                             "updated E5.1 processing"));

        DUK_ASSERT(ref.holder != NULL);
        holder = ref.holder;

        /* holder will be set to the target object, not the actual object
         * where the property was found (see duk__get_identifier_reference()).
         */
        DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(holder) == DUK_HOBJECT_CLASS_GLOBAL);
        DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARRAY(holder));  /* global object doesn't have array part */

        /* XXX: use a helper for prototype traversal; no loop check here */
        /* must be found: was found earlier, and cannot be inherited */
        for (;;) {
            DUK_ASSERT(holder != NULL);
            if (duk_hobject_find_entry(thr->heap, holder, name, &e_idx, &h_idx)) {
                DUK_ASSERT(e_idx >= 0);
                break;
            }
            /* SCANBUILD: NULL pointer dereference, doesn't actually trigger,
             * asserted above.
             */
            holder = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, holder);
        }
        DUK_ASSERT(holder != NULL);
        DUK_ASSERT(e_idx >= 0);
        /* SCANBUILD: scan-build produces a NULL pointer dereference warning
         * below; it never actually triggers because holder is actually never
         * NULL.
         */

        /* ref.holder is global object, holder is the object with the
         * conflicting property.
         */

        flags = DUK_HOBJECT_E_GET_FLAGS(thr->heap, holder, e_idx);
        if (!(flags & DUK_PROPDESC_FLAG_CONFIGURABLE)) {
            if (flags & DUK_PROPDESC_FLAG_ACCESSOR) {
                DUK_DDD(DUK_DDDPRINT("existing property is a non-configurable "
                                     "accessor -> reject"));
                goto fail_existing_attributes;
            }
            if (!((flags & DUK_PROPDESC_FLAG_WRITABLE) &&
                  (flags & DUK_PROPDESC_FLAG_ENUMERABLE))) {
                DUK_DDD(DUK_DDDPRINT("existing property is a non-configurable "
                                     "plain property which is not writable and "
                                     "enumerable -> reject"));
                goto fail_existing_attributes;
            }

            DUK_DDD(DUK_DDDPRINT("existing property is not configurable but "
                                 "is plain, enumerable, and writable -> "
                                 "allow redeclaration"));
        }

        if (holder == ref.holder) {
            /* XXX: if duk_hobject_define_property_internal() was updated
             * to handle a pre-existing accessor property, this would be
             * a simple call (like for the ancestor case).
             */
            DUK_DDD(DUK_DDDPRINT("redefine, offending property in global object itself"));

            if (flags & DUK_PROPDESC_FLAG_ACCESSOR) {
                duk_hobject *tmp;

                tmp = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, holder, e_idx);
                DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, holder, e_idx, NULL);
                DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp);
                DUK_UNREF(tmp);
                tmp = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, holder, e_idx);
                DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, holder, e_idx, NULL);
                DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp);
                DUK_UNREF(tmp);
            } else {
                tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, holder, e_idx);
                DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv);
            }

            /* Here val would be potentially invalid if we didn't make
             * a value copy at the caller.
             */

            tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, holder, e_idx);
            DUK_TVAL_SET_TVAL(tv, val);
            DUK_TVAL_INCREF(thr, tv);
            DUK_HOBJECT_E_SET_FLAGS(thr->heap, holder, e_idx, prop_flags);

            DUK_DDD(DUK_DDDPRINT("updated global binding, final result: "
                                 "value -> %!T, prop_flags=0x%08lx",
                                 (duk_tval *) DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, holder, e_idx),
                                 (unsigned long) prop_flags));
        } else {
            DUK_DDD(DUK_DDDPRINT("redefine, offending property in ancestor"));

            DUK_ASSERT(ref.holder == thr->builtins[DUK_BIDX_GLOBAL]);
            duk_push_tval(thr, val);
            duk_hobject_define_property_internal(thr, ref.holder, name, prop_flags);
        }

        return 0;
    }

    /*
     *  Not found (in registers or record objects).  Declare
     *  to current variable environment.
     */

    /*
     *  Get holder object
     */

    if (DUK_HOBJECT_IS_DECENV(env)) {
        DUK_HDECENV_ASSERT_VALID((duk_hdecenv *) env);
        holder = env;
    } else {
        DUK_HOBJENV_ASSERT_VALID((duk_hobjenv *) env);
        holder = ((duk_hobjenv *) env)->target;
        DUK_ASSERT(holder != NULL);
    }

    /*
     *  Define new property
     *
     *  Note: this may fail if the holder is not extensible.
     */

    /* XXX: this is awkward as we use an internal method which doesn't handle
     * extensibility etc correctly.  Basically we'd want to do a [[DefineOwnProperty]]
     * or Object.defineProperty() here.
     */

    if (!DUK_HOBJECT_HAS_EXTENSIBLE(holder)) {
        goto fail_not_extensible;
    }

    duk_push_hobject(thr, holder);
    duk_push_hstring(thr, name);
    duk_push_tval(thr, val);
    duk_xdef_prop(thr, -3, prop_flags);  /* [holder name val] -> [holder] */
    duk_pop_unsafe(thr);

    return 0;

 fail_existing_attributes:
 fail_not_extensible:
    DUK_ERROR_TYPE(thr, "declaration failed");
    DUK_WO_NORETURN(return 0;);
}

DUK_INTERNAL
duk_bool_t duk_js_declvar_activation(duk_hthread *thr,
                                     duk_activation *act,
                                     duk_hstring *name,
                                     duk_tval *val,
                                     duk_small_uint_t prop_flags,
                                     duk_bool_t is_func_decl) {
    duk_hobject *env;
    duk_tval tv_val_copy;

    DUK_ASSERT(act != NULL);

    /*
     *  Make a value copy of the input val.  This ensures that
     *  side effects cannot invalidate the pointer.
     */

    DUK_TVAL_SET_TVAL(&tv_val_copy, val);
    val = &tv_val_copy;

    /*
     *  Delayed env creation check
     */

    if (!act->var_env) {
        DUK_ASSERT(act->lex_env == NULL);
        duk_js_init_activation_environment_records_delayed(thr, act);
        /* 'act' is a stable pointer, so still OK. */
    }
    DUK_ASSERT(act->lex_env != NULL);
    DUK_ASSERT(act->var_env != NULL);

    env = act->var_env;
    DUK_ASSERT(env != NULL);
    DUK_ASSERT(DUK_HOBJECT_IS_ENV(env));

    return duk__declvar_helper(thr, env, name, val, prop_flags, is_func_decl);
}
//#line 1 "duk_lexer.c"
/*
 *  Lexer for source files, ToNumber() string conversions, RegExp expressions,
 *  and JSON.
 *
 *  Provides a stream of ECMAScript tokens from an UTF-8/CESU-8 buffer.  The
 *  caller can also rewind the token stream into a certain position which is
 *  needed by the compiler part for multi-pass scanning.  Tokens are
 *  represented as duk_token structures, and contain line number information.
 *  Token types are identified with DUK_TOK_* defines.
 *
 *  Characters are decoded into a fixed size lookup window consisting of
 *  decoded Unicode code points, with window positions past the end of the
 *  input filled with an invalid codepoint (-1).  The tokenizer can thus
 *  perform multiple character lookups efficiently and with few sanity
 *  checks (such as access outside the end of the input), which keeps the
 *  tokenization code small at the cost of performance.
 *
 *  Character data in tokens, such as identifier names and string literals,
 *  is encoded into CESU-8 format on-the-fly while parsing the token in
 *  question.  The string data is made reachable to garbage collection by
 *  placing the token-related values in value stack entries allocated for
 *  this purpose by the caller.  The characters exist in Unicode code point
 *  form only in the fixed size lookup window, which keeps character data
 *  expansion (of especially ASCII data) low.
 *
 *  Token parsing supports the full range of Unicode characters as described
 *  in the E5 specification.  Parsing has been optimized for ASCII characters
 *  because ordinary ECMAScript code consists almost entirely of ASCII
 *  characters.  Matching of complex Unicode codepoint sets (such as in the
 *  IdentifierStart and IdentifierPart productions) is optimized for size,
 *  and is done using a linear scan of a bit-packed list of ranges.  This is
 *  very slow, but should never be entered unless the source code actually
 *  contains Unicode characters.
 *
 *  ECMAScript tokenization is partially context sensitive.  First,
 *  additional future reserved words are recognized in strict mode (see E5
 *  Section 7.6.1.2).  Second, a forward slash character ('/') can be
 *  recognized either as starting a RegExp literal or as a division operator,
 *  depending on context.  The caller must provide necessary context flags
 *  when requesting a new token.
 *
 *  Future work:
 *
 *    * Make line number tracking optional, as it consumes space.
 *
 *    * Add a feature flag for disabling UTF-8 decoding of input, as most
 *      source code is ASCII.  Because of Unicode escapes written in ASCII,
 *      this does not allow Unicode support to be removed from e.g.
 *      duk_unicode_is_identifier_start() nor does it allow removal of CESU-8
 *      encoding of e.g. string literals.
 *
 *    * Add a feature flag for disabling Unicode compliance of e.g. identifier
 *      names.  This allows for a build more than a kilobyte smaller, because
 *      Unicode ranges needed by duk_unicode_is_identifier_start() and
 *      duk_unicode_is_identifier_part() can be dropped.  String literals
 *      should still be allowed to contain escaped Unicode, so this still does
 *      not allow removal of CESU-8 encoding of e.g. string literals.
 *
 *    * Character lookup tables for codepoints above BMP could be stripped.
 *
 *    * Strictly speaking, E5 specification requires that source code consists
 *      of 16-bit code units, and if not, must be conceptually converted to
 *      that format first.  The current lexer processes Unicode code points
 *      and allows characters outside the BMP.  These should be converted to
 *      surrogate pairs while reading the source characters into the window,
 *      not after tokens have been formed (as is done now).  However, the fix
 *      is not trivial because two characters are decoded from one codepoint.
 *
 *    * Optimize for speed as well as size.  Large if-else ladders are (at
 *      least potentially) slow.
 */

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

/*
 *  Various defines and file specific helper macros
 */

#define DUK__MAX_RE_DECESC_DIGITS     9
#define DUK__MAX_RE_QUANT_DIGITS      9   /* Does not allow e.g. 2**31-1, but one more would allow overflows of u32. */

/* whether to use macros or helper function depends on call count */
#define DUK__ISDIGIT(x)          ((x) >= DUK_ASC_0 && (x) <= DUK_ASC_9)
#define DUK__ISHEXDIGIT(x)       duk__is_hex_digit((x))
#define DUK__ISOCTDIGIT(x)       ((x) >= DUK_ASC_0 && (x) <= DUK_ASC_7)
#define DUK__ISDIGIT03(x)        ((x) >= DUK_ASC_0 && (x) <= DUK_ASC_3)
#define DUK__ISDIGIT47(x)        ((x) >= DUK_ASC_4 && (x) <= DUK_ASC_7)

/* lexer character window helpers */
#define DUK__LOOKUP(lex_ctx,idx)            ((lex_ctx)->window[(idx)].codepoint)
#define DUK__ADVANCECHARS(lex_ctx,count)    duk__advance_chars((lex_ctx), (count))
#define DUK__ADVANCEBYTES(lex_ctx,count)    duk__advance_bytes((lex_ctx), (count))
#define DUK__INITBUFFER(lex_ctx)            duk__initbuffer((lex_ctx))
#define DUK__APPENDBUFFER(lex_ctx,x)        duk__appendbuffer((lex_ctx), (duk_codepoint_t) (x))
#define DUK__APPENDBUFFER_ASCII(lex_ctx,x)  duk__appendbuffer_ascii((lex_ctx), (duk_codepoint_t) (x))

/* lookup shorthands (note: assume context variable is named 'lex_ctx') */
#define DUK__L0()  DUK__LOOKUP(lex_ctx, 0)
#define DUK__L1()  DUK__LOOKUP(lex_ctx, 1)
#define DUK__L2()  DUK__LOOKUP(lex_ctx, 2)
#define DUK__L3()  DUK__LOOKUP(lex_ctx, 3)
#define DUK__L4()  DUK__LOOKUP(lex_ctx, 4)
#define DUK__L5()  DUK__LOOKUP(lex_ctx, 5)

/* packed advance/token number macro used by multiple functions */
#define DUK__ADVTOK(advbytes,tok)  ((((advbytes) * sizeof(duk_lexer_codepoint)) << 8) + (tok))

/*
 *  Advance lookup window by N characters, filling in new characters as
 *  necessary.  After returning caller is guaranteed a character window of
 *  at least DUK_LEXER_WINDOW_SIZE characters.
 *
 *  The main function duk__advance_bytes() is called at least once per every
 *  token so it has a major lexer/compiler performance impact.  There are two
 *  variants for the main duk__advance_bytes() algorithm: a sliding window
 *  approach which is slightly faster at the cost of larger code footprint,
 *  and a simple copying one.
 *
 *  Decoding directly from the source string would be another lexing option.
 *  But the lookup window based approach has the advantage of hiding the
 *  source string and its encoding effectively which gives more flexibility
 *  going forward to e.g. support chunked streaming of source from flash.
 *
 *  Decodes UTF-8/CESU-8 leniently with support for code points from U+0000 to
 *  U+10FFFF, causing an error if the input is unparseable.  Leniency means:
 *
 *    * Unicode code point validation is intentionally not performed,
 *      except to check that the codepoint does not exceed 0x10ffff.
 *
 *    * In particular, surrogate pairs are allowed and not combined, which
 *      allows source files to represent all SourceCharacters with CESU-8.
 *      Broken surrogate pairs are allowed, as ECMAScript does not mandate
 *      their validation.
 *
 *    * Allow non-shortest UTF-8 encodings.
 *
 *  Leniency here causes few security concerns because all character data is
 *  decoded into Unicode codepoints before lexer processing, and is then
 *  re-encoded into CESU-8.  The source can be parsed as strict UTF-8 with
 *  a compiler option.  However, ECMAScript source characters include -all-
 *  16-bit unsigned integer codepoints, so leniency seems to be appropriate.
 *
 *  Note that codepoints above the BMP are not strictly SourceCharacters,
 *  but the lexer still accepts them as such.  Before ending up in a string
 *  or an identifier name, codepoints above BMP are converted into surrogate
 *  pairs and then CESU-8 encoded, resulting in 16-bit Unicode data as
 *  expected by ECMAScript.
 *
 *  An alternative approach to dealing with invalid or partial sequences
 *  would be to skip them and replace them with e.g. the Unicode replacement
 *  character U+FFFD.  This has limited utility because a replacement character
 *  will most likely cause a parse error, unless it occurs inside a string.
 *  Further, ECMAScript source is typically pure ASCII.
 *
 *  See:
 *
 *     http://en.wikipedia.org/wiki/UTF-8
 *     http://en.wikipedia.org/wiki/CESU-8
 *     http://tools.ietf.org/html/rfc3629
 *     http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences
 *
 *  Future work:
 *
 *    * Reject other invalid Unicode sequences (see Wikipedia entry for examples)
 *      in strict UTF-8 mode.
 *
 *    * Size optimize.  An attempt to use a 16-byte lookup table for the first
 *      byte resulted in a code increase though.
 *
 *    * Is checking against maximum 0x10ffff really useful?  4-byte encoding
 *      imposes a certain limit anyway.
 *
 *    * Support chunked streaming of source code.  Can be implemented either
 *      by streaming chunks of bytes or chunks of codepoints.
 */

#if defined(DUK_USE_LEXER_SLIDING_WINDOW)
DUK_LOCAL void duk__fill_lexer_buffer(duk_lexer_ctx *lex_ctx, duk_small_uint_t start_offset_bytes) {
    duk_lexer_codepoint *cp, *cp_end;
    duk_ucodepoint_t x;
    duk_small_uint_t contlen;
    const duk_uint8_t *p, *p_end;
#if defined(DUK_USE_STRICT_UTF8_SOURCE)
    duk_ucodepoint_t mincp;
#endif
    duk_int_t input_line;

    /* Use temporaries and update lex_ctx only when finished. */
    input_line = lex_ctx->input_line;
    p = lex_ctx->input + lex_ctx->input_offset;
    p_end = lex_ctx->input + lex_ctx->input_length;

    cp = (duk_lexer_codepoint *) (void *) ((duk_uint8_t *) lex_ctx->buffer + start_offset_bytes);
    cp_end = lex_ctx->buffer + DUK_LEXER_BUFFER_SIZE;

    for (; cp != cp_end; cp++) {
        cp->offset = (duk_size_t) (p - lex_ctx->input);
        cp->line = input_line;

        /* XXX: potential issue with signed pointers, p_end < p. */
        if (DUK_UNLIKELY(p >= p_end)) {
            /* If input_offset were assigned a negative value, it would
             * result in a large positive value.  Most likely it would be
             * larger than input_length and be caught here.  In any case
             * no memory unsafe behavior would happen.
             */
            cp->codepoint = -1;
            continue;
        }

        x = (duk_ucodepoint_t) (*p++);

        /* Fast path. */

        if (DUK_LIKELY(x < 0x80UL)) {
            DUK_ASSERT(x != 0x2028UL && x != 0x2029UL);  /* not LS/PS */
            if (DUK_UNLIKELY(x <= 0x000dUL)) {
                if ((x == 0x000aUL) ||
                    ((x == 0x000dUL) && (p >= p_end || *p != 0x000aUL))) {
                    /* lookup for 0x000a above assumes shortest encoding now */

                    /* E5 Section 7.3, treat the following as newlines:
                     *   LF
                     *   CR [not followed by LF]
                     *   LS
                     *   PS
                     *
                     * For CR LF, CR is ignored if it is followed by LF, and the LF will bump
                     * the line number.
                     */
                    input_line++;
                }
            }

            cp->codepoint = (duk_codepoint_t) x;
            continue;
        }

        /* Slow path. */

        if (x < 0xc0UL) {
            /* 10xx xxxx -> invalid */
            goto error_encoding;
        } else if (x < 0xe0UL) {
            /* 110x xxxx   10xx xxxx  */
            contlen = 1;
#if defined(DUK_USE_STRICT_UTF8_SOURCE)
            mincp = 0x80UL;
#endif
            x = x & 0x1fUL;
        } else if (x < 0xf0UL) {
            /* 1110 xxxx   10xx xxxx   10xx xxxx */
            contlen = 2;
#if defined(DUK_USE_STRICT_UTF8_SOURCE)
            mincp = 0x800UL;
#endif
            x = x & 0x0fUL;
        } else if (x < 0xf8UL) {
            /* 1111 0xxx   10xx xxxx   10xx xxxx   10xx xxxx */
            contlen = 3;
#if defined(DUK_USE_STRICT_UTF8_SOURCE)
            mincp = 0x10000UL;
#endif
            x = x & 0x07UL;
        } else {
            /* no point in supporting encodings of 5 or more bytes */
            goto error_encoding;
        }

        DUK_ASSERT(p_end >= p);
        if ((duk_size_t) contlen > (duk_size_t) (p_end - p)) {
            goto error_clipped;
        }

        while (contlen > 0) {
            duk_small_uint_t y;
            y = *p++;
            if ((y & 0xc0U) != 0x80U) {
                /* check that byte has the form 10xx xxxx */
                goto error_encoding;
            }
            x = x << 6;
            x += y & 0x3fUL;
            contlen--;
        }

        /* check final character validity */

        if (x > 0x10ffffUL) {
            goto error_encoding;
        }
#if defined(DUK_USE_STRICT_UTF8_SOURCE)
        if (x < mincp || (x >= 0xd800UL && x <= 0xdfffUL) || x == 0xfffeUL) {
            goto error_encoding;
        }
#endif

        DUK_ASSERT(x != 0x000aUL && x != 0x000dUL);
        if ((x == 0x2028UL) || (x == 0x2029UL)) {
            input_line++;
        }

        cp->codepoint = (duk_codepoint_t) x;
    }

    lex_ctx->input_offset = (duk_size_t) (p - lex_ctx->input);
    lex_ctx->input_line = input_line;
    return;

 error_clipped:   /* clipped codepoint */
 error_encoding:  /* invalid codepoint encoding or codepoint */
    lex_ctx->input_offset = (duk_size_t) (p - lex_ctx->input);
    lex_ctx->input_line = input_line;

    DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_SOURCE_DECODE_FAILED);
    DUK_WO_NORETURN(return;);
}

DUK_LOCAL void duk__advance_bytes(duk_lexer_ctx *lex_ctx, duk_small_uint_t count_bytes) {
    duk_small_uint_t used_bytes, avail_bytes;

    DUK_ASSERT_DISABLE(count_bytes >= 0);  /* unsigned */
    DUK_ASSERT(count_bytes <= (duk_small_uint_t) (DUK_LEXER_WINDOW_SIZE * sizeof(duk_lexer_codepoint)));
    DUK_ASSERT(lex_ctx->window >= lex_ctx->buffer);
    DUK_ASSERT(lex_ctx->window < lex_ctx->buffer + DUK_LEXER_BUFFER_SIZE);
    DUK_ASSERT((duk_uint8_t *) lex_ctx->window + count_bytes <= (duk_uint8_t *) lex_ctx->buffer + DUK_LEXER_BUFFER_SIZE * sizeof(duk_lexer_codepoint));

    /* Zero 'count' is also allowed to make call sites easier.
     * Arithmetic in bytes generates better code in GCC.
     */

    lex_ctx->window = (duk_lexer_codepoint *) (void *) ((duk_uint8_t *) lex_ctx->window + count_bytes);  /* avoid multiply */
    used_bytes = (duk_small_uint_t) ((duk_uint8_t *) lex_ctx->window - (duk_uint8_t *) lex_ctx->buffer);
    avail_bytes = DUK_LEXER_BUFFER_SIZE * sizeof(duk_lexer_codepoint) - used_bytes;
    if (avail_bytes < (duk_small_uint_t) (DUK_LEXER_WINDOW_SIZE * sizeof(duk_lexer_codepoint))) {
        /* Not enough data to provide a full window, so "scroll" window to
         * start of buffer and fill up the rest.
         */
        duk_memmove((void *) lex_ctx->buffer,
                    (const void *) lex_ctx->window,
                    (size_t) avail_bytes);
        lex_ctx->window = lex_ctx->buffer;
        duk__fill_lexer_buffer(lex_ctx, avail_bytes);
    }
}

DUK_LOCAL void duk__init_lexer_window(duk_lexer_ctx *lex_ctx) {
    lex_ctx->window = lex_ctx->buffer;
    duk__fill_lexer_buffer(lex_ctx, 0);
}
#else  /* DUK_USE_LEXER_SLIDING_WINDOW */
DUK_LOCAL duk_codepoint_t duk__read_char(duk_lexer_ctx *lex_ctx) {
    duk_ucodepoint_t x;
    duk_small_uint_t len;
    duk_small_uint_t i;
    const duk_uint8_t *p;
#if defined(DUK_USE_STRICT_UTF8_SOURCE)
    duk_ucodepoint_t mincp;
#endif
    duk_size_t input_offset;

    input_offset = lex_ctx->input_offset;
    if (DUK_UNLIKELY(input_offset >= lex_ctx->input_length)) {
        /* If input_offset were assigned a negative value, it would
         * result in a large positive value.  Most likely it would be
         * larger than input_length and be caught here.  In any case
         * no memory unsafe behavior would happen.
         */
        return -1;
    }

    p = lex_ctx->input + input_offset;
    x = (duk_ucodepoint_t) (*p);

    if (DUK_LIKELY(x < 0x80UL)) {
        /* 0xxx xxxx -> fast path */

        /* input offset tracking */
        lex_ctx->input_offset++;

        DUK_ASSERT(x != 0x2028UL && x != 0x2029UL);  /* not LS/PS */
        if (DUK_UNLIKELY(x <= 0x000dUL)) {
            if ((x == 0x000aUL) ||
                ((x == 0x000dUL) && (lex_ctx->input_offset >= lex_ctx->input_length ||
                                     lex_ctx->input[lex_ctx->input_offset] != 0x000aUL))) {
                /* lookup for 0x000a above assumes shortest encoding now */

                /* E5 Section 7.3, treat the following as newlines:
                 *   LF
                 *   CR [not followed by LF]
                 *   LS
                 *   PS
                 *
                 * For CR LF, CR is ignored if it is followed by LF, and the LF will bump
                 * the line number.
                 */
                lex_ctx->input_line++;
            }
        }

        return (duk_codepoint_t) x;
    }

    /* Slow path. */

    if (x < 0xc0UL) {
        /* 10xx xxxx -> invalid */
        goto error_encoding;
    } else if (x < 0xe0UL) {
        /* 110x xxxx   10xx xxxx  */
        len = 2;
#if defined(DUK_USE_STRICT_UTF8_SOURCE)
        mincp = 0x80UL;
#endif
        x = x & 0x1fUL;
    } else if (x < 0xf0UL) {
        /* 1110 xxxx   10xx xxxx   10xx xxxx */
        len = 3;
#if defined(DUK_USE_STRICT_UTF8_SOURCE)
        mincp = 0x800UL;
#endif
        x = x & 0x0fUL;
    } else if (x < 0xf8UL) {
        /* 1111 0xxx   10xx xxxx   10xx xxxx   10xx xxxx */
        len = 4;
#if defined(DUK_USE_STRICT_UTF8_SOURCE)
        mincp = 0x10000UL;
#endif
        x = x & 0x07UL;
    } else {
        /* no point in supporting encodings of 5 or more bytes */
        goto error_encoding;
    }

    DUK_ASSERT(lex_ctx->input_length >= lex_ctx->input_offset);
    if ((duk_size_t) len > (duk_size_t) (lex_ctx->input_length - lex_ctx->input_offset)) {
        goto error_clipped;
    }

    p++;
    for (i = 1; i < len; i++) {
        duk_small_uint_t y;
        y = *p++;
        if ((y & 0xc0U) != 0x80U) {
            /* check that byte has the form 10xx xxxx */
            goto error_encoding;
        }
        x = x << 6;
        x += y & 0x3fUL;
    }

    /* check final character validity */

    if (x > 0x10ffffUL) {
        goto error_encoding;
    }
#if defined(DUK_USE_STRICT_UTF8_SOURCE)
    if (x < mincp || (x >= 0xd800UL && x <= 0xdfffUL) || x == 0xfffeUL) {
        goto error_encoding;
    }
#endif

    /* input offset tracking */
    lex_ctx->input_offset += len;

    /* line tracking */
    DUK_ASSERT(x != 0x000aUL && x != 0x000dUL);
    if ((x == 0x2028UL) || (x == 0x2029UL)) {
        lex_ctx->input_line++;
    }

    return (duk_codepoint_t) x;

 error_clipped:   /* clipped codepoint */
 error_encoding:  /* invalid codepoint encoding or codepoint */
    DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_SOURCE_DECODE_FAILED);
    DUK_WO_NORETURN(return 0;);
}

DUK_LOCAL void duk__advance_bytes(duk_lexer_ctx *lex_ctx, duk_small_uint_t count_bytes) {
    duk_small_uint_t keep_bytes;
    duk_lexer_codepoint *cp, *cp_end;

    DUK_ASSERT_DISABLE(count_bytes >= 0);  /* unsigned */
    DUK_ASSERT(count_bytes <= (duk_small_uint_t) (DUK_LEXER_WINDOW_SIZE * sizeof(duk_lexer_codepoint)));

    /* Zero 'count' is also allowed to make call sites easier. */

    keep_bytes = DUK_LEXER_WINDOW_SIZE * sizeof(duk_lexer_codepoint) - count_bytes;
    duk_memmove((void *) lex_ctx->window,
                (const void *) ((duk_uint8_t *) lex_ctx->window + count_bytes),
                (size_t) keep_bytes);

    cp = (duk_lexer_codepoint *) ((duk_uint8_t *) lex_ctx->window + keep_bytes);
    cp_end = lex_ctx->window + DUK_LEXER_WINDOW_SIZE;
    for (; cp != cp_end; cp++) {
        cp->offset = lex_ctx->input_offset;
        cp->line = lex_ctx->input_line;
        cp->codepoint = duk__read_char(lex_ctx);
    }
}

DUK_LOCAL void duk__init_lexer_window(duk_lexer_ctx *lex_ctx) {
    /* Call with count == DUK_LEXER_WINDOW_SIZE to fill buffer initially. */
    duk__advance_bytes(lex_ctx, DUK_LEXER_WINDOW_SIZE * sizeof(duk_lexer_codepoint));  /* fill window */
}
#endif  /* DUK_USE_LEXER_SLIDING_WINDOW */

DUK_LOCAL void duk__advance_chars(duk_lexer_ctx *lex_ctx, duk_small_uint_t count_chars) {
    duk__advance_bytes(lex_ctx, count_chars * sizeof(duk_lexer_codepoint));
}

/*
 *  (Re)initialize the temporary byte buffer.  May be called extra times
 *  with little impact.
 */

DUK_LOCAL void duk__initbuffer(duk_lexer_ctx *lex_ctx) {
    /* Reuse buffer as is unless buffer has grown large. */
    if (DUK_HBUFFER_DYNAMIC_GET_SIZE(lex_ctx->buf) < DUK_LEXER_TEMP_BUF_LIMIT) {
        /* Keep current size */
    } else {
        duk_hbuffer_resize(lex_ctx->thr, lex_ctx->buf, DUK_LEXER_TEMP_BUF_LIMIT);
    }

    DUK_BW_INIT_WITHBUF(lex_ctx->thr, &lex_ctx->bw, lex_ctx->buf);
}

/*
 *  Append a Unicode codepoint to the temporary byte buffer.  Performs
 *  CESU-8 surrogate pair encoding for codepoints above the BMP.
 *  Existing surrogate pairs are allowed and also encoded into CESU-8.
 */

DUK_LOCAL void duk__appendbuffer(duk_lexer_ctx *lex_ctx, duk_codepoint_t x) {
    /*
     *  Since character data is only generated by decoding the source or by
     *  the compiler itself, we rely on the input codepoints being correct
     *  and avoid a check here.
     *
     *  Character data can also come here through decoding of Unicode
     *  escapes ("\udead\ubeef") so all 16-but unsigned values can be
     *  present, even when the source file itself is strict UTF-8.
     */
    DUK_ASSERT(x >= 0 && x <= 0x10ffffL);

    DUK_BW_WRITE_ENSURE_CESU8(lex_ctx->thr, &lex_ctx->bw, (duk_ucodepoint_t) x);
}

DUK_LOCAL void duk__appendbuffer_ascii(duk_lexer_ctx *lex_ctx, duk_codepoint_t x) {
    /* ASCII characters can be emitted as a single byte without encoding
     * which matters for some fast paths.
     */
    DUK_ASSERT(x >= 0 && x <= 0x7f);

    DUK_BW_WRITE_ENSURE_U8(lex_ctx->thr, &lex_ctx->bw, (duk_uint8_t) x);
}

/*
 *  Intern the temporary byte buffer into a valstack slot
 *  (in practice, slot1 or slot2).
 */

DUK_LOCAL duk_hstring *duk__internbuffer(duk_lexer_ctx *lex_ctx, duk_idx_t valstack_idx) {
    DUK_ASSERT(valstack_idx == lex_ctx->slot1_idx || valstack_idx == lex_ctx->slot2_idx);

    DUK_BW_PUSH_AS_STRING(lex_ctx->thr, &lex_ctx->bw);
    duk_replace(lex_ctx->thr, valstack_idx);
    return duk_known_hstring(lex_ctx->thr, valstack_idx);
}

/*
 *  Init lexer context
 */

DUK_INTERNAL void duk_lexer_initctx(duk_lexer_ctx *lex_ctx) {
    DUK_ASSERT(lex_ctx != NULL);

    duk_memzero(lex_ctx, sizeof(*lex_ctx));
#if defined(DUK_USE_EXPLICIT_NULL_INIT)
#if defined(DUK_USE_LEXER_SLIDING_WINDOW)
    lex_ctx->window = NULL;
#endif
    lex_ctx->thr = NULL;
    lex_ctx->input = NULL;
    lex_ctx->buf = NULL;
#endif
}

/*
 *  Set lexer input position and reinitialize lookup window.
 */

DUK_INTERNAL void duk_lexer_getpoint(duk_lexer_ctx *lex_ctx, duk_lexer_point *pt) {
    pt->offset = lex_ctx->window[0].offset;
    pt->line = lex_ctx->window[0].line;
}

DUK_INTERNAL void duk_lexer_setpoint(duk_lexer_ctx *lex_ctx, duk_lexer_point *pt) {
    DUK_ASSERT_DISABLE(pt->offset >= 0);  /* unsigned */
    DUK_ASSERT(pt->line >= 1);
    lex_ctx->input_offset = pt->offset;
    lex_ctx->input_line = pt->line;
    duk__init_lexer_window(lex_ctx);
}

/*
 *  Lexing helpers
 */

/* Numeric value of a hex digit (also covers octal and decimal digits) or
 * -1 if not a valid hex digit.
 */
DUK_LOCAL duk_codepoint_t duk__hexval_validate(duk_codepoint_t x) {
    duk_small_int_t t;

    /* Here 'x' is a Unicode codepoint */
    if (DUK_LIKELY(x >= 0 && x <= 0xff)) {
        t = duk_hex_dectab[x];
        if (DUK_LIKELY(t >= 0)) {
            return t;
        }
    }

    return -1;
}

/* Just a wrapper for call sites where 'x' is known to be valid so
 * we assert for it before decoding.
 */
DUK_LOCAL duk_codepoint_t duk__hexval(duk_codepoint_t x) {
    duk_codepoint_t ret;

    DUK_ASSERT((x >= DUK_ASC_0 && x <= DUK_ASC_9) ||
               (x >= DUK_ASC_LC_A && x <= DUK_ASC_LC_F) ||
               (x >= DUK_ASC_UC_A && x <= DUK_ASC_UC_F));
    ret = duk__hexval_validate(x);
    DUK_ASSERT(ret >= 0 && ret <= 15);
    return ret;
}

/* having this as a separate function provided a size benefit */
DUK_LOCAL duk_bool_t duk__is_hex_digit(duk_codepoint_t x) {
    if (DUK_LIKELY(x >= 0 && x <= 0xff)) {
        return (duk_hex_dectab[x] >= 0);
    }
    return 0;
}

/* Parse a Unicode escape of the form \xHH, \uHHHH, or \u{H+}.  Shared by
 * source and RegExp parsing.
 */
DUK_LOCAL duk_codepoint_t duk__lexer_parse_escape(duk_lexer_ctx *lex_ctx, duk_bool_t allow_es6) {
    duk_small_int_t digits;  /* Initial value 2 or 4 for fixed length escapes, 0 for ES2015 \u{H+}. */
    duk_codepoint_t escval;
    duk_codepoint_t x;
    duk_small_uint_t adv;

    DUK_ASSERT(DUK__L0() == DUK_ASC_BACKSLASH);  /* caller responsibilities */
    DUK_ASSERT(DUK__L1() == DUK_ASC_LC_X || DUK__L1() == DUK_ASC_LC_U);
    DUK_UNREF(allow_es6);

    adv = 2;
    digits = 2;
    if (DUK__L1() == DUK_ASC_LC_U) {
        digits = 4;
#if defined(DUK_USE_ES6_UNICODE_ESCAPE)
        if (DUK__L2() == DUK_ASC_LCURLY && allow_es6) {
            digits = 0;
            adv = 3;
        }
#endif
    }
    DUK__ADVANCECHARS(lex_ctx, adv);

    escval = 0;
    for (;;) {
        /* One of the escape forms: \xHH, \uHHHH, \u{H+}.
         * The 'digits' variable tracks parsing state and is
         * initialized to:
         *
         *   \xHH     2
         *   \uHH     4
         *   \u{H+}   0 first time, updated to -1 to indicate
         *            at least one digit has been parsed
         *
         * Octal parsing is handled separately because it can be
         * done with fixed lookahead and also has validation
         * rules which depend on the escape length (which is
         * variable).
         *
         * We don't need a specific check for x < 0 (end of
         * input) or duk_unicode_is_line_terminator(x)
         * because the 'dig' decode will fail and lead to a
         * SyntaxError.
         */
        duk_codepoint_t dig;

        x = DUK__L0();
        DUK__ADVANCECHARS(lex_ctx, 1);

        dig = duk__hexval_validate(x);
        if (digits > 0) {
            digits--;
            if (dig < 0) {
                goto fail_escape;
            }
            DUK_ASSERT(dig >= 0x00 && dig <= 0x0f);
            escval = (escval << 4) + dig;
            if (digits == 0) {
                DUK_ASSERT(escval >= 0 && escval <= 0xffffL);
                break;
            }
        } else {
#if defined(DUK_USE_ES6_UNICODE_ESCAPE)
            DUK_ASSERT(digits == 0 /* first time */ || digits == -1 /* others */);
            if (dig >= 0) {
                DUK_ASSERT(dig >= 0x00 && dig <= 0x0f);
                escval = (escval << 4) + dig;
                if (escval > 0x10ffffL) {
                    goto fail_escape;
                }
            } else if (x == DUK_ASC_RCURLY) {
                if (digits == 0) {
                    /* Empty escape, \u{}. */
                    goto fail_escape;
                }
                DUK_ASSERT(escval >= 0 && escval <= 0x10ffffL);
                break;
            } else {
                goto fail_escape;
            }
            digits = -1;  /* Indicate we have at least one digit. */
#else  /* DUK_USE_ES6_UNICODE_ESCAPE */
            DUK_ASSERT(0);  /* Never happens if \u{H+} support disabled. */
#endif  /* DUK_USE_ES6_UNICODE_ESCAPE */
        }
    }

    return escval;

 fail_escape:
    DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_ESCAPE);
    DUK_WO_NORETURN(return 0;);
}

/* Parse legacy octal escape of the form \N{1,3}, e.g. \0, \5, \0377.  Maximum
 * allowed value is \0377 (U+00FF), longest match is used.  Used for both string
 * RegExp octal escape parsing.  Window[0] must be the slash '\' and the first
 * digit must already be validated to be in [0-9] by the caller.
 */
DUK_LOCAL duk_codepoint_t duk__lexer_parse_legacy_octal(duk_lexer_ctx *lex_ctx, duk_small_uint_t *out_adv, duk_bool_t reject_annex_b) {
    duk_codepoint_t cp;
    duk_small_uint_t lookup_idx;
    duk_small_uint_t adv;
    duk_codepoint_t tmp;

    DUK_ASSERT(out_adv != NULL);
    DUK_ASSERT(DUK__LOOKUP(lex_ctx, 0) == DUK_ASC_BACKSLASH);
    DUK_ASSERT(DUK__LOOKUP(lex_ctx, 1) >= DUK_ASC_0 && DUK__LOOKUP(lex_ctx, 1) <= DUK_ASC_9);

    cp = 0;
    tmp = 0;
    for (lookup_idx = 1; lookup_idx <= 3; lookup_idx++) {
        DUK_DDD(DUK_DDDPRINT("lookup_idx=%ld, cp=%ld", (long) lookup_idx, (long) cp));
        tmp = DUK__LOOKUP(lex_ctx, lookup_idx);
        if (tmp < DUK_ASC_0 || tmp > DUK_ASC_7) {
            /* No more valid digits. */
            break;
        }
        tmp = (cp << 3) + (tmp - DUK_ASC_0);
        if (tmp > 0xff) {
            /* Three digit octal escapes above \377 (= 0xff)
             * are not allowed.
             */
            break;
        }
        cp = tmp;
    }
    DUK_DDD(DUK_DDDPRINT("final lookup_idx=%ld, cp=%ld", (long) lookup_idx, (long) cp));

    adv = lookup_idx;
    if (lookup_idx == 1) {
        DUK_DDD(DUK_DDDPRINT("\\8 or \\9 -> treat as literal, accept in strict mode too"));
        DUK_ASSERT(tmp == DUK_ASC_8 || tmp == DUK_ASC_9);
        cp = tmp;
        adv++;  /* correction to above, eat offending character */
    } else if (lookup_idx == 2 && cp == 0) {
        /* Note: 'foo\0bar' is OK in strict mode, but 'foo\00bar' is not.
         * It won't be interpreted as 'foo\u{0}0bar' but as a SyntaxError.
         */
        DUK_DDD(DUK_DDDPRINT("\\0 -> accept in strict mode too"));
    } else {
        /* This clause also handles non-shortest zero, e.g. \00. */
        if (reject_annex_b) {
            DUK_DDD(DUK_DDDPRINT("non-zero octal literal %ld -> reject in strict-mode", (long) cp));
            cp = -1;
        } else {
            DUK_DDD(DUK_DDDPRINT("non-zero octal literal %ld -> accepted", (long) cp));
            DUK_ASSERT(cp >= 0 && cp <= 0xff);
        }
    }

    *out_adv = adv;

    DUK_ASSERT((cp >= 0 && cp <= 0xff) || (cp == -1 && reject_annex_b));
    return cp;
}

/* XXX: move strict mode to lex_ctx? */
DUK_LOCAL void duk__lexer_parse_string_literal(duk_lexer_ctx *lex_ctx, duk_token *out_token, duk_small_int_t quote, duk_bool_t strict_mode) {
    duk_small_uint_t adv;

    for (adv = 1 /* initial quote */ ;;) {
        duk_codepoint_t x;

        DUK__ADVANCECHARS(lex_ctx, adv);  /* eat opening quote on first loop */
        x = DUK__L0();

        adv = 1;
        if (x == quote) {
            DUK__ADVANCECHARS(lex_ctx, 1);  /* eat closing quote */
            break;
        } else if (x == '\\') {
            /* DUK__L0        -> '\' char
             * DUK__L1 ... DUK__L5 -> more lookup
             */
            duk_small_int_t emitcp = -1;

            x = DUK__L1();

            /* How much to advance before next loop. */
            adv = 2;  /* note: long live range */

            switch (x) {
            case '\'':
                emitcp = 0x0027;
                break;
            case '"':
                emitcp = 0x0022;
                break;
            case '\\':
                emitcp = 0x005c;
                break;
            case 'b':
                emitcp = 0x0008;
                break;
            case 'f':
                emitcp = 0x000c;
                break;
            case 'n':
                emitcp = 0x000a;
                break;
            case 'r':
                emitcp = 0x000d;
                break;
            case 't':
                emitcp = 0x0009;
                break;
            case 'v':
                emitcp = 0x000b;
                break;
            case 'x':
            case 'u': {
                duk_codepoint_t esc_cp;
                esc_cp = duk__lexer_parse_escape(lex_ctx, 1 /*allow_es6*/);
                DUK__APPENDBUFFER(lex_ctx, esc_cp);
                adv = 0;
                break;
            }
            default: {
                if (duk_unicode_is_line_terminator(x)) {
                    /* line continuation */
                    if (x == 0x000d && DUK__L2() == 0x000a) {
                        /* CR LF again a special case */
                        adv = 3;  /* line terminator, CR, LF */
                    }
                } else if (DUK__ISDIGIT(x)) {
                    /*
                     *  Octal escape or zero escape:
                     *    \0                                     (lookahead not OctalDigit)
                     *    \1 ... \7                              (lookahead not OctalDigit)
                     *    \ZeroToThree OctalDigit                (lookahead not OctalDigit)
                     *    \FourToSeven OctalDigit                (no lookahead restrictions)
                     *    \ZeroToThree OctalDigit OctalDigit     (no lookahead restrictions)
                     *
                     *  Zero escape is part of the standard syntax.  Octal escapes are
                     *  defined in E5 Section B.1.2, and are only allowed in non-strict mode.
                     *  Any other productions starting with a decimal digit are invalid
                     *  but are in practice treated like identity escapes.
                     *
                     *  Parse octal (up to 3 digits) from the lookup window.
                     */

                    emitcp = duk__lexer_parse_legacy_octal(lex_ctx, &adv, strict_mode /*reject_annex_b*/);
                    if (emitcp < 0) {
                        goto fail_escape;
                    }
                } else if (x < 0) {
                    goto fail_unterminated;
                } else {
                    /* escaped NonEscapeCharacter */
                    DUK__APPENDBUFFER(lex_ctx, x);
                }
            }  /* end default clause */
            }  /* end switch */

            /* Shared handling for single codepoint escapes. */
            if (emitcp >= 0) {
                DUK__APPENDBUFFER(lex_ctx, emitcp);
            }

            /* Track number of escapes; count not really needed but directive
             * prologues need to detect whether there were any escapes or line
             * continuations or not.
             */
            out_token->num_escapes++;
        } else if (x >= 0x20 && x <= 0x7f) {
            /* Fast path for ASCII case, avoids line terminator
             * check and CESU-8 encoding.
             */
            DUK_ASSERT(x >= 0);
            DUK_ASSERT(!duk_unicode_is_line_terminator(x));
            DUK_ASSERT(x != quote);
            DUK_ASSERT(x != DUK_ASC_BACKSLASH);
            DUK__APPENDBUFFER_ASCII(lex_ctx, x);
        } else if (x < 0 || duk_unicode_is_line_terminator(x)) {
            goto fail_unterminated;
        } else {
            /* Character which is part of the string but wasn't handled
             * by the fast path.
             */
            DUK__APPENDBUFFER(lex_ctx, x);
        }
    } /* string parse loop */

    return;

 fail_escape:
    DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_ESCAPE);
    DUK_WO_NORETURN(return;);

 fail_unterminated:
    DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_UNTERMINATED_STRING);
    DUK_WO_NORETURN(return;);
}

/* Skip to end-of-line (or end-of-file), used for single line comments. */
DUK_LOCAL void duk__lexer_skip_to_endofline(duk_lexer_ctx *lex_ctx) {
    for (;;) {
        duk_codepoint_t x;

        x = DUK__L0();
        if (x < 0 || duk_unicode_is_line_terminator(x)) {
            break;
        }
        DUK__ADVANCECHARS(lex_ctx, 1);
    }
}

/*
 *  Parse ECMAScript source InputElementDiv or InputElementRegExp
 *  (E5 Section 7), skipping whitespace, comments, and line terminators.
 *
 *  Possible results are:
 *    (1) a token
 *    (2) a line terminator (skipped)
 *    (3) a comment (skipped)
 *    (4) EOF
 *
 *  White space is automatically skipped from the current position (but
 *  not after the input element).  If input has already ended, returns
 *  DUK_TOK_EOF indefinitely.  If a parse error occurs, uses an DUK_ERROR()
 *  macro call (and hence a longjmp through current heap longjmp context).
 *  Comments and line terminator tokens are automatically skipped.
 *
 *  The input element being matched is determined by regexp_mode; if set,
 *  parses a InputElementRegExp, otherwise a InputElementDiv.  The
 *  difference between these are handling of productions starting with a
 *  forward slash.
 *
 *  If strict_mode is set, recognizes additional future reserved words
 *  specific to strict mode, and refuses to parse octal literals.
 *
 *  The matching strategy below is to (currently) use a six character
 *  lookup window to quickly determine which production is the -longest-
 *  matching one, and then parse that.  The top-level if-else clauses
 *  match the first character, and the code blocks for each clause
 *  handle -all- alternatives for that first character.  ECMAScript
 *  specification uses the "longest match wins" semantics, so the order
 *  of the if-clauses matters.
 *
 *  Misc notes:
 *
 *    * ECMAScript numeric literals do not accept a sign character.
 *      Consequently e.g. "-1.0" is parsed as two tokens: a negative
 *      sign and a positive numeric literal.  The compiler performs
 *      the negation during compilation, so this has no adverse impact.
 *
 *    * There is no token for "undefined": it is just a value available
 *      from the global object (or simply established by doing a reference
 *      to an undefined value).
 *
 *    * Some contexts want Identifier tokens, which are IdentifierNames
 *      excluding reserved words, while some contexts want IdentifierNames
 *      directly.  In the latter case e.g. "while" is interpreted as an
 *      identifier name, not a DUK_TOK_WHILE token.  The solution here is
 *      to provide both token types: DUK_TOK_WHILE goes to 't' while
 *      DUK_TOK_IDENTIFIER goes to 't_nores', and 'slot1' always contains
 *      the identifier / keyword name.
 *
 *    * Directive prologue needs to identify string literals such as
 *      "use strict" and 'use strict', which are sensitive to line
 *      continuations and escape sequences.  For instance, "use\u0020strict"
 *      is a valid directive but is distinct from "use strict".  The solution
 *      here is to decode escapes while tokenizing, but to keep track of the
 *      number of escapes.  Directive detection can then check that the
 *      number of escapes is zero.
 *
 *    * Multi-line comments with one or more internal LineTerminator are
 *      treated like a line terminator to comply with automatic semicolon
 *      insertion.
 */

DUK_INTERNAL
void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx,
                                      duk_token *out_token,
                                      duk_bool_t strict_mode,
                                      duk_bool_t regexp_mode) {
    duk_codepoint_t x;           /* temporary, must be signed and 32-bit to hold Unicode code points */
    duk_small_uint_t advtok = 0; /* (advance << 8) + token_type, updated at function end,
                                  * init is unnecessary but suppresses "may be used uninitialized" warnings.
                                  */
    duk_bool_t got_lineterm = 0;  /* got lineterm preceding non-whitespace, non-lineterm token */

    if (++lex_ctx->token_count >= lex_ctx->token_limit) {
        goto fail_token_limit;
    }

    out_token->t = DUK_TOK_EOF;
    out_token->t_nores = DUK_TOK_INVALID;  /* marker: copy t if not changed */
#if 0  /* not necessary to init, disabled for faster parsing */
    out_token->num = DUK_DOUBLE_NAN;
    out_token->str1 = NULL;
    out_token->str2 = NULL;
#endif
    out_token->num_escapes = 0;
    /* out_token->lineterm set by caller */

    /* This would be nice, but parsing is faster without resetting the
     * value slots.  The only side effect is that references to temporary
     * string values may linger until lexing is finished; they're then
     * freed normally.
     */
#if 0
    duk_to_undefined(lex_ctx->thr, lex_ctx->slot1_idx);
    duk_to_undefined(lex_ctx->thr, lex_ctx->slot2_idx);
#endif

    /* 'advtok' indicates how much to advance and which token id to assign
     * at the end.  This shared functionality minimizes code size.  All
     * code paths are required to set 'advtok' to some value, so no default
     * init value is used.  Code paths calling DUK_ERROR() never return so
     * they don't need to set advtok.
     */

    /*
     *  Matching order:
     *
     *    Punctuator first chars, also covers comments, regexps
     *    LineTerminator
     *    Identifier or reserved word, also covers null/true/false literals
     *    NumericLiteral
     *    StringLiteral
     *    EOF
     *
     *  The order does not matter as long as the longest match is
     *  always correctly identified.  There are order dependencies
     *  in the clauses, so it's not trivial to convert to a switch.
     */

 restart_lineupdate:
    out_token->start_line = lex_ctx->window[0].line;

 restart:
    out_token->start_offset = lex_ctx->window[0].offset;

    x = DUK__L0();

    switch (x) {
    case DUK_ASC_SPACE:
    case DUK_ASC_HT:  /* fast paths for space and tab */
        DUK__ADVANCECHARS(lex_ctx, 1);
        goto restart;
    case DUK_ASC_LF:  /* LF line terminator; CR LF and Unicode lineterms are handled in slow path */
        DUK__ADVANCECHARS(lex_ctx, 1);
        got_lineterm = 1;
        goto restart_lineupdate;
#if defined(DUK_USE_SHEBANG_COMMENTS)
    case DUK_ASC_HASH:  /* '#' */
        if (DUK__L1() == DUK_ASC_EXCLAMATION && lex_ctx->window[0].offset == 0 &&
            (lex_ctx->flags & DUK_COMPILE_SHEBANG)) {
            /* "Shebang" comment ('#! ...') on first line. */
            /* DUK__ADVANCECHARS(lex_ctx, 2) would be correct here, but not necessary */
            duk__lexer_skip_to_endofline(lex_ctx);
            goto restart;  /* line terminator will be handled on next round */
        }
        goto fail_token;
#endif  /* DUK_USE_SHEBANG_COMMENTS */
    case DUK_ASC_SLASH:  /* '/' */
        if (DUK__L1() == DUK_ASC_SLASH) {
            /*
             *  E5 Section 7.4, allow SourceCharacter (which is any 16-bit
             *  code point).
             */

            /* DUK__ADVANCECHARS(lex_ctx, 2) would be correct here, but not necessary */
            duk__lexer_skip_to_endofline(lex_ctx);
            goto restart;  /* line terminator will be handled on next round */
        } else if (DUK__L1() == DUK_ASC_STAR) {
            /*
             *  E5 Section 7.4.  If the multi-line comment contains a newline,
             *  it is treated like a single line terminator for automatic
             *  semicolon insertion.
             */

            duk_bool_t last_asterisk = 0;
            DUK__ADVANCECHARS(lex_ctx, 2);
            for (;;) {
                x = DUK__L0();
                if (x < 0) {
                    goto fail_unterm_comment;
                }
                DUK__ADVANCECHARS(lex_ctx, 1);
                if (last_asterisk && x == DUK_ASC_SLASH) {
                    break;
                }
                if (duk_unicode_is_line_terminator(x)) {
                    got_lineterm = 1;
                }
                last_asterisk = (x == DUK_ASC_STAR);
            }
            goto restart_lineupdate;
        } else if (regexp_mode) {
#if defined(DUK_USE_REGEXP_SUPPORT)
            /*
             *  "/" followed by something in regexp mode.  See E5 Section 7.8.5.
             *
             *  RegExp parsing is a bit complex.  First, the regexp body is delimited
             *  by forward slashes, but the body may also contain forward slashes as
             *  part of an escape sequence or inside a character class (delimited by
             *  square brackets).  A mini state machine is used to implement these.
             *
             *  Further, an early (parse time) error must be thrown if the regexp
             *  would cause a run-time error when used in the expression new RegExp(...).
             *  Parsing here simply extracts the (candidate) regexp, and also accepts
             *  invalid regular expressions (which are delimited properly).  The caller
             *  (compiler) must perform final validation and regexp compilation.
             *
             *  RegExp first char may not be '/' (single line comment) or '*' (multi-
             *  line comment).  These have already been checked above, so there is no
             *  need below for special handling of the first regexp character as in
             *  the E5 productions.
             *
             *  About unicode escapes within regexp literals:
             *
             *      E5 Section 7.8.5 grammar does NOT accept \uHHHH escapes.
             *      However, Section 6 states that regexps accept the escapes,
             *      see paragraph starting with "In string literals...".
             *      The regexp grammar, which sees the decoded regexp literal
             *      (after lexical parsing) DOES have a \uHHHH unicode escape.
             *      So, for instance:
             *
             *          /\u1234/
             *
             *      should first be parsed by the lexical grammar as:
             *
             *          '\' 'u'      RegularExpressionBackslashSequence
             *          '1'          RegularExpressionNonTerminator
             *          '2'          RegularExpressionNonTerminator
             *          '3'          RegularExpressionNonTerminator
             *          '4'          RegularExpressionNonTerminator
             *
             *      and the escape itself is then parsed by the regexp engine.
             *      This is the current implementation.
             *
             *  Minor spec inconsistency:
             *
             *      E5 Section 7.8.5 RegularExpressionBackslashSequence is:
             *
             *         \ RegularExpressionNonTerminator
             *
             *      while Section A.1 RegularExpressionBackslashSequence is:
             *
             *         \ NonTerminator
             *
             *      The latter is not normative and a typo.
             *
             */

            /* first, parse regexp body roughly */

            duk_small_int_t state = 0;  /* 0=base, 1=esc, 2=class, 3=class+esc */

            DUK__INITBUFFER(lex_ctx);
            for (;;) {
                DUK__ADVANCECHARS(lex_ctx, 1);  /* skip opening slash on first loop */
                x = DUK__L0();
                if (x < 0 || duk_unicode_is_line_terminator(x)) {
                    goto fail_unterm_regexp;
                }
                x = DUK__L0();  /* re-read to avoid spill / fetch */
                if (state == 0) {
                    if (x == DUK_ASC_SLASH) {
                        DUK__ADVANCECHARS(lex_ctx, 1);  /* eat closing slash */
                        break;
                    } else if (x == DUK_ASC_BACKSLASH) {
                        state = 1;
                    } else if (x == DUK_ASC_LBRACKET) {
                        state = 2;
                    }
                } else if (state == 1) {
                    state = 0;
                } else if (state == 2) {
                    if (x == DUK_ASC_RBRACKET) {
                        state = 0;
                    } else if (x == DUK_ASC_BACKSLASH) {
                        state = 3;
                    }
                } else { /* state == 3 */
                    state = 2;
                }
                DUK__APPENDBUFFER(lex_ctx, x);
            }
            out_token->str1 = duk__internbuffer(lex_ctx, lex_ctx->slot1_idx);

            /* second, parse flags */

            DUK__INITBUFFER(lex_ctx);
            for (;;) {
                x = DUK__L0();
                if (!duk_unicode_is_identifier_part(x)) {
                    break;
                }
                x = DUK__L0();  /* re-read to avoid spill / fetch */
                DUK__APPENDBUFFER(lex_ctx, x);
                DUK__ADVANCECHARS(lex_ctx, 1);
            }
            out_token->str2 = duk__internbuffer(lex_ctx, lex_ctx->slot2_idx);

            DUK__INITBUFFER(lex_ctx);  /* free some memory */

            /* validation of the regexp is caller's responsibility */

            advtok = DUK__ADVTOK(0, DUK_TOK_REGEXP);
#else  /* DUK_USE_REGEXP_SUPPORT */
            goto fail_regexp_support;
#endif  /* DUK_USE_REGEXP_SUPPORT */
        } else if (DUK__L1() == DUK_ASC_EQUALS) {
            /* "/=" and not in regexp mode */
            advtok = DUK__ADVTOK(2, DUK_TOK_DIV_EQ);
        } else {
            /* "/" and not in regexp mode */
            advtok = DUK__ADVTOK(1, DUK_TOK_DIV);
        }
        break;
    case DUK_ASC_LCURLY:  /* '{' */
        advtok = DUK__ADVTOK(1, DUK_TOK_LCURLY);
        break;
    case DUK_ASC_RCURLY:  /* '}' */
        advtok = DUK__ADVTOK(1, DUK_TOK_RCURLY);
        break;
    case DUK_ASC_LPAREN:  /* '(' */
        advtok = DUK__ADVTOK(1, DUK_TOK_LPAREN);
        break;
    case DUK_ASC_RPAREN:  /* ')' */
        advtok = DUK__ADVTOK(1, DUK_TOK_RPAREN);
        break;
    case DUK_ASC_LBRACKET:  /* '[' */
        advtok = DUK__ADVTOK(1, DUK_TOK_LBRACKET);
        break;
    case DUK_ASC_RBRACKET:  /* ']' */
        advtok = DUK__ADVTOK(1, DUK_TOK_RBRACKET);
        break;
    case DUK_ASC_PERIOD:  /* '.' */
        if (DUK__ISDIGIT(DUK__L1())) {
            /* Period followed by a digit can only start DecimalLiteral
             * (handled in slow path).  We could jump straight into the
             * DecimalLiteral handling but should avoid goto to inside
             * a block.
             */
            goto slow_path;
        }
        advtok = DUK__ADVTOK(1, DUK_TOK_PERIOD);
        break;
    case DUK_ASC_SEMICOLON:  /* ';' */
        advtok = DUK__ADVTOK(1, DUK_TOK_SEMICOLON);
        break;
    case DUK_ASC_COMMA:  /* ',' */
        advtok = DUK__ADVTOK(1, DUK_TOK_COMMA);
        break;
    case DUK_ASC_LANGLE:  /* '<' */
#if defined(DUK_USE_HTML_COMMENTS)
        if (DUK__L1() == DUK_ASC_EXCLAMATION && DUK__L2() == DUK_ASC_MINUS && DUK__L3() == DUK_ASC_MINUS) {
            /*
             *  ES2015: B.1.3, handle "<!--" SingleLineHTMLOpenComment
             */

            /* DUK__ADVANCECHARS(lex_ctx, 4) would be correct here, but not necessary */
            duk__lexer_skip_to_endofline(lex_ctx);
            goto restart;  /* line terminator will be handled on next round */
        }
        else
#endif  /* DUK_USE_HTML_COMMENTS */
        if (DUK__L1() == DUK_ASC_LANGLE && DUK__L2() == DUK_ASC_EQUALS) {
            advtok = DUK__ADVTOK(3, DUK_TOK_ALSHIFT_EQ);
        } else if (DUK__L1() == DUK_ASC_EQUALS) {
            advtok = DUK__ADVTOK(2, DUK_TOK_LE);
        } else if (DUK__L1() == DUK_ASC_LANGLE) {
            advtok = DUK__ADVTOK(2, DUK_TOK_ALSHIFT);
        } else {
            advtok = DUK__ADVTOK(1, DUK_TOK_LT);
        }
        break;
    case DUK_ASC_RANGLE:  /* '>' */
        if (DUK__L1() == DUK_ASC_RANGLE && DUK__L2() == DUK_ASC_RANGLE && DUK__L3() == DUK_ASC_EQUALS) {
            advtok = DUK__ADVTOK(4, DUK_TOK_RSHIFT_EQ);
        } else if (DUK__L1() == DUK_ASC_RANGLE && DUK__L2() == DUK_ASC_RANGLE) {
            advtok = DUK__ADVTOK(3, DUK_TOK_RSHIFT);
        } else if (DUK__L1() == DUK_ASC_RANGLE && DUK__L2() == DUK_ASC_EQUALS) {
            advtok = DUK__ADVTOK(3, DUK_TOK_ARSHIFT_EQ);
        } else if (DUK__L1() == DUK_ASC_EQUALS) {
            advtok = DUK__ADVTOK(2, DUK_TOK_GE);
        } else if (DUK__L1() == DUK_ASC_RANGLE) {
            advtok = DUK__ADVTOK(2, DUK_TOK_ARSHIFT);
        } else {
            advtok = DUK__ADVTOK(1, DUK_TOK_GT);
        }
        break;
    case DUK_ASC_EQUALS:  /* '=' */
        if (DUK__L1() == DUK_ASC_EQUALS && DUK__L2() == DUK_ASC_EQUALS) {
            advtok = DUK__ADVTOK(3, DUK_TOK_SEQ);
        } else if (DUK__L1() == DUK_ASC_EQUALS) {
            advtok = DUK__ADVTOK(2, DUK_TOK_EQ);
        } else {
            advtok = DUK__ADVTOK(1, DUK_TOK_EQUALSIGN);
        }
        break;
    case DUK_ASC_EXCLAMATION:  /* '!' */
        if (DUK__L1() == DUK_ASC_EQUALS && DUK__L2() == DUK_ASC_EQUALS) {
            advtok = DUK__ADVTOK(3, DUK_TOK_SNEQ);
        } else if (DUK__L1() == DUK_ASC_EQUALS) {
            advtok = DUK__ADVTOK(2, DUK_TOK_NEQ);
        } else {
            advtok = DUK__ADVTOK(1, DUK_TOK_LNOT);
        }
        break;
    case DUK_ASC_PLUS:  /* '+' */
        if (DUK__L1() == DUK_ASC_PLUS) {
            advtok = DUK__ADVTOK(2, DUK_TOK_INCREMENT);
        } else if (DUK__L1() == DUK_ASC_EQUALS) {
            advtok = DUK__ADVTOK(2, DUK_TOK_ADD_EQ);
        } else {
            advtok = DUK__ADVTOK(1, DUK_TOK_ADD);
        }
        break;
    case DUK_ASC_MINUS:  /* '-' */
#if defined(DUK_USE_HTML_COMMENTS)
        if (got_lineterm && DUK__L1() == DUK_ASC_MINUS && DUK__L2() == DUK_ASC_RANGLE) {
            /*
             *  ES2015: B.1.3, handle "-->" SingleLineHTMLCloseComment
             *  Only allowed:
             *  - on new line
             *  - preceded only by whitespace
             *  - preceded by end of multiline comment and optional whitespace
             *
             * Since whitespace generates no tokens, and multiline comments
             * are treated as a line ending, consulting `got_lineterm` is
             * sufficient to test for these three options.
             */

            /* DUK__ADVANCECHARS(lex_ctx, 3) would be correct here, but not necessary */
            duk__lexer_skip_to_endofline(lex_ctx);
            goto restart;  /* line terminator will be handled on next round */
        } else
#endif  /* DUK_USE_HTML_COMMENTS */
        if (DUK__L1() == DUK_ASC_MINUS) {
            advtok = DUK__ADVTOK(2, DUK_TOK_DECREMENT);
        } else if (DUK__L1() == DUK_ASC_EQUALS) {
            advtok = DUK__ADVTOK(2, DUK_TOK_SUB_EQ);
        } else {
            advtok = DUK__ADVTOK(1, DUK_TOK_SUB);
        }
        break;
    case DUK_ASC_STAR:  /* '*' */
#if defined(DUK_USE_ES7_EXP_OPERATOR)
        if (DUK__L1() == DUK_ASC_STAR && DUK__L2() == DUK_ASC_EQUALS) {
            advtok = DUK__ADVTOK(3, DUK_TOK_EXP_EQ);
        } else if (DUK__L1() == DUK_ASC_STAR) {
            advtok = DUK__ADVTOK(2, DUK_TOK_EXP);
        } else
#endif
        if (DUK__L1() == DUK_ASC_EQUALS) {
            advtok = DUK__ADVTOK(2, DUK_TOK_MUL_EQ);
        } else {
            advtok = DUK__ADVTOK(1, DUK_TOK_MUL);
        }
        break;
    case DUK_ASC_PERCENT:  /* '%' */
        if (DUK__L1() == DUK_ASC_EQUALS) {
            advtok = DUK__ADVTOK(2, DUK_TOK_MOD_EQ);
        } else {
            advtok = DUK__ADVTOK(1, DUK_TOK_MOD);
        }
        break;
    case DUK_ASC_AMP:  /* '&' */
        if (DUK__L1() == DUK_ASC_AMP) {
            advtok = DUK__ADVTOK(2, DUK_TOK_LAND);
        } else if (DUK__L1() == DUK_ASC_EQUALS) {
            advtok = DUK__ADVTOK(2, DUK_TOK_BAND_EQ);
        } else {
            advtok = DUK__ADVTOK(1, DUK_TOK_BAND);
        }
        break;
    case DUK_ASC_PIPE:  /* '|' */
        if (DUK__L1() == DUK_ASC_PIPE) {
            advtok = DUK__ADVTOK(2, DUK_TOK_LOR);
        } else if (DUK__L1() == DUK_ASC_EQUALS) {
            advtok = DUK__ADVTOK(2, DUK_TOK_BOR_EQ);
        } else {
            advtok = DUK__ADVTOK(1, DUK_TOK_BOR);
        }
        break;
    case DUK_ASC_CARET:  /* '^' */
        if (DUK__L1() == DUK_ASC_EQUALS) {
            advtok = DUK__ADVTOK(2, DUK_TOK_BXOR_EQ);
        } else {
            advtok = DUK__ADVTOK(1, DUK_TOK_BXOR);
        }
        break;
    case DUK_ASC_TILDE:  /* '~' */
        advtok = DUK__ADVTOK(1, DUK_TOK_BNOT);
        break;
    case DUK_ASC_QUESTION:  /* '?' */
        advtok = DUK__ADVTOK(1, DUK_TOK_QUESTION);
        break;
    case DUK_ASC_COLON:  /* ':' */
        advtok = DUK__ADVTOK(1, DUK_TOK_COLON);
        break;
    case DUK_ASC_DOUBLEQUOTE:    /* '"' */
    case DUK_ASC_SINGLEQUOTE: {  /* '\'' */
        DUK__INITBUFFER(lex_ctx);
        duk__lexer_parse_string_literal(lex_ctx, out_token, x /*quote*/, strict_mode);
        duk__internbuffer(lex_ctx, lex_ctx->slot1_idx);
        out_token->str1 = duk_known_hstring(lex_ctx->thr, lex_ctx->slot1_idx);

        DUK__INITBUFFER(lex_ctx);  /* free some memory */

        advtok = DUK__ADVTOK(0, DUK_TOK_STRING);
        break;
    }
    default:
        goto slow_path;
    }  /* switch */

    goto skip_slow_path;

 slow_path:
    if (duk_unicode_is_line_terminator(x)) {
        if (x == 0x000d && DUK__L1() == 0x000a) {
            /*
             *  E5 Section 7.3: CR LF is detected as a single line terminator for
             *  line numbers.  Here we also detect it as a single line terminator
             *  token.
             */
            DUK__ADVANCECHARS(lex_ctx, 2);
        } else {
            DUK__ADVANCECHARS(lex_ctx, 1);
        }
        got_lineterm = 1;
        goto restart_lineupdate;
    } else if (duk_unicode_is_identifier_start(x) || x == DUK_ASC_BACKSLASH) {
        /*
         *  Parse an identifier and then check whether it is:
         *    - reserved word (keyword or other reserved word)
         *    - "null"  (NullLiteral)
         *    - "true"  (BooleanLiteral)
         *    - "false" (BooleanLiteral)
         *    - anything else => identifier
         *
         *  This does not follow the E5 productions cleanly, but is
         *  useful and compact.
         *
         *  Note that identifiers may contain Unicode escapes,
         *  see E5 Sections 6 and 7.6.  They must be decoded first,
         *  and the result checked against allowed characters.
         *  The above if-clause accepts an identifier start and an
         *  '\' character -- no other token can begin with a '\'.
         *
         *  Note that "get" and "set" are not reserved words in E5
         *  specification so they are recognized as plain identifiers
         *  (the tokens DUK_TOK_GET and DUK_TOK_SET are actually not
         *  used now).  The compiler needs to work around this.
         *
         *  Strictly speaking, following ECMAScript longest match
         *  specification, an invalid escape for the first character
         *  should cause a syntax error.  However, an invalid escape
         *  for IdentifierParts should just terminate the identifier
         *  early (longest match), and let the next tokenization
         *  fail.  For instance Rhino croaks with 'foo\z' when
         *  parsing the identifier.  This has little practical impact.
         */

        duk_small_uint_t i, i_end;
        duk_bool_t first = 1;
        duk_hstring *str;

        DUK__INITBUFFER(lex_ctx);
        for (;;) {
            /* re-lookup first char on first loop */
            if (DUK__L0() == DUK_ASC_BACKSLASH) {
                duk_codepoint_t esc_cp;
                if (DUK__L1() != DUK_ASC_LC_U) {
                    goto fail_escape;
                }
                esc_cp = duk__lexer_parse_escape(lex_ctx, 1 /*allow_es6*/);
                DUK__APPENDBUFFER(lex_ctx, esc_cp);

                /* IdentifierStart is stricter than IdentifierPart, so if the first
                 * character is escaped, must have a stricter check here.
                 */
                if (!(first ? duk_unicode_is_identifier_start(esc_cp) : duk_unicode_is_identifier_part(esc_cp))) {
                    goto fail_escape;
                }

                /* Track number of escapes: necessary for proper keyword
                 * detection.
                 */
                out_token->num_escapes++;
            } else {
                /* Note: first character is checked against this.  But because
                 * IdentifierPart includes all IdentifierStart characters, and
                 * the first character (if unescaped) has already been checked
                 * in the if condition, this is OK.
                 */
                if (!duk_unicode_is_identifier_part(DUK__L0())) {
                    break;
                }
                DUK__APPENDBUFFER(lex_ctx, DUK__L0());
                DUK__ADVANCECHARS(lex_ctx, 1);
            }
            first = 0;
        }

        out_token->str1 = duk__internbuffer(lex_ctx, lex_ctx->slot1_idx);
        str = out_token->str1;
        out_token->t_nores = DUK_TOK_IDENTIFIER;

        DUK__INITBUFFER(lex_ctx);  /* free some memory */

        /*
         *  Interned identifier is compared against reserved words, which are
         *  currently interned into the heap context.  See genbuiltins.py.
         *
         *  Note that an escape in the identifier disables recognition of
         *  keywords; e.g. "\u0069f = 1;" is a valid statement (assigns to
         *  identifier named "if").  This is not necessarily compliant,
         *  see test-dec-escaped-char-in-keyword.js.
         *
         *  Note: "get" and "set" are awkward.  They are not officially
         *  ReservedWords (and indeed e.g. "var set = 1;" is valid), and
         *  must come out as DUK_TOK_IDENTIFIER.  The compiler needs to
         *  work around this a bit.
         */

        /* XXX: optimize by adding the token numbers directly into the
         * always interned duk_hstring objects (there should be enough
         * flag bits free for that)?
         */

        i_end = (strict_mode ? DUK_STRIDX_END_RESERVED : DUK_STRIDX_START_STRICT_RESERVED);

        advtok = DUK__ADVTOK(0, DUK_TOK_IDENTIFIER);
        if (out_token->num_escapes == 0) {
            for (i = DUK_STRIDX_START_RESERVED; i < i_end; i++) {
                DUK_ASSERT_DISABLE(i >= 0);  /* unsigned */
                DUK_ASSERT(i < DUK_HEAP_NUM_STRINGS);
                if (DUK_HTHREAD_GET_STRING(lex_ctx->thr, i) == str) {
                    advtok = DUK__ADVTOK(0, DUK_STRIDX_TO_TOK(i));
                    break;
                }
            }
        }
    } else if (DUK__ISDIGIT(x) || (x == DUK_ASC_PERIOD)) {
        /* Note: decimal number may start with a period, but must be followed by a digit */

        /*
         *  Pre-parsing for decimal, hex, octal (both legacy and ES2015),
         *  and binary literals, followed by an actual parser step
         *  provided by numconv.
         *
         *  Note: the leading sign character ('+' or '-') is -not- part of
         *  the production in E5 grammar, and that the a DecimalLiteral
         *  starting with a '0' must be followed by a non-digit.
         *
         *  XXX: the two step parsing process is quite awkward, it would
         *  be more straightforward to allow numconv to parse the longest
         *  valid prefix (it already does that, it only needs to indicate
         *  where the input ended).  However, the lexer decodes characters
         *  using a limited lookup window, so this is not a trivial change.
         */

        /* XXX: because of the final check below (that the literal is not
         * followed by a digit), this could maybe be simplified, if we bail
         * out early from a leading zero (and if there are no periods etc).
         * Maybe too complex.
         */

        duk_double_t val;
        duk_bool_t legacy_oct = 0;
        duk_small_int_t state;  /* 0=before period/exp,
                                 * 1=after period, before exp
                                 * 2=after exp, allow '+' or '-'
                                 * 3=after exp and exp sign
                                 */
        duk_small_uint_t s2n_flags;
        duk_codepoint_t y, z;
        duk_small_int_t s2n_radix = 10;
        duk_small_uint_t pre_adv = 0;

        DUK__INITBUFFER(lex_ctx);
        y = DUK__L1();

        if (x == DUK_ASC_0) {
            z = DUK_LOWERCASE_CHAR_ASCII(y);

            pre_adv = 2;  /* default for 0xNNN, 0oNNN, 0bNNN. */
            if (z == DUK_ASC_LC_X) {
                s2n_radix = 16;
            } else if (z == DUK_ASC_LC_O) {
                s2n_radix = 8;
            } else if (z == DUK_ASC_LC_B) {
                s2n_radix = 2;
            } else {
                pre_adv = 0;
                if (DUK__ISDIGIT(y)) {
                    if (strict_mode) {
                        /* Reject octal like \07 but also octal-lookalike
                         * decimal like \08 in strict mode.
                         */
                        goto fail_number_literal;
                    } else {
                        /* Legacy OctalIntegerLiteral or octal-lookalice
                         * decimal.  Deciding between the two happens below
                         * in digit scanning.
                         */
                        DUK__APPENDBUFFER(lex_ctx, x);
                        pre_adv = 1;
                        legacy_oct = 1;
                        s2n_radix = 8;  /* tentative unless conflicting digits found */
                    }
                }
            }
        }

        DUK__ADVANCECHARS(lex_ctx, pre_adv);

        /* XXX: we could parse integers here directly, and fall back
         * to numconv only when encountering a fractional expression
         * or when an octal literal turned out to be decimal (0778 etc).
         */
        state = 0;
        for (;;) {
            x = DUK__L0();  /* re-lookup curr char on first round */
            if (DUK__ISDIGIT(x)) {
                /* Note: intentionally allow leading zeroes here, as the
                 * actual parser will check for them.
                 */
                if (state == 0 && legacy_oct && (x == DUK_ASC_8 || x == DUK_ASC_9)) {
                    /* Started out as an octal-lookalike
                     * but interpreted as decimal, e.g.
                     * '0779' -> 779.  This also means
                     * that fractions are allowed, e.g.
                     * '0779.123' is allowed but '0777.123'
                     * is not!
                     */
                    s2n_radix = 10;
                }
                if (state == 2) {
                    state = 3;
                }
            } else if (s2n_radix == 16 && DUK__ISHEXDIGIT(x)) {
                /* Note: 'e' and 'E' are also accepted here. */
                ;
            } else if (x == DUK_ASC_PERIOD) {
                if (state >= 1 || s2n_radix != 10) {
                    break;
                } else {
                    state = 1;
                }
            } else if (x == DUK_ASC_LC_E || x == DUK_ASC_UC_E) {
                if (state >= 2 || s2n_radix != 10) {
                    break;
                } else {
                    state = 2;
                }
            } else if (x == DUK_ASC_MINUS || x == DUK_ASC_PLUS) {
                if (state != 2) {
                    break;
                } else {
                    state = 3;
                }
            } else {
                break;
            }
            DUK__APPENDBUFFER(lex_ctx, x);
            DUK__ADVANCECHARS(lex_ctx, 1);
        }

        /* XXX: better coercion */
        (void) duk__internbuffer(lex_ctx, lex_ctx->slot1_idx);

        if (s2n_radix != 10) {
            /* For bases other than 10, integer only. */
            s2n_flags = DUK_S2N_FLAG_ALLOW_LEADING_ZERO;
        } else {
            s2n_flags = DUK_S2N_FLAG_ALLOW_EXP |
                        DUK_S2N_FLAG_ALLOW_FRAC |
                        DUK_S2N_FLAG_ALLOW_NAKED_FRAC |
                        DUK_S2N_FLAG_ALLOW_EMPTY_FRAC |
                        DUK_S2N_FLAG_ALLOW_LEADING_ZERO;
        }

        duk_dup(lex_ctx->thr, lex_ctx->slot1_idx);
        duk_numconv_parse(lex_ctx->thr, s2n_radix, s2n_flags);
        val = duk_to_number_m1(lex_ctx->thr);
        if (DUK_ISNAN(val)) {
            goto fail_number_literal;
        }
        duk_replace(lex_ctx->thr, lex_ctx->slot1_idx);  /* could also just pop? */

        DUK__INITBUFFER(lex_ctx);  /* free some memory */

        /* Section 7.8.3 (note): NumericLiteral must be followed by something other than
         * IdentifierStart or DecimalDigit.
         */

        if (DUK__ISDIGIT(DUK__L0()) || duk_unicode_is_identifier_start(DUK__L0())) {
            goto fail_number_literal;
        }

        out_token->num = val;
        advtok = DUK__ADVTOK(0, DUK_TOK_NUMBER);
    } else if (duk_unicode_is_whitespace(DUK__LOOKUP(lex_ctx, 0))) {
        DUK__ADVANCECHARS(lex_ctx, 1);
        goto restart;
    } else if (x < 0) {
        advtok = DUK__ADVTOK(0, DUK_TOK_EOF);
    } else {
        goto fail_token;
    }
 skip_slow_path:

    /*
     *  Shared exit path
     */

    DUK__ADVANCEBYTES(lex_ctx, advtok >> 8);
    out_token->t = advtok & 0xff;
    if (out_token->t_nores == DUK_TOK_INVALID) {
        out_token->t_nores = out_token->t;
    }
    out_token->lineterm = got_lineterm;

    /* Automatic semicolon insertion is allowed if a token is preceded
     * by line terminator(s), or terminates a statement list (right curly
     * or EOF).
     */
    if (got_lineterm || out_token->t == DUK_TOK_RCURLY || out_token->t == DUK_TOK_EOF) {
        out_token->allow_auto_semi = 1;
    } else {
        out_token->allow_auto_semi = 0;
    }

    return;

 fail_token_limit:
    DUK_ERROR_RANGE(lex_ctx->thr, DUK_STR_TOKEN_LIMIT);
    DUK_WO_NORETURN(return;);

 fail_token:
    DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_TOKEN);
    DUK_WO_NORETURN(return;);

 fail_number_literal:
    DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_NUMBER_LITERAL);
    DUK_WO_NORETURN(return;);

 fail_escape:
    DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_ESCAPE);
    DUK_WO_NORETURN(return;);

 fail_unterm_regexp:
    DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_UNTERMINATED_REGEXP);
    DUK_WO_NORETURN(return;);

 fail_unterm_comment:
    DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_UNTERMINATED_COMMENT);
    DUK_WO_NORETURN(return;);

#if !defined(DUK_USE_REGEXP_SUPPORT)
 fail_regexp_support:
    DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_REGEXP_SUPPORT_DISABLED);
    DUK_WO_NORETURN(return;);
#endif
}

#if defined(DUK_USE_REGEXP_SUPPORT)

/*
 *  Parse a RegExp token.  The grammar is described in E5 Section 15.10.
 *  Terminal constructions (such as quantifiers) are parsed directly here.
 *
 *  0xffffffffU is used as a marker for "infinity" in quantifiers.  Further,
 *  DUK__MAX_RE_QUANT_DIGITS limits the maximum number of digits that
 *  will be accepted for a quantifier.
 */

DUK_INTERNAL void duk_lexer_parse_re_token(duk_lexer_ctx *lex_ctx, duk_re_token *out_token) {
    duk_small_uint_t advtok = 0;  /* init is unnecessary but suppresses "may be used uninitialized" warnings */
    duk_codepoint_t x, y;

    if (++lex_ctx->token_count >= lex_ctx->token_limit) {
        goto fail_token_limit;
    }

    duk_memzero(out_token, sizeof(*out_token));

    x = DUK__L0();
    y = DUK__L1();

    DUK_DDD(DUK_DDDPRINT("parsing regexp token, L0=%ld, L1=%ld", (long) x, (long) y));

    switch (x) {
    case DUK_ASC_PIPE: {
        advtok = DUK__ADVTOK(1, DUK_RETOK_DISJUNCTION);
        break;
    }
    case DUK_ASC_CARET: {
        advtok = DUK__ADVTOK(1, DUK_RETOK_ASSERT_START);
        break;
    }
    case DUK_ASC_DOLLAR: {
        advtok = DUK__ADVTOK(1, DUK_RETOK_ASSERT_END);
        break;
    }
    case DUK_ASC_QUESTION: {
        out_token->qmin = 0;
        out_token->qmax = 1;
        if (y == DUK_ASC_QUESTION) {
            advtok = DUK__ADVTOK(2, DUK_RETOK_QUANTIFIER);
            out_token->greedy = 0;
        } else {
            advtok = DUK__ADVTOK(1, DUK_RETOK_QUANTIFIER);
            out_token->greedy = 1;
        }
        break;
    }
    case DUK_ASC_STAR: {
        out_token->qmin = 0;
        out_token->qmax = DUK_RE_QUANTIFIER_INFINITE;
        if (y == DUK_ASC_QUESTION) {
            advtok = DUK__ADVTOK(2, DUK_RETOK_QUANTIFIER);
            out_token->greedy = 0;
        } else {
            advtok = DUK__ADVTOK(1, DUK_RETOK_QUANTIFIER);
            out_token->greedy = 1;
        }
        break;
    }
    case DUK_ASC_PLUS: {
        out_token->qmin = 1;
        out_token->qmax = DUK_RE_QUANTIFIER_INFINITE;
        if (y == DUK_ASC_QUESTION) {
            advtok = DUK__ADVTOK(2, DUK_RETOK_QUANTIFIER);
            out_token->greedy = 0;
        } else {
            advtok = DUK__ADVTOK(1, DUK_RETOK_QUANTIFIER);
            out_token->greedy = 1;
        }
        break;
    }
    case DUK_ASC_LCURLY: {
        /* Production allows 'DecimalDigits', including leading zeroes */
        duk_uint32_t val1 = 0;
        duk_uint32_t val2 = DUK_RE_QUANTIFIER_INFINITE;
        duk_small_int_t digits = 0;
#if defined(DUK_USE_ES6_REGEXP_SYNTAX)
        duk_lexer_point lex_pt;
#endif

#if defined(DUK_USE_ES6_REGEXP_SYNTAX)
        /* Store lexer position, restoring if quantifier is invalid. */
        DUK_LEXER_GETPOINT(lex_ctx, &lex_pt);
#endif

        for (;;) {
            DUK__ADVANCECHARS(lex_ctx, 1);  /* eat '{' on entry */
            x = DUK__L0();
            if (DUK__ISDIGIT(x)) {
                digits++;
                val1 = val1 * 10 + (duk_uint32_t) duk__hexval(x);
            } else if (x == DUK_ASC_COMMA) {
                if (digits > DUK__MAX_RE_QUANT_DIGITS) {
                    goto invalid_quantifier;
                }
                if (val2 != DUK_RE_QUANTIFIER_INFINITE) {
                    goto invalid_quantifier;
                }
                if (DUK__L1() == DUK_ASC_RCURLY) {
                    /* form: { DecimalDigits , }, val1 = min count */
                    if (digits == 0) {
                        goto invalid_quantifier;
                    }
                    out_token->qmin = val1;
                    out_token->qmax = DUK_RE_QUANTIFIER_INFINITE;
                    DUK__ADVANCECHARS(lex_ctx, 2);
                    break;
                }
                val2 = val1;
                val1 = 0;
                digits = 0;  /* not strictly necessary because of lookahead '}' above */
            } else if (x == DUK_ASC_RCURLY) {
                if (digits > DUK__MAX_RE_QUANT_DIGITS) {
                    goto invalid_quantifier;
                }
                if (digits == 0) {
                    goto invalid_quantifier;
                }
                if (val2 != DUK_RE_QUANTIFIER_INFINITE) {
                    /* val2 = min count, val1 = max count */
                    out_token->qmin = val2;
                    out_token->qmax = val1;
                } else {
                    /* val1 = count */
                    out_token->qmin = val1;
                    out_token->qmax = val1;
                }
                DUK__ADVANCECHARS(lex_ctx, 1);
                break;
            } else {
                goto invalid_quantifier;
            }
        }
        if (DUK__L0() == DUK_ASC_QUESTION) {
            out_token->greedy = 0;
            DUK__ADVANCECHARS(lex_ctx, 1);
        } else {
            out_token->greedy = 1;
        }
        advtok = DUK__ADVTOK(0, DUK_RETOK_QUANTIFIER);
        break;
 invalid_quantifier:
#if defined(DUK_USE_ES6_REGEXP_SYNTAX)
        /* Failed to match the quantifier, restore lexer and parse
         * opening brace as a literal.
         */
        DUK_LEXER_SETPOINT(lex_ctx, &lex_pt);
        advtok = DUK__ADVTOK(1, DUK_RETOK_ATOM_CHAR);
        out_token->num = DUK_ASC_LCURLY;
#else
        goto fail_quantifier;
#endif
        break;
    }
    case DUK_ASC_PERIOD: {
        advtok = DUK__ADVTOK(1, DUK_RETOK_ATOM_PERIOD);
        break;
    }
    case DUK_ASC_BACKSLASH: {
        /* The E5.1 specification does not seem to allow IdentifierPart characters
         * to be used as identity escapes.  Unfortunately this includes '$', which
         * cannot be escaped as '\$'; it needs to be escaped e.g. as '\u0024'.
         * Many other implementations (including V8 and Rhino, for instance) do
         * accept '\$' as a valid identity escape, which is quite pragmatic, and
         * ES2015 Annex B relaxes the rules to allow these (and other) real world forms.
         */

        advtok = DUK__ADVTOK(2, DUK_RETOK_ATOM_CHAR);  /* default: char escape (two chars) */
        if (y == DUK_ASC_LC_B) {
            advtok = DUK__ADVTOK(2, DUK_RETOK_ASSERT_WORD_BOUNDARY);
        } else if (y == DUK_ASC_UC_B) {
            advtok = DUK__ADVTOK(2, DUK_RETOK_ASSERT_NOT_WORD_BOUNDARY);
        } else if (y == DUK_ASC_LC_F) {
            out_token->num = 0x000c;
        } else if (y == DUK_ASC_LC_N) {
            out_token->num = 0x000a;
        } else if (y == DUK_ASC_LC_T) {
            out_token->num = 0x0009;
        } else if (y == DUK_ASC_LC_R) {
            out_token->num = 0x000d;
        } else if (y == DUK_ASC_LC_V) {
            out_token->num = 0x000b;
        } else if (y == DUK_ASC_LC_C) {
            x = DUK__L2();
            if ((x >= DUK_ASC_LC_A && x <= DUK_ASC_LC_Z) ||
                (x >= DUK_ASC_UC_A && x <= DUK_ASC_UC_Z)) {
                out_token->num = (duk_uint32_t) (x % 32);
                advtok = DUK__ADVTOK(3, DUK_RETOK_ATOM_CHAR);
            } else {
                goto fail_escape;
            }
        } else if (y == DUK_ASC_LC_X || y == DUK_ASC_LC_U) {
            /* The token value is the Unicode codepoint without
             * it being decode into surrogate pair characters
             * here.  The \u{H+} is only allowed in Unicode mode
             * which we don't support yet.
             */
            out_token->num = (duk_uint32_t) duk__lexer_parse_escape(lex_ctx, 0 /*allow_es6*/);
            advtok = DUK__ADVTOK(0, DUK_RETOK_ATOM_CHAR);
        } else if (y == DUK_ASC_LC_D) {
            advtok = DUK__ADVTOK(2, DUK_RETOK_ATOM_DIGIT);
        } else if (y == DUK_ASC_UC_D) {
            advtok = DUK__ADVTOK(2, DUK_RETOK_ATOM_NOT_DIGIT);
        } else if (y == DUK_ASC_LC_S) {
            advtok = DUK__ADVTOK(2, DUK_RETOK_ATOM_WHITE);
        } else if (y == DUK_ASC_UC_S) {
            advtok = DUK__ADVTOK(2, DUK_RETOK_ATOM_NOT_WHITE);
        } else if (y == DUK_ASC_LC_W) {
            advtok = DUK__ADVTOK(2, DUK_RETOK_ATOM_WORD_CHAR);
        } else if (y == DUK_ASC_UC_W) {
            advtok = DUK__ADVTOK(2, DUK_RETOK_ATOM_NOT_WORD_CHAR);
        } else if (DUK__ISDIGIT(y)) {
            /* E5 Section 15.10.2.11 */
            if (y == DUK_ASC_0) {
                if (DUK__ISDIGIT(DUK__L2())) {
                    goto fail_escape;
                }
                out_token->num = 0x0000;
                advtok = DUK__ADVTOK(2, DUK_RETOK_ATOM_CHAR);
            } else {
                /* XXX: shared parsing? */
                duk_uint32_t val = 0;
                duk_small_int_t i;
                for (i = 0; ; i++) {
                    if (i >= DUK__MAX_RE_DECESC_DIGITS) {
                        goto fail_escape;
                    }
                    DUK__ADVANCECHARS(lex_ctx, 1);  /* eat backslash on entry */
                    x = DUK__L0();
                    if (!DUK__ISDIGIT(x)) {
                        break;
                    }
                    val = val * 10 + (duk_uint32_t) duk__hexval(x);
                }
                /* DUK__L0() cannot be a digit, because the loop doesn't terminate if it is */
                advtok = DUK__ADVTOK(0, DUK_RETOK_ATOM_BACKREFERENCE);
                out_token->num = val;
            }
#if defined(DUK_USE_ES6_REGEXP_SYNTAX)
        } else if (y >= 0) {
            /* For ES2015 Annex B, accept any source character as identity
             * escape except 'c' which is used for control characters.
             * http://www.ecma-international.org/ecma-262/6.0/#sec-regular-expressions-patterns
             * Careful not to match end-of-buffer (<0) here.
             * This is not yet full ES2015 Annex B because cases above
             * (like hex escape) won't backtrack.
             */
            DUK_ASSERT(y != DUK_ASC_LC_C);  /* covered above */
#else  /* DUK_USE_ES6_REGEXP_SYNTAX */
        } else if ((y >= 0 && !duk_unicode_is_identifier_part(y)) ||
                   y == DUK_UNICODE_CP_ZWNJ ||
                   y == DUK_UNICODE_CP_ZWJ) {
            /* For ES5.1 identity escapes are not allowed for identifier
             * parts.  This conflicts with a lot of real world code as this
             * doesn't e.g. allow escaping a dollar sign as /\$/, see
             * test-regexp-identity-escape-dollar.js.
             */
#endif  /* DUK_USE_ES6_REGEXP_SYNTAX */
            out_token->num = (duk_uint32_t) y;
        } else {
            goto fail_escape;
        }
        break;
    }
    case DUK_ASC_LPAREN: {
        /* XXX: naming is inconsistent: ATOM_END_GROUP ends an ASSERT_START_LOOKAHEAD */

        if (y == DUK_ASC_QUESTION) {
            if (DUK__L2() == DUK_ASC_EQUALS) {
                /* (?= */
                advtok = DUK__ADVTOK(3, DUK_RETOK_ASSERT_START_POS_LOOKAHEAD);
            } else if (DUK__L2() == DUK_ASC_EXCLAMATION) {
                /* (?! */
                advtok = DUK__ADVTOK(3, DUK_RETOK_ASSERT_START_NEG_LOOKAHEAD);
            } else if (DUK__L2() == DUK_ASC_COLON) {
                /* (?: */
                advtok = DUK__ADVTOK(3, DUK_RETOK_ATOM_START_NONCAPTURE_GROUP);
            } else {
                goto fail_group;
            }
        } else {
            /* ( */
            advtok = DUK__ADVTOK(1, DUK_RETOK_ATOM_START_CAPTURE_GROUP);
        }
        break;
    }
    case DUK_ASC_RPAREN: {
        advtok = DUK__ADVTOK(1, DUK_RETOK_ATOM_END_GROUP);
        break;
    }
    case DUK_ASC_LBRACKET: {
        /*
         *  To avoid creating a heavy intermediate value for the list of ranges,
         *  only the start token ('[' or '[^') is parsed here.  The regexp
         *  compiler parses the ranges itself.
         */

        /* XXX: with DUK_USE_ES6_REGEXP_SYNTAX we should allow left bracket
         * literal too, but it's not easy to parse without backtracking.
         */

        advtok = DUK__ADVTOK(1, DUK_RETOK_ATOM_START_CHARCLASS);
        if (y == DUK_ASC_CARET) {
            advtok = DUK__ADVTOK(2, DUK_RETOK_ATOM_START_CHARCLASS_INVERTED);
        }
        break;
    }
#if !defined(DUK_USE_ES6_REGEXP_SYNTAX)
    case DUK_ASC_RCURLY:
    case DUK_ASC_RBRACKET: {
        /* Although these could be parsed as PatternCharacters unambiguously (here),
         * E5 Section 15.10.1 grammar explicitly forbids these as PatternCharacters.
         */
        goto fail_invalid_char;
        break;
    }
#endif
    case -1: {
        /* EOF */
        advtok = DUK__ADVTOK(0, DUK_TOK_EOF);
        break;
    }
    default: {
        /* PatternCharacter, all excluded characters are matched by cases above */
        advtok = DUK__ADVTOK(1, DUK_RETOK_ATOM_CHAR);
        out_token->num = (duk_uint32_t) x;
        break;
    }
    }

    /*
     *  Shared exit path
     */

    DUK__ADVANCEBYTES(lex_ctx, advtok >> 8);
    out_token->t = advtok & 0xff;
    return;

 fail_token_limit:
    DUK_ERROR_RANGE(lex_ctx->thr, DUK_STR_TOKEN_LIMIT);
    DUK_WO_NORETURN(return;);

 fail_escape:
    DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_REGEXP_ESCAPE);
    DUK_WO_NORETURN(return;);

 fail_group:
    DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_REGEXP_GROUP);
    DUK_WO_NORETURN(return;);

#if !defined(DUK_USE_ES6_REGEXP_SYNTAX)
 fail_invalid_char:
    DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_REGEXP_CHARACTER);
    DUK_WO_NORETURN(return;);

 fail_quantifier:
    DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_QUANTIFIER);
    DUK_WO_NORETURN(return;);
#endif
}

/*
 *  Special parser for character classes; calls callback for every
 *  range parsed and returns the number of ranges present.
 */

/* XXX: this duplicates functionality in duk_regexp.c where a similar loop is
 * required anyway.  We could use that BUT we need to update the regexp compiler
 * 'nranges' too.  Work this out a bit more cleanly to save space.
 */

/* XXX: the handling of character range detection is a bit convoluted.
 * Try to simplify and make smaller.
 */

/* XXX: logic for handling character ranges is now incorrect, it will accept
 * e.g. [\d-z] whereas it should croak from it?  SMJS accepts this too, though.
 *
 * Needs a read through and a lot of additional tests.
 */

DUK_LOCAL
void duk__emit_u16_direct_ranges(duk_lexer_ctx *lex_ctx,
                                 duk_re_range_callback gen_range,
                                 void *userdata,
                                 const duk_uint16_t *ranges,
                                 duk_small_int_t num) {
    const duk_uint16_t *ranges_end;

    DUK_UNREF(lex_ctx);

    ranges_end = ranges + num;
    while (ranges < ranges_end) {
        /* mark range 'direct', bypass canonicalization (see Wiki) */
        gen_range(userdata, (duk_codepoint_t) ranges[0], (duk_codepoint_t) ranges[1], 1);
        ranges += 2;
    }
}

DUK_INTERNAL void duk_lexer_parse_re_ranges(duk_lexer_ctx *lex_ctx, duk_re_range_callback gen_range, void *userdata) {
    duk_codepoint_t start = -1;
    duk_codepoint_t ch;
    duk_codepoint_t x;
    duk_bool_t dash = 0;
    duk_small_uint_t adv = 0;

    DUK_DD(DUK_DDPRINT("parsing regexp ranges"));

    for (;;) {
        DUK__ADVANCECHARS(lex_ctx, adv);
        adv = 1;

        x = DUK__L0();

        ch = -1;  /* not strictly necessary, but avoids "uninitialized variable" warnings */
        DUK_UNREF(ch);

        if (x < 0) {
            goto fail_unterm_charclass;
        } else if (x == DUK_ASC_RBRACKET) {
            if (start >= 0) {
                gen_range(userdata, start, start, 0);
            }
            DUK__ADVANCECHARS(lex_ctx, 1);  /* eat ']' before finishing */
            break;
        } else if (x == DUK_ASC_MINUS) {
            if (start >= 0 && !dash && DUK__L1() != DUK_ASC_RBRACKET) {
                /* '-' as a range indicator */
                dash = 1;
                continue;
            } else {
                /* '-' verbatim */
                ch = x;
            }
        } else if (x == DUK_ASC_BACKSLASH) {
            /*
             *  The escapes are same as outside a character class, except that \b has a
             *  different meaning, and \B and backreferences are prohibited (see E5
             *  Section 15.10.2.19).  However, it's difficult to share code because we
             *  handle e.g. "\n" very differently: here we generate a single character
             *  range for it.
             */

            /* XXX: ES2015 surrogate pair handling. */

            x = DUK__L1();

            adv = 2;

            if (x == DUK_ASC_LC_B) {
                /* Note: '\b' in char class is different than outside (assertion),
                 * '\B' is not allowed and is caught by the duk_unicode_is_identifier_part()
                 * check below.
                 */
                ch = 0x0008;
            } else if (x == DUK_ASC_LC_F) {
                ch = 0x000c;
            } else if (x == DUK_ASC_LC_N) {
                ch = 0x000a;
            } else if (x == DUK_ASC_LC_T) {
                ch = 0x0009;
            } else if (x == DUK_ASC_LC_R) {
                ch = 0x000d;
            } else if (x == DUK_ASC_LC_V) {
                ch = 0x000b;
            } else if (x == DUK_ASC_LC_C) {
                x = DUK__L2();
                adv = 3;
                if ((x >= DUK_ASC_LC_A && x <= DUK_ASC_LC_Z) ||
                    (x >= DUK_ASC_UC_A && x <= DUK_ASC_UC_Z)) {
                    ch = (x % 32);
                } else {
                    goto fail_escape;
                }
            } else if (x == DUK_ASC_LC_X || x == DUK_ASC_LC_U) {
                /* The \u{H+} form is only allowed in Unicode mode which
                 * we don't support yet.
                 */
                ch = duk__lexer_parse_escape(lex_ctx, 0 /*allow_es6*/);
                adv = 0;
            } else if (x == DUK_ASC_LC_D) {
                duk__emit_u16_direct_ranges(lex_ctx,
                                            gen_range,
                                            userdata,
                                            duk_unicode_re_ranges_digit,
                                            sizeof(duk_unicode_re_ranges_digit) / sizeof(duk_uint16_t));
                ch = -1;
            } else if (x == DUK_ASC_UC_D) {
                duk__emit_u16_direct_ranges(lex_ctx,
                                            gen_range,
                                            userdata,
                                            duk_unicode_re_ranges_not_digit,
                                            sizeof(duk_unicode_re_ranges_not_digit) / sizeof(duk_uint16_t));
                ch = -1;
            } else if (x == DUK_ASC_LC_S) {
                duk__emit_u16_direct_ranges(lex_ctx,
                                            gen_range,
                                            userdata,
                                            duk_unicode_re_ranges_white,
                                            sizeof(duk_unicode_re_ranges_white) / sizeof(duk_uint16_t));
                ch = -1;
            } else if (x == DUK_ASC_UC_S) {
                duk__emit_u16_direct_ranges(lex_ctx,
                                            gen_range,
                                            userdata,
                                            duk_unicode_re_ranges_not_white,
                                            sizeof(duk_unicode_re_ranges_not_white) / sizeof(duk_uint16_t));
                ch = -1;
            } else if (x == DUK_ASC_LC_W) {
                duk__emit_u16_direct_ranges(lex_ctx,
                                            gen_range,
                                            userdata,
                                            duk_unicode_re_ranges_wordchar,
                                            sizeof(duk_unicode_re_ranges_wordchar) / sizeof(duk_uint16_t));
                ch = -1;
            } else if (x == DUK_ASC_UC_W) {
                duk__emit_u16_direct_ranges(lex_ctx,
                                            gen_range,
                                            userdata,
                                            duk_unicode_re_ranges_not_wordchar,
                                            sizeof(duk_unicode_re_ranges_not_wordchar) / sizeof(duk_uint16_t));
                ch = -1;
            } else if (DUK__ISDIGIT(x)) {
                /* DecimalEscape, only \0 is allowed, no leading
                 * zeroes are allowed.
                 *
                 * ES2015 Annex B also allows (maximal match) legacy
                 * octal escapes up to \377 and \8 and \9 are
                 * accepted as literal '8' and '9', also in strict mode.
                 */

#if defined(DUK_USE_ES6_REGEXP_SYNTAX)
                ch = duk__lexer_parse_legacy_octal(lex_ctx, &adv, 0 /*reject_annex_b*/);
                DUK_ASSERT(ch >= 0);  /* no rejections */
#else
                if (x == DUK_ASC_0 && !DUK__ISDIGIT(DUK__L2())) {
                    ch = 0x0000;
                } else {
                    goto fail_escape;
                }
#endif
#if defined(DUK_USE_ES6_REGEXP_SYNTAX)
            } else if (x >= 0) {
                /* IdentityEscape: ES2015 Annex B allows almost all
                 * source characters here.  Match anything except
                 * EOF here.
                 */
                ch = x;
#else  /* DUK_USE_ES6_REGEXP_SYNTAX */
            } else if (!duk_unicode_is_identifier_part(x)) {
                /* IdentityEscape: ES5.1 doesn't allow identity escape
                 * for identifier part characters, which conflicts with
                 * some real world code.  For example, it doesn't allow
                 * /[\$]/ which is awkward.
                 */
                ch = x;
#endif  /* DUK_USE_ES6_REGEXP_SYNTAX */
            } else {
                goto fail_escape;
            }
        } else {
            /* character represents itself */
            ch = x;
        }

        /* ch is a literal character here or -1 if parsed entity was
         * an escape such as "\s".
         */

        if (ch < 0) {
            /* multi-character sets not allowed as part of ranges, see
             * E5 Section 15.10.2.15, abstract operation CharacterRange.
             */
            if (start >= 0) {
                if (dash) {
                    goto fail_range;
                } else {
                    gen_range(userdata, start, start, 0);
                    start = -1;
                    /* dash is already 0 */
                }
            }
        } else {
            if (start >= 0) {
                if (dash) {
                    if (start > ch) {
                        goto fail_range;
                    }
                    gen_range(userdata, start, ch, 0);
                    start = -1;
                    dash = 0;
                } else {
                    gen_range(userdata, start, start, 0);
                    start = ch;
                    /* dash is already 0 */
                }
            } else {
                start = ch;
            }
        }
    }

    return;

 fail_escape:
    DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_REGEXP_ESCAPE);
    DUK_WO_NORETURN(return;);

 fail_range:
    DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_RANGE);
    DUK_WO_NORETURN(return;);

 fail_unterm_charclass:
    DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_UNTERMINATED_CHARCLASS);
    DUK_WO_NORETURN(return;);
}

#endif  /* DUK_USE_REGEXP_SUPPORT */

/* automatic undefs */
#undef DUK__ADVANCEBYTES
#undef DUK__ADVANCECHARS
#undef DUK__ADVTOK
#undef DUK__APPENDBUFFER
#undef DUK__APPENDBUFFER_ASCII
#undef DUK__INITBUFFER
#undef DUK__ISDIGIT
#undef DUK__ISDIGIT03
#undef DUK__ISDIGIT47
#undef DUK__ISHEXDIGIT
#undef DUK__ISOCTDIGIT
#undef DUK__L0
#undef DUK__L1
#undef DUK__L2
#undef DUK__L3
#undef DUK__L4
#undef DUK__L5
#undef DUK__LOOKUP
#undef DUK__MAX_RE_DECESC_DIGITS
#undef DUK__MAX_RE_QUANT_DIGITS
//#line 1 "duk_numconv.c"
/*
 *  Number-to-string and string-to-number conversions.
 *
 *  Slow path number-to-string and string-to-number conversion is based on
 *  a Dragon4 variant, with fast paths for small integers.  Big integer
 *  arithmetic is needed for guaranteeing that the conversion is correct
 *  and uses a minimum number of digits.  The big number arithmetic has a
 *  fixed maximum size and does not require dynamic allocations.
 *
 *  See: doc/number-conversion.rst.
 */

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

#define DUK__IEEE_DOUBLE_EXP_BIAS  1023
#define DUK__IEEE_DOUBLE_EXP_MIN   (-1022)   /* biased exp == 0 -> denormal, exp -1022 */

#define DUK__DIGITCHAR(x)  duk_lc_digits[(x)]

/*
 *  Tables generated with util/gennumdigits.py.
 *
 *  duk__str2num_digits_for_radix indicates, for each radix, how many input
 *  digits should be considered significant for string-to-number conversion.
 *  The input is also padded to this many digits to give the Dragon4
 *  conversion enough (apparent) precision to work with.
 *
 *  duk__str2num_exp_limits indicates, for each radix, the radix-specific
 *  minimum/maximum exponent values (for a Dragon4 integer mantissa)
 *  below and above which the number is guaranteed to underflow to zero
 *  or overflow to Infinity.  This allows parsing to keep bigint values
 *  bounded.
 */

DUK_LOCAL const duk_uint8_t duk__str2num_digits_for_radix[] = {
    69, 44, 35, 30, 27, 25, 23, 22, 20, 20,    /* 2 to 11 */
    20, 19, 19, 18, 18, 17, 17, 17, 16, 16,    /* 12 to 21 */
    16, 16, 16, 15, 15, 15, 15, 15, 15, 14,    /* 22 to 31 */
    14, 14, 14, 14, 14                         /* 31 to 36 */
};

typedef struct {
    duk_int16_t upper;
    duk_int16_t lower;
} duk__exp_limits;

DUK_LOCAL const duk__exp_limits duk__str2num_exp_limits[] = {
    { 957, -1147 }, { 605, -725 },  { 479, -575 },  { 414, -496 },
    { 372, -446 },  { 342, -411 },  { 321, -384 },  { 304, -364 },
    { 291, -346 },  { 279, -334 },  { 268, -323 },  { 260, -312 },
    { 252, -304 },  { 247, -296 },  { 240, -289 },  { 236, -283 },
    { 231, -278 },  { 227, -273 },  { 223, -267 },  { 220, -263 },
    { 216, -260 },  { 213, -256 },  { 210, -253 },  { 208, -249 },
    { 205, -246 },  { 203, -244 },  { 201, -241 },  { 198, -239 },
    { 196, -237 },  { 195, -234 },  { 193, -232 },  { 191, -230 },
    { 190, -228 },  { 188, -226 },  { 187, -225 },
};

/*
 *  Limited functionality bigint implementation.
 *
 *  Restricted to non-negative numbers with less than 32 * DUK__BI_MAX_PARTS bits,
 *  with the caller responsible for ensuring this is never exceeded.  No memory
 *  allocation (except stack) is needed for bigint computation.  Operations
 *  have been tailored for number conversion needs.
 *
 *  Argument order is "assignment order", i.e. target first, then arguments:
 *  x <- y * z  -->  duk__bi_mul(x, y, z);
 */

/* This upper value has been experimentally determined; debug build will check
 * bigint size with assertions.
 */
#define DUK__BI_MAX_PARTS  37  /* 37x32 = 1184 bits */

#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)
#define DUK__BI_PRINT(name,x)  duk__bi_print((name),(x))
#else
#define DUK__BI_PRINT(name,x)
#endif

/* Current size is about 152 bytes. */
typedef struct {
    duk_small_int_t n;
    duk_uint32_t v[DUK__BI_MAX_PARTS];  /* low to high */
} duk__bigint;

#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)
DUK_LOCAL void duk__bi_print(const char *name, duk__bigint *x) {
    /* Overestimate required size; debug code so not critical to be tight. */
    char buf[DUK__BI_MAX_PARTS * 9 + 64];
    char *p = buf;
    duk_small_int_t i;

    /* No NUL term checks in this debug code. */
    p += DUK_SPRINTF(p, "%p n=%ld", (void *) x, (long) x->n);
    if (x->n == 0) {
        p += DUK_SPRINTF(p, " 0");
    }
    for (i = x->n - 1; i >= 0; i--) {
        p += DUK_SPRINTF(p, " %08lx", (unsigned long) x->v[i]);
    }

    DUK_DDD(DUK_DDDPRINT("%s: %s", (const char *) name, (const char *) buf));
}
#endif

#if defined(DUK_USE_ASSERTIONS)
DUK_LOCAL duk_small_int_t duk__bi_is_valid(duk__bigint *x) {
    return (duk_small_int_t)
           ( ((x->n >= 0) && (x->n <= DUK__BI_MAX_PARTS)) /* is valid size */ &&
             ((x->n == 0) || (x->v[x->n - 1] != 0)) /* is normalized */ );
}
#endif

DUK_LOCAL void duk__bi_normalize(duk__bigint *x) {
    duk_small_int_t i;

    for (i = x->n - 1; i >= 0; i--) {
        if (x->v[i] != 0) {
            break;
        }
    }

    /* Note: if 'x' is zero, x->n becomes 0 here */
    x->n = i + 1;
    DUK_ASSERT(duk__bi_is_valid(x));
}

/* x <- y */
DUK_LOCAL void duk__bi_copy(duk__bigint *x, duk__bigint *y) {
    duk_small_int_t n;

    n = y->n;
    x->n = n;
    /* No need to special case n == 0. */
    duk_memcpy((void *) x->v, (const void *) y->v, (size_t) (sizeof(duk_uint32_t) * (size_t) n));
}

DUK_LOCAL void duk__bi_set_small(duk__bigint *x, duk_uint32_t v) {
    if (v == 0U) {
        x->n = 0;
    } else {
        x->n = 1;
        x->v[0] = v;
    }
    DUK_ASSERT(duk__bi_is_valid(x));
}

/* Return value: <0  <=>  x < y
 *                0  <=>  x == y
 *               >0  <=>  x > y
 */
DUK_LOCAL int duk__bi_compare(duk__bigint *x, duk__bigint *y) {
    duk_small_int_t i, nx, ny;
    duk_uint32_t tx, ty;

    DUK_ASSERT(duk__bi_is_valid(x));
    DUK_ASSERT(duk__bi_is_valid(y));

    nx = x->n;
    ny = y->n;
    if (nx > ny) {
        goto ret_gt;
    }
    if (nx < ny) {
        goto ret_lt;
    }
    for (i = nx - 1; i >= 0; i--) {
        tx = x->v[i];
        ty = y->v[i];

        if (tx > ty) {
            goto ret_gt;
        }
        if (tx < ty) {
            goto ret_lt;
        }
    }

    return 0;

 ret_gt:
    return 1;

 ret_lt:
    return -1;
}

/* x <- y + z */
#if defined(DUK_USE_64BIT_OPS)
DUK_LOCAL void duk__bi_add(duk__bigint *x, duk__bigint *y, duk__bigint *z) {
    duk_uint64_t tmp;
    duk_small_int_t i, ny, nz;

    DUK_ASSERT(duk__bi_is_valid(y));
    DUK_ASSERT(duk__bi_is_valid(z));

    if (z->n > y->n) {
        duk__bigint *t;
        t = y; y = z; z = t;
    }
    DUK_ASSERT(y->n >= z->n);

    ny = y->n; nz = z->n;
    tmp = 0U;
    for (i = 0; i < ny; i++) {
        DUK_ASSERT(i < DUK__BI_MAX_PARTS);
        tmp += y->v[i];
        if (i < nz) {
            tmp += z->v[i];
        }
        x->v[i] = (duk_uint32_t) (tmp & 0xffffffffUL);
        tmp = tmp >> 32;
    }
    if (tmp != 0U) {
        DUK_ASSERT(i < DUK__BI_MAX_PARTS);
        x->v[i++] = (duk_uint32_t) tmp;
    }
    x->n = i;
    DUK_ASSERT(x->n <= DUK__BI_MAX_PARTS);

    /* no need to normalize */
    DUK_ASSERT(duk__bi_is_valid(x));
}
#else  /* DUK_USE_64BIT_OPS */
DUK_LOCAL void duk__bi_add(duk__bigint *x, duk__bigint *y, duk__bigint *z) {
    duk_uint32_t carry, tmp1, tmp2;
    duk_small_int_t i, ny, nz;

    DUK_ASSERT(duk__bi_is_valid(y));
    DUK_ASSERT(duk__bi_is_valid(z));

    if (z->n > y->n) {
        duk__bigint *t;
        t = y; y = z; z = t;
    }
    DUK_ASSERT(y->n >= z->n);

    ny = y->n; nz = z->n;
    carry = 0U;
    for (i = 0; i < ny; i++) {
        /* Carry is detected based on wrapping which relies on exact 32-bit
         * types.
         */
        DUK_ASSERT(i < DUK__BI_MAX_PARTS);
        tmp1 = y->v[i];
        tmp2 = tmp1;
        if (i < nz) {
            tmp2 += z->v[i];
        }

        /* Careful with carry condition:
         *  - If carry not added: 0x12345678 + 0 + 0xffffffff = 0x12345677 (< 0x12345678)
         *  - If carry added:     0x12345678 + 1 + 0xffffffff = 0x12345678 (== 0x12345678)
         */
        if (carry) {
            tmp2++;
            carry = (tmp2 <= tmp1 ? 1U : 0U);
        } else {
            carry = (tmp2 < tmp1 ? 1U : 0U);
        }

        x->v[i] = tmp2;
    }
    if (carry) {
        DUK_ASSERT(i < DUK__BI_MAX_PARTS);
        DUK_ASSERT(carry == 1U);
        x->v[i++] = carry;
    }
    x->n = i;
    DUK_ASSERT(x->n <= DUK__BI_MAX_PARTS);

    /* no need to normalize */
    DUK_ASSERT(duk__bi_is_valid(x));
}
#endif  /* DUK_USE_64BIT_OPS */

/* x <- y + z */
DUK_LOCAL void duk__bi_add_small(duk__bigint *x, duk__bigint *y, duk_uint32_t z) {
    duk__bigint tmp;

    DUK_ASSERT(duk__bi_is_valid(y));

    /* XXX: this could be optimized; there is only one call site now though */
    duk__bi_set_small(&tmp, z);
    duk__bi_add(x, y, &tmp);

    DUK_ASSERT(duk__bi_is_valid(x));
}

#if 0  /* unused */
/* x <- x + y, use t as temp */
DUK_LOCAL void duk__bi_add_copy(duk__bigint *x, duk__bigint *y, duk__bigint *t) {
    duk__bi_add(t, x, y);
    duk__bi_copy(x, t);
}
#endif

/* x <- y - z, require x >= y => z >= 0, i.e. y >= z */
#if defined(DUK_USE_64BIT_OPS)
DUK_LOCAL void duk__bi_sub(duk__bigint *x, duk__bigint *y, duk__bigint *z) {
    duk_small_int_t i, ny, nz;
    duk_uint32_t ty, tz;
    duk_int64_t tmp;

    DUK_ASSERT(duk__bi_is_valid(y));
    DUK_ASSERT(duk__bi_is_valid(z));
    DUK_ASSERT(duk__bi_compare(y, z) >= 0);
    DUK_ASSERT(y->n >= z->n);

    ny = y->n; nz = z->n;
    tmp = 0;
    for (i = 0; i < ny; i++) {
        ty = y->v[i];
        if (i < nz) {
            tz = z->v[i];
        } else {
            tz = 0;
        }
        tmp = (duk_int64_t) ty - (duk_int64_t) tz + tmp;
        x->v[i] = (duk_uint32_t) ((duk_uint64_t) tmp & 0xffffffffUL);
        tmp = tmp >> 32;  /* 0 or -1 */
    }
    DUK_ASSERT(tmp == 0);

    x->n = i;
    duk__bi_normalize(x);  /* need to normalize, may even cancel to 0 */
    DUK_ASSERT(duk__bi_is_valid(x));
}
#else
DUK_LOCAL void duk__bi_sub(duk__bigint *x, duk__bigint *y, duk__bigint *z) {
    duk_small_int_t i, ny, nz;
    duk_uint32_t tmp1, tmp2, borrow;

    DUK_ASSERT(duk__bi_is_valid(y));
    DUK_ASSERT(duk__bi_is_valid(z));
    DUK_ASSERT(duk__bi_compare(y, z) >= 0);
    DUK_ASSERT(y->n >= z->n);

    ny = y->n; nz = z->n;
    borrow = 0U;
    for (i = 0; i < ny; i++) {
        /* Borrow is detected based on wrapping which relies on exact 32-bit
         * types.
         */
        tmp1 = y->v[i];
        tmp2 = tmp1;
        if (i < nz) {
            tmp2 -= z->v[i];
        }

        /* Careful with borrow condition:
         *  - If borrow not subtracted: 0x12345678 - 0 - 0xffffffff = 0x12345679 (> 0x12345678)
         *  - If borrow subtracted:     0x12345678 - 1 - 0xffffffff = 0x12345678 (== 0x12345678)
         */
        if (borrow) {
            tmp2--;
            borrow = (tmp2 >= tmp1 ? 1U : 0U);
        } else {
            borrow = (tmp2 > tmp1 ? 1U : 0U);
        }

        x->v[i] = tmp2;
    }
    DUK_ASSERT(borrow == 0U);

    x->n = i;
    duk__bi_normalize(x);  /* need to normalize, may even cancel to 0 */
    DUK_ASSERT(duk__bi_is_valid(x));
}
#endif

#if 0  /* unused */
/* x <- y - z */
DUK_LOCAL void duk__bi_sub_small(duk__bigint *x, duk__bigint *y, duk_uint32_t z) {
    duk__bigint tmp;

    DUK_ASSERT(duk__bi_is_valid(y));

    /* XXX: this could be optimized */
    duk__bi_set_small(&tmp, z);
    duk__bi_sub(x, y, &tmp);

    DUK_ASSERT(duk__bi_is_valid(x));
}
#endif

/* x <- x - y, use t as temp */
DUK_LOCAL void duk__bi_sub_copy(duk__bigint *x, duk__bigint *y, duk__bigint *t) {
    duk__bi_sub(t, x, y);
    duk__bi_copy(x, t);
}

/* x <- y * z */
DUK_LOCAL void duk__bi_mul(duk__bigint *x, duk__bigint *y, duk__bigint *z) {
    duk_small_int_t i, j, nx, nz;

    DUK_ASSERT(duk__bi_is_valid(y));
    DUK_ASSERT(duk__bi_is_valid(z));

    nx = y->n + z->n;  /* max possible */
    DUK_ASSERT(nx <= DUK__BI_MAX_PARTS);

    if (nx == 0) {
        /* Both inputs are zero; cases where only one is zero can go
         * through main algorithm.
         */
        x->n = 0;
        return;
    }

    duk_memzero((void *) x->v, (size_t) (sizeof(duk_uint32_t) * (size_t) nx));
    x->n = nx;

    nz = z->n;
    for (i = 0; i < y->n; i++) {
#if defined(DUK_USE_64BIT_OPS)
        duk_uint64_t tmp = 0U;
        for (j = 0; j < nz; j++) {
            tmp += (duk_uint64_t) y->v[i] * (duk_uint64_t) z->v[j] + x->v[i+j];
            x->v[i+j] = (duk_uint32_t) (tmp & 0xffffffffUL);
            tmp = tmp >> 32;
        }
        if (tmp > 0) {
            DUK_ASSERT(i + j < nx);
            DUK_ASSERT(i + j < DUK__BI_MAX_PARTS);
            DUK_ASSERT(x->v[i+j] == 0U);
            x->v[i+j] = (duk_uint32_t) tmp;
        }
#else
        /*
         *  Multiply + add + carry for 32-bit components using only 16x16->32
         *  multiplies and carry detection based on unsigned overflow.
         *
         *    1st mult, 32-bit: (A*2^16 + B)
         *    2nd mult, 32-bit: (C*2^16 + D)
         *    3rd add, 32-bit: E
         *    4th add, 32-bit: F
         *
         *      (AC*2^16 + B) * (C*2^16 + D) + E + F
         *    = AC*2^32 + AD*2^16 + BC*2^16 + BD + E + F
         *    = AC*2^32 + (AD + BC)*2^16 + (BD + E + F)
         *    = AC*2^32 + AD*2^16 + BC*2^16 + (BD + E + F)
         */
        duk_uint32_t a, b, c, d, e, f;
        duk_uint32_t r, s, t;

        a = y->v[i]; b = a & 0xffffUL; a = a >> 16;

        f = 0;
        for (j = 0; j < nz; j++) {
            c = z->v[j]; d = c & 0xffffUL; c = c >> 16;
            e = x->v[i+j];

            /* build result as: (r << 32) + s: start with (BD + E + F) */
            r = 0;
            s = b * d;

            /* add E */
            t = s + e;
            if (t < s) { r++; }  /* carry */
            s = t;

            /* add F */
            t = s + f;
            if (t < s) { r++; }  /* carry */
            s = t;

            /* add BC*2^16 */
            t = b * c;
            r += (t >> 16);
            t = s + ((t & 0xffffUL) << 16);
            if (t < s) { r++; }  /* carry */
            s = t;

            /* add AD*2^16 */
            t = a * d;
            r += (t >> 16);
            t = s + ((t & 0xffffUL) << 16);
            if (t < s) { r++; }  /* carry */
            s = t;

            /* add AC*2^32 */
            t = a * c;
            r += t;

            DUK_DDD(DUK_DDDPRINT("ab=%08lx cd=%08lx ef=%08lx -> rs=%08lx %08lx",
                                 (unsigned long) y->v[i], (unsigned long) z->v[j],
                                 (unsigned long) x->v[i+j], (unsigned long) r,
                                 (unsigned long) s));

            x->v[i+j] = s;
            f = r;
        }
        if (f > 0U) {
            DUK_ASSERT(i + j < nx);
            DUK_ASSERT(i + j < DUK__BI_MAX_PARTS);
            DUK_ASSERT(x->v[i+j] == 0U);
            x->v[i+j] = (duk_uint32_t) f;
        }
#endif  /* DUK_USE_64BIT_OPS */
    }

    duk__bi_normalize(x);
    DUK_ASSERT(duk__bi_is_valid(x));
}

/* x <- y * z */
DUK_LOCAL void duk__bi_mul_small(duk__bigint *x, duk__bigint *y, duk_uint32_t z) {
    duk__bigint tmp;

    DUK_ASSERT(duk__bi_is_valid(y));

    /* XXX: this could be optimized */
    duk__bi_set_small(&tmp, z);
    duk__bi_mul(x, y, &tmp);

    DUK_ASSERT(duk__bi_is_valid(x));
}

/* x <- x * y, use t as temp */
DUK_LOCAL void duk__bi_mul_copy(duk__bigint *x, duk__bigint *y, duk__bigint *t) {
    duk__bi_mul(t, x, y);
    duk__bi_copy(x, t);
}

/* x <- x * y, use t as temp */
DUK_LOCAL void duk__bi_mul_small_copy(duk__bigint *x, duk_uint32_t y, duk__bigint *t) {
    duk__bi_mul_small(t, x, y);
    duk__bi_copy(x, t);
}

DUK_LOCAL int duk__bi_is_even(duk__bigint *x) {
    DUK_ASSERT(duk__bi_is_valid(x));
    return (x->n == 0) || ((x->v[0] & 0x01) == 0);
}

DUK_LOCAL int duk__bi_is_zero(duk__bigint *x) {
    DUK_ASSERT(duk__bi_is_valid(x));
    return (x->n == 0);  /* this is the case for normalized numbers */
}

/* Bigint is 2^52.  Used to detect normalized IEEE double mantissa values
 * which are at the lowest edge (next floating point value downwards has
 * a different exponent).  The lowest mantissa has the form:
 *
 *     1000........000    (52 zeroes; only "hidden bit" is set)
 */
DUK_LOCAL duk_small_int_t duk__bi_is_2to52(duk__bigint *x) {
    DUK_ASSERT(duk__bi_is_valid(x));
    return (duk_small_int_t)
            (x->n == 2) && (x->v[0] == 0U) && (x->v[1] == (1U << (52-32)));
}

/* x <- (1<<y) */
DUK_LOCAL void duk__bi_twoexp(duk__bigint *x, duk_small_int_t y) {
    duk_small_int_t n, r;

    n = (y / 32) + 1;
    DUK_ASSERT(n > 0);
    r = y % 32;
    duk_memzero((void *) x->v, sizeof(duk_uint32_t) * (size_t) n);
    x->n = n;
    x->v[n - 1] = (((duk_uint32_t) 1) << r);
}

/* x <- b^y; use t1 and t2 as temps */
DUK_LOCAL void duk__bi_exp_small(duk__bigint *x, duk_small_int_t b, duk_small_int_t y, duk__bigint *t1, duk__bigint *t2) {
    /* Fast path the binary case */

    DUK_ASSERT(x != t1 && x != t2 && t1 != t2);  /* distinct bignums, easy mistake to make */
    DUK_ASSERT(b >= 0);
    DUK_ASSERT(y >= 0);

    if (b == 2) {
        duk__bi_twoexp(x, y);
        return;
    }

    /* http://en.wikipedia.org/wiki/Exponentiation_by_squaring */

    DUK_DDD(DUK_DDDPRINT("exp_small: b=%ld, y=%ld", (long) b, (long) y));

    duk__bi_set_small(x, 1);
    duk__bi_set_small(t1, (duk_uint32_t) b);
    for (;;) {
        /* Loop structure ensures that we don't compute t1^2 unnecessarily
         * on the final round, as that might create a bignum exceeding the
         * current DUK__BI_MAX_PARTS limit.
         */
        if (y & 0x01) {
            duk__bi_mul_copy(x, t1, t2);
        }
        y = y >> 1;
        if (y == 0) {
            break;
        }
        duk__bi_mul_copy(t1, t1, t2);
    }

    DUK__BI_PRINT("exp_small result", x);
}

/*
 *  A Dragon4 number-to-string variant, based on:
 *
 *    Guy L. Steele Jr., Jon L. White: "How to Print Floating-Point Numbers
 *    Accurately"
 *
 *    Robert G. Burger, R. Kent Dybvig: "Printing Floating-Point Numbers
 *    Quickly and Accurately"
 *
 *  The current algorithm is based on Figure 1 of the Burger-Dybvig paper,
 *  i.e. the base implementation without logarithm estimation speedups
 *  (these would increase code footprint considerably).  Fixed-format output
 *  does not follow the suggestions in the paper; instead, we generate an
 *  extra digit and round-with-carry.
 *
 *  The same algorithm is used for number parsing (with b=10 and B=2)
 *  by generating one extra digit and doing rounding manually.
 *
 *  See doc/number-conversion.rst for limitations.
 */

/* Maximum number of digits generated. */
#define DUK__MAX_OUTPUT_DIGITS          1040  /* (Number.MAX_VALUE).toString(2).length == 1024, + slack */

/* Maximum number of characters in formatted value. */
#define DUK__MAX_FORMATTED_LENGTH       1040  /* (-Number.MAX_VALUE).toString(2).length == 1025, + slack */

/* Number and (minimum) size of bigints in the nc_ctx structure. */
#define DUK__NUMCONV_CTX_NUM_BIGINTS    7
#define DUK__NUMCONV_CTX_BIGINTS_SIZE   (sizeof(duk__bigint) * DUK__NUMCONV_CTX_NUM_BIGINTS)

typedef struct {
    /* Currently about 7*152 = 1064 bytes.  The space for these
     * duk__bigints is used also as a temporary buffer for generating
     * the final string.  This is a bit awkard; a union would be
     * more correct.
     */
    duk__bigint f, r, s, mp, mm, t1, t2;

    duk_small_int_t is_s2n;        /* if 1, doing a string-to-number; else doing a number-to-string */
    duk_small_int_t is_fixed;      /* if 1, doing a fixed format output (not free format) */
    duk_small_int_t req_digits;    /* requested number of output digits; 0 = free-format */
    duk_small_int_t abs_pos;       /* digit position is absolute, not relative */
    duk_small_int_t e;             /* exponent for 'f' */
    duk_small_int_t b;             /* input radix */
    duk_small_int_t B;             /* output radix */
    duk_small_int_t k;             /* see algorithm */
    duk_small_int_t low_ok;        /* see algorithm */
    duk_small_int_t high_ok;       /* see algorithm */
    duk_small_int_t unequal_gaps;  /* m+ != m- (very rarely) */

    /* Buffer used for generated digits, values are in the range [0,B-1]. */
    duk_uint8_t digits[DUK__MAX_OUTPUT_DIGITS];
    duk_small_int_t count;  /* digit count */
} duk__numconv_stringify_ctx;

/* Note: computes with 'idx' in assertions, so caller beware.
 * 'idx' is preincremented, i.e. '1' on first call, because it
 * is more convenient for the caller.
 */
#define DUK__DRAGON4_OUTPUT_PREINC(nc_ctx,preinc_idx,x)  do { \
        DUK_ASSERT((preinc_idx) - 1 >= 0); \
        DUK_ASSERT((preinc_idx) - 1 < DUK__MAX_OUTPUT_DIGITS); \
        ((nc_ctx)->digits[(preinc_idx) - 1]) = (duk_uint8_t) (x); \
    } while (0)

DUK_LOCAL duk_size_t duk__dragon4_format_uint32(duk_uint8_t *buf, duk_uint32_t x, duk_small_int_t radix) {
    duk_uint8_t *p;
    duk_size_t len;
    duk_small_int_t dig;
    duk_uint32_t t;

    DUK_ASSERT(buf != NULL);
    DUK_ASSERT(radix >= 2 && radix <= 36);

    /* A 32-bit unsigned integer formats to at most 32 digits (the
     * worst case happens with radix == 2).  Output the digits backwards,
     * and use a memmove() to get them in the right place.
     */

    p = buf + 32;
    for (;;) {
        t = x / (duk_uint32_t) radix;
        dig = (duk_small_int_t) (x - t * (duk_uint32_t) radix);
        x = t;

        DUK_ASSERT(dig >= 0 && dig < 36);
        *(--p) = DUK__DIGITCHAR(dig);

        if (x == 0) {
            break;
        }
    }
    len = (duk_size_t) ((buf + 32) - p);

    duk_memmove((void *) buf, (const void *) p, (size_t) len);

    return len;
}

DUK_LOCAL void duk__dragon4_prepare(duk__numconv_stringify_ctx *nc_ctx) {
    duk_small_int_t lowest_mantissa;

#if 1
    /* Assume IEEE round-to-even, so that shorter encoding can be used
     * when round-to-even would produce correct result.  By removing
     * this check (and having low_ok == high_ok == 0) the results would
     * still be accurate but in some cases longer than necessary.
     */
    if (duk__bi_is_even(&nc_ctx->f)) {
        DUK_DDD(DUK_DDDPRINT("f is even"));
        nc_ctx->low_ok = 1;
        nc_ctx->high_ok = 1;
    } else {
        DUK_DDD(DUK_DDDPRINT("f is odd"));
        nc_ctx->low_ok = 0;
        nc_ctx->high_ok = 0;
    }
#else
    /* Note: not honoring round-to-even should work but now generates incorrect
     * results.  For instance, 1e23 serializes to "a000...", i.e. the first digit
     * equals the radix (10).  Scaling stops one step too early in this case.
     * Don't know why this is the case, but since this code path is unused, it
     * doesn't matter.
     */
    nc_ctx->low_ok = 0;
    nc_ctx->high_ok = 0;
#endif

    /* For string-to-number, pretend we never have the lowest mantissa as there
     * is no natural "precision" for inputs.  Having lowest_mantissa == 0, we'll
     * fall into the base cases for both e >= 0 and e < 0.
     */
    if (nc_ctx->is_s2n) {
        lowest_mantissa = 0;
    } else {
        lowest_mantissa = duk__bi_is_2to52(&nc_ctx->f);
    }

    nc_ctx->unequal_gaps = 0;
    if (nc_ctx->e >= 0) {
        /* exponent non-negative (and thus not minimum exponent) */

        if (lowest_mantissa) {
            /* (>= e 0) AND (= f (expt b (- p 1)))
             *
             * be <- (expt b e) == b^e
             * be1 <- (* be b) == (expt b (+ e 1)) == b^(e+1)
             * r <- (* f be1 2) == 2 * f * b^(e+1)    [if b==2 -> f * b^(e+2)]
             * s <- (* b 2)                           [if b==2 -> 4]
             * m+ <- be1 == b^(e+1)
             * m- <- be == b^e
             * k <- 0
             * B <- B
             * low_ok <- round
             * high_ok <- round
             */

            DUK_DDD(DUK_DDDPRINT("non-negative exponent (not smallest exponent); "
                                 "lowest mantissa value for this exponent -> "
                                 "unequal gaps"));

            duk__bi_exp_small(&nc_ctx->mm, nc_ctx->b, nc_ctx->e, &nc_ctx->t1, &nc_ctx->t2);  /* mm <- b^e */
            duk__bi_mul_small(&nc_ctx->mp, &nc_ctx->mm, (duk_uint32_t) nc_ctx->b);           /* mp <- b^(e+1) */
            duk__bi_mul_small(&nc_ctx->t1, &nc_ctx->f, 2);
            duk__bi_mul(&nc_ctx->r, &nc_ctx->t1, &nc_ctx->mp);              /* r <- (2 * f) * b^(e+1) */
            duk__bi_set_small(&nc_ctx->s, (duk_uint32_t) (nc_ctx->b * 2));  /* s <- 2 * b */
            nc_ctx->unequal_gaps = 1;
        } else {
            /* (>= e 0) AND (not (= f (expt b (- p 1))))
             *
             * be <- (expt b e) == b^e
             * r <- (* f be 2) == 2 * f * b^e    [if b==2 -> f * b^(e+1)]
             * s <- 2
             * m+ <- be == b^e
             * m- <- be == b^e
             * k <- 0
             * B <- B
             * low_ok <- round
             * high_ok <- round
             */

            DUK_DDD(DUK_DDDPRINT("non-negative exponent (not smallest exponent); "
                                 "not lowest mantissa for this exponent -> "
                                 "equal gaps"));

            duk__bi_exp_small(&nc_ctx->mm, nc_ctx->b, nc_ctx->e, &nc_ctx->t1, &nc_ctx->t2);  /* mm <- b^e */
            duk__bi_copy(&nc_ctx->mp, &nc_ctx->mm);                /* mp <- b^e */
            duk__bi_mul_small(&nc_ctx->t1, &nc_ctx->f, 2);
            duk__bi_mul(&nc_ctx->r, &nc_ctx->t1, &nc_ctx->mp);     /* r <- (2 * f) * b^e */
            duk__bi_set_small(&nc_ctx->s, 2);                      /* s <- 2 */
        }
    } else {
        /* When doing string-to-number, lowest_mantissa is always 0 so
         * the exponent check, while incorrect, won't matter.
         */
        if (nc_ctx->e > DUK__IEEE_DOUBLE_EXP_MIN /*not minimum exponent*/ &&
            lowest_mantissa /* lowest mantissa for this exponent*/) {
            /* r <- (* f b 2)                                [if b==2 -> (* f 4)]
             * s <- (* (expt b (- 1 e)) 2) == b^(1-e) * 2    [if b==2 -> b^(2-e)]
             * m+ <- b == 2
             * m- <- 1
             * k <- 0
             * B <- B
             * low_ok <- round
             * high_ok <- round
             */

            DUK_DDD(DUK_DDDPRINT("negative exponent; not minimum exponent and "
                                 "lowest mantissa for this exponent -> "
                                 "unequal gaps"));

            duk__bi_mul_small(&nc_ctx->r, &nc_ctx->f, (duk_uint32_t) (nc_ctx->b * 2));  /* r <- (2 * b) * f */
            duk__bi_exp_small(&nc_ctx->t1, nc_ctx->b, 1 - nc_ctx->e, &nc_ctx->s, &nc_ctx->t2);  /* NB: use 's' as temp on purpose */
            duk__bi_mul_small(&nc_ctx->s, &nc_ctx->t1, 2);             /* s <- b^(1-e) * 2 */
            duk__bi_set_small(&nc_ctx->mp, 2);
            duk__bi_set_small(&nc_ctx->mm, 1);
            nc_ctx->unequal_gaps = 1;
        } else {
            /* r <- (* f 2)
             * s <- (* (expt b (- e)) 2) == b^(-e) * 2    [if b==2 -> b^(1-e)]
             * m+ <- 1
             * m- <- 1
             * k <- 0
             * B <- B
             * low_ok <- round
             * high_ok <- round
             */

            DUK_DDD(DUK_DDDPRINT("negative exponent; minimum exponent or not "
                                 "lowest mantissa for this exponent -> "
                                 "equal gaps"));

            duk__bi_mul_small(&nc_ctx->r, &nc_ctx->f, 2);            /* r <- 2 * f */
            duk__bi_exp_small(&nc_ctx->t1, nc_ctx->b, -nc_ctx->e, &nc_ctx->s, &nc_ctx->t2);  /* NB: use 's' as temp on purpose */
            duk__bi_mul_small(&nc_ctx->s, &nc_ctx->t1, 2);           /* s <- b^(-e) * 2 */
            duk__bi_set_small(&nc_ctx->mp, 1);
            duk__bi_set_small(&nc_ctx->mm, 1);
        }
    }
}

DUK_LOCAL void duk__dragon4_scale(duk__numconv_stringify_ctx *nc_ctx) {
    duk_small_int_t k = 0;

    /* This is essentially the 'scale' algorithm, with recursion removed.
     * Note that 'k' is either correct immediately, or will move in one
     * direction in the loop.  There's no need to do the low/high checks
     * on every round (like the Scheme algorithm does).
     *
     * The scheme algorithm finds 'k' and updates 's' simultaneously,
     * while the logical algorithm finds 'k' with 's' having its initial
     * value, after which 's' is updated separately (see the Burger-Dybvig
     * paper, Section 3.1, steps 2 and 3).
     *
     * The case where m+ == m- (almost always) is optimized for, because
     * it reduces the bigint operations considerably and almost always
     * applies.  The scale loop only needs to work with m+, so this works.
     */

    /* XXX: this algorithm could be optimized quite a lot by using e.g.
     * a logarithm based estimator for 'k' and performing B^n multiplication
     * using a lookup table or using some bit-representation based exp
     * algorithm.  Currently we just loop, with significant performance
     * impact for very large and very small numbers.
     */

    DUK_DDD(DUK_DDDPRINT("scale: B=%ld, low_ok=%ld, high_ok=%ld",
                         (long) nc_ctx->B, (long) nc_ctx->low_ok, (long) nc_ctx->high_ok));
    DUK__BI_PRINT("r(init)", &nc_ctx->r);
    DUK__BI_PRINT("s(init)", &nc_ctx->s);
    DUK__BI_PRINT("mp(init)", &nc_ctx->mp);
    DUK__BI_PRINT("mm(init)", &nc_ctx->mm);

    for (;;) {
        DUK_DDD(DUK_DDDPRINT("scale loop (inc k), k=%ld", (long) k));
        DUK__BI_PRINT("r", &nc_ctx->r);
        DUK__BI_PRINT("s", &nc_ctx->s);
        DUK__BI_PRINT("m+", &nc_ctx->mp);
        DUK__BI_PRINT("m-", &nc_ctx->mm);

        duk__bi_add(&nc_ctx->t1, &nc_ctx->r, &nc_ctx->mp);  /* t1 = (+ r m+) */
        if (duk__bi_compare(&nc_ctx->t1, &nc_ctx->s) >= (nc_ctx->high_ok ? 0 : 1)) {
            DUK_DDD(DUK_DDDPRINT("k is too low"));
            /* r <- r
             * s <- (* s B)
             * m+ <- m+
             * m- <- m-
             * k <- (+ k 1)
             */

            duk__bi_mul_small_copy(&nc_ctx->s, (duk_uint32_t) nc_ctx->B, &nc_ctx->t1);
            k++;
        } else {
            break;
        }
    }

    /* k > 0 -> k was too low, and cannot be too high */
    if (k > 0) {
        goto skip_dec_k;
    }

    for (;;) {
        DUK_DDD(DUK_DDDPRINT("scale loop (dec k), k=%ld", (long) k));
        DUK__BI_PRINT("r", &nc_ctx->r);
        DUK__BI_PRINT("s", &nc_ctx->s);
        DUK__BI_PRINT("m+", &nc_ctx->mp);
        DUK__BI_PRINT("m-", &nc_ctx->mm);

        duk__bi_add(&nc_ctx->t1, &nc_ctx->r, &nc_ctx->mp);  /* t1 = (+ r m+) */
        duk__bi_mul_small(&nc_ctx->t2, &nc_ctx->t1, (duk_uint32_t) nc_ctx->B);   /* t2 = (* (+ r m+) B) */
        if (duk__bi_compare(&nc_ctx->t2, &nc_ctx->s) <= (nc_ctx->high_ok ? -1 : 0)) {
            DUK_DDD(DUK_DDDPRINT("k is too high"));
            /* r <- (* r B)
             * s <- s
             * m+ <- (* m+ B)
             * m- <- (* m- B)
             * k <- (- k 1)
             */
            duk__bi_mul_small_copy(&nc_ctx->r, (duk_uint32_t) nc_ctx->B, &nc_ctx->t1);
            duk__bi_mul_small_copy(&nc_ctx->mp, (duk_uint32_t) nc_ctx->B, &nc_ctx->t1);
            if (nc_ctx->unequal_gaps) {
                DUK_DDD(DUK_DDDPRINT("m+ != m- -> need to update m- too"));
                duk__bi_mul_small_copy(&nc_ctx->mm, (duk_uint32_t) nc_ctx->B, &nc_ctx->t1);
            }
            k--;
        } else {
            break;
        }
    }

 skip_dec_k:

    if (!nc_ctx->unequal_gaps) {
        DUK_DDD(DUK_DDDPRINT("equal gaps, copy m- from m+"));
        duk__bi_copy(&nc_ctx->mm, &nc_ctx->mp);  /* mm <- mp */
    }
    nc_ctx->k = k;

    DUK_DDD(DUK_DDDPRINT("final k: %ld", (long) k));
    DUK__BI_PRINT("r(final)", &nc_ctx->r);
    DUK__BI_PRINT("s(final)", &nc_ctx->s);
    DUK__BI_PRINT("mp(final)", &nc_ctx->mp);
    DUK__BI_PRINT("mm(final)", &nc_ctx->mm);
}

DUK_LOCAL void duk__dragon4_generate(duk__numconv_stringify_ctx *nc_ctx) {
    duk_small_int_t tc1, tc2;    /* terminating conditions */
    duk_small_int_t d;           /* current digit */
    duk_small_int_t count = 0;   /* digit count */

    /*
     *  Digit generation loop.
     *
     *  Different termination conditions:
     *
     *    1. Free format output.  Terminate when shortest accurate
     *       representation found.
     *
     *    2. Fixed format output, with specific number of digits.
     *       Ignore termination conditions, terminate when digits
     *       generated.  Caller requests an extra digit and rounds.
     *
     *    3. Fixed format output, with a specific absolute cut-off
     *       position (e.g. 10 digits after decimal point).  Note
     *       that we always generate at least one digit, even if
     *       the digit is below the cut-off point already.
     */

    for (;;) {
        DUK_DDD(DUK_DDDPRINT("generate loop, count=%ld, k=%ld, B=%ld, low_ok=%ld, high_ok=%ld",
                             (long) count, (long) nc_ctx->k, (long) nc_ctx->B,
                             (long) nc_ctx->low_ok, (long) nc_ctx->high_ok));
        DUK__BI_PRINT("r", &nc_ctx->r);
        DUK__BI_PRINT("s", &nc_ctx->s);
        DUK__BI_PRINT("m+", &nc_ctx->mp);
        DUK__BI_PRINT("m-", &nc_ctx->mm);

        /* (quotient-remainder (* r B) s) using a dummy subtraction loop */
        duk__bi_mul_small(&nc_ctx->t1, &nc_ctx->r, (duk_uint32_t) nc_ctx->B);       /* t1 <- (* r B) */
        d = 0;
        for (;;) {
            if (duk__bi_compare(&nc_ctx->t1, &nc_ctx->s) < 0) {
                break;
            }
            duk__bi_sub_copy(&nc_ctx->t1, &nc_ctx->s, &nc_ctx->t2);  /* t1 <- t1 - s */
            d++;
        }
        duk__bi_copy(&nc_ctx->r, &nc_ctx->t1);  /* r <- (remainder (* r B) s) */
                                                /* d <- (quotient (* r B) s)   (in range 0...B-1) */
        DUK_DDD(DUK_DDDPRINT("-> d(quot)=%ld", (long) d));
        DUK__BI_PRINT("r(rem)", &nc_ctx->r);

        duk__bi_mul_small_copy(&nc_ctx->mp, (duk_uint32_t) nc_ctx->B, &nc_ctx->t2); /* m+ <- (* m+ B) */
        duk__bi_mul_small_copy(&nc_ctx->mm, (duk_uint32_t) nc_ctx->B, &nc_ctx->t2); /* m- <- (* m- B) */
        DUK__BI_PRINT("mp(upd)", &nc_ctx->mp);
        DUK__BI_PRINT("mm(upd)", &nc_ctx->mm);

        /* Terminating conditions.  For fixed width output, we just ignore the
         * terminating conditions (and pretend that tc1 == tc2 == false).  The
         * the current shortcut for fixed-format output is to generate a few
         * extra digits and use rounding (with carry) to finish the output.
         */

        if (nc_ctx->is_fixed == 0) {
            /* free-form */
            tc1 = (duk__bi_compare(&nc_ctx->r, &nc_ctx->mm) <= (nc_ctx->low_ok ? 0 : -1));

            duk__bi_add(&nc_ctx->t1, &nc_ctx->r, &nc_ctx->mp);  /* t1 <- (+ r m+) */
            tc2 = (duk__bi_compare(&nc_ctx->t1, &nc_ctx->s) >= (nc_ctx->high_ok ? 0 : 1));

            DUK_DDD(DUK_DDDPRINT("tc1=%ld, tc2=%ld", (long) tc1, (long) tc2));
        } else {
            /* fixed-format */
            tc1 = 0;
            tc2 = 0;
        }

        /* Count is incremented before DUK__DRAGON4_OUTPUT_PREINC() call
         * on purpose, which is taken into account by the macro.
         */
        count++;

        if (tc1) {
            if (tc2) {
                /* tc1 = true, tc2 = true */
                duk__bi_mul_small(&nc_ctx->t1, &nc_ctx->r, 2);
                if (duk__bi_compare(&nc_ctx->t1, &nc_ctx->s) < 0) {  /* (< (* r 2) s) */
                    DUK_DDD(DUK_DDDPRINT("tc1=true, tc2=true, 2r > s: output d --> %ld (k=%ld)",
                                         (long) d, (long) nc_ctx->k));
                    DUK__DRAGON4_OUTPUT_PREINC(nc_ctx, count, d);
                } else {
                    DUK_DDD(DUK_DDDPRINT("tc1=true, tc2=true, 2r <= s: output d+1 --> %ld (k=%ld)",
                                         (long) (d + 1), (long) nc_ctx->k));
                    DUK__DRAGON4_OUTPUT_PREINC(nc_ctx, count, d + 1);
                }
                break;
            } else {
                /* tc1 = true, tc2 = false */
                DUK_DDD(DUK_DDDPRINT("tc1=true, tc2=false: output d --> %ld (k=%ld)",
                                     (long) d, (long) nc_ctx->k));
                DUK__DRAGON4_OUTPUT_PREINC(nc_ctx, count, d);
                break;
            }
        } else {
            if (tc2) {
                /* tc1 = false, tc2 = true */
                DUK_DDD(DUK_DDDPRINT("tc1=false, tc2=true: output d+1 --> %ld (k=%ld)",
                                     (long) (d + 1), (long) nc_ctx->k));
                DUK__DRAGON4_OUTPUT_PREINC(nc_ctx, count, d + 1);
                break;
            } else {
                /* tc1 = false, tc2 = false */
                DUK_DDD(DUK_DDDPRINT("tc1=false, tc2=false: output d --> %ld (k=%ld)",
                                     (long) d, (long) nc_ctx->k));
                DUK__DRAGON4_OUTPUT_PREINC(nc_ctx, count, d);

                /* r <- r    (updated above: r <- (remainder (* r B) s)
                 * s <- s
                 * m+ <- m+  (updated above: m+ <- (* m+ B)
                 * m- <- m-  (updated above: m- <- (* m- B)
                 * B, low_ok, high_ok are fixed
                 */

                /* fall through and continue for-loop */
            }
        }

        /* fixed-format termination conditions */
        if (nc_ctx->is_fixed) {
            if (nc_ctx->abs_pos) {
                int pos = nc_ctx->k - count + 1;  /* count is already incremented, take into account */
                DUK_DDD(DUK_DDDPRINT("fixed format, absolute: abs pos=%ld, k=%ld, count=%ld, req=%ld",
                                     (long) pos, (long) nc_ctx->k, (long) count, (long) nc_ctx->req_digits));
                if (pos <= nc_ctx->req_digits) {
                    DUK_DDD(DUK_DDDPRINT("digit position reached req_digits, end generate loop"));
                    break;
                }
            } else {
                DUK_DDD(DUK_DDDPRINT("fixed format, relative: k=%ld, count=%ld, req=%ld",
                                     (long) nc_ctx->k, (long) count, (long) nc_ctx->req_digits));
                if (count >= nc_ctx->req_digits) {
                    DUK_DDD(DUK_DDDPRINT("digit count reached req_digits, end generate loop"));
                    break;
                }
            }
        }
    }  /* for */

    nc_ctx->count = count;

    DUK_DDD(DUK_DDDPRINT("generate finished"));

#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)
    {
        duk_uint8_t buf[2048];
        duk_small_int_t i, t;
        duk_memzero(buf, sizeof(buf));
        for (i = 0; i < nc_ctx->count; i++) {
            t = nc_ctx->digits[i];
            if (t < 0 || t > 36) {
                buf[i] = (duk_uint8_t) '?';
            } else {
                buf[i] = (duk_uint8_t) DUK__DIGITCHAR(t);
            }
        }
        DUK_DDD(DUK_DDDPRINT("-> generated digits; k=%ld, digits='%s'",
                             (long) nc_ctx->k, (const char *) buf));
    }
#endif
}

/* Round up digits to a given position.  If position is out-of-bounds,
 * does nothing.  If carry propagates over the first digit, a '1' is
 * prepended to digits and 'k' will be updated.  Return value indicates
 * whether carry propagated over the first digit.
 *
 * Note that nc_ctx->count is NOT updated based on the rounding position
 * (it is updated only if carry overflows over the first digit and an
 * extra digit is prepended).
 */
DUK_LOCAL duk_small_int_t duk__dragon4_fixed_format_round(duk__numconv_stringify_ctx *nc_ctx, duk_small_int_t round_idx) {
    duk_small_int_t t;
    duk_uint8_t *p;
    duk_uint8_t roundup_limit;
    duk_small_int_t ret = 0;

    /*
     *  round_idx points to the digit which is considered for rounding; the
     *  digit to its left is the final digit of the rounded value.  If round_idx
     *  is zero, rounding will be performed; the result will either be an empty
     *  rounded value or if carry happens a '1' digit is generated.
     */

    if (round_idx >= nc_ctx->count) {
        DUK_DDD(DUK_DDDPRINT("round_idx out of bounds (%ld >= %ld (count)) -> no rounding",
                             (long) round_idx, (long) nc_ctx->count));
        return 0;
    } else if (round_idx < 0) {
        DUK_DDD(DUK_DDDPRINT("round_idx out of bounds (%ld < 0) -> no rounding",
                             (long) round_idx));
        return 0;
    }

    /*
     *  Round-up limit.
     *
     *  For even values, divides evenly, e.g. 10 -> roundup_limit=5.
     *
     *  For odd values, rounds up, e.g. 3 -> roundup_limit=2.
     *  If radix is 3, 0/3 -> down, 1/3 -> down, 2/3 -> up.
     */
    roundup_limit = (duk_uint8_t) ((nc_ctx->B + 1) / 2);

    p = &nc_ctx->digits[round_idx];
    if (*p >= roundup_limit) {
        DUK_DDD(DUK_DDDPRINT("fixed-format rounding carry required"));
        /* carry */
        for (;;) {
            *p = 0;
            if (p == &nc_ctx->digits[0]) {
                DUK_DDD(DUK_DDDPRINT("carry propagated to first digit -> special case handling"));
                duk_memmove((void *) (&nc_ctx->digits[1]),
                            (const void *) (&nc_ctx->digits[0]),
                            (size_t) (sizeof(char) * (size_t) nc_ctx->count));
                nc_ctx->digits[0] = 1;  /* don't increase 'count' */
                nc_ctx->k++;  /* position of highest digit changed */
                nc_ctx->count++;  /* number of digits changed */
                ret = 1;
                break;
            }

            DUK_DDD(DUK_DDDPRINT("fixed-format rounding carry: B=%ld, roundup_limit=%ld, p=%p, digits=%p",
                                 (long) nc_ctx->B, (long) roundup_limit, (void *) p, (void *) nc_ctx->digits));
            p--;
            t = *p;
            DUK_DDD(DUK_DDDPRINT("digit before carry: %ld", (long) t));
            if (++t < nc_ctx->B) {
                DUK_DDD(DUK_DDDPRINT("rounding carry terminated"));
                *p = (duk_uint8_t) t;
                break;
            }

            DUK_DDD(DUK_DDDPRINT("wraps, carry to next digit"));
        }
    }

    return ret;
}

#define DUK__NO_EXP  (65536)  /* arbitrary marker, outside valid exp range */

DUK_LOCAL void duk__dragon4_convert_and_push(duk__numconv_stringify_ctx *nc_ctx,
                                             duk_hthread *thr,
                                             duk_small_int_t radix,
                                             duk_small_int_t digits,
                                             duk_small_uint_t flags,
                                             duk_small_int_t neg) {
    duk_small_int_t k;
    duk_small_int_t pos, pos_end;
    duk_small_int_t expt;
    duk_small_int_t dig;
    duk_uint8_t *q;
    duk_uint8_t *buf;

    /*
     *  The string conversion here incorporates all the necessary ECMAScript
     *  semantics without attempting to be generic.  nc_ctx->digits contains
     *  nc_ctx->count digits (>= 1), with the topmost digit's 'position'
     *  indicated by nc_ctx->k as follows:
     *
     *    digits="123" count=3 k=0   -->   0.123
     *    digits="123" count=3 k=1   -->   1.23
     *    digits="123" count=3 k=5   -->   12300
     *    digits="123" count=3 k=-1  -->   0.0123
     *
     *  Note that the identifier names used for format selection are different
     *  in Burger-Dybvig paper and ECMAScript specification (quite confusingly
     *  so, because e.g. 'k' has a totally different meaning in each).  See
     *  documentation for discussion.
     *
     *  ECMAScript doesn't specify any specific behavior for format selection
     *  (e.g. when to use exponent notation) for non-base-10 numbers.
     *
     *  The bigint space in the context is reused for string output, as there
     *  is more than enough space for that (>1kB at the moment), and we avoid
     *  allocating even more stack.
     */

    DUK_ASSERT(DUK__NUMCONV_CTX_BIGINTS_SIZE >= DUK__MAX_FORMATTED_LENGTH);
    DUK_ASSERT(nc_ctx->count >= 1);

    k = nc_ctx->k;
    buf = (duk_uint8_t *) &nc_ctx->f;  /* XXX: union would be more correct */
    q = buf;

    /* Exponent handling: if exponent format is used, record exponent value and
     * fake k such that one leading digit is generated (e.g. digits=123 -> "1.23").
     *
     * toFixed() prevents exponent use; otherwise apply a set of criteria to
     * match the other API calls (toString(), toPrecision, etc).
     */

    expt = DUK__NO_EXP;
    if (!nc_ctx->abs_pos /* toFixed() */) {
        if ((flags & DUK_N2S_FLAG_FORCE_EXP) ||             /* exponential notation forced */
            ((flags & DUK_N2S_FLAG_NO_ZERO_PAD) &&          /* fixed precision and zero padding would be required */
                 (k - digits >= 1)) ||                          /* (e.g. k=3, digits=2 -> "12X") */
            ((k > 21 || k <= -6) && (radix == 10))) {       /* toString() conditions */
            DUK_DDD(DUK_DDDPRINT("use exponential notation: k=%ld -> expt=%ld",
                                 (long) k, (long) (k - 1)));
            expt = k - 1;  /* e.g. 12.3 -> digits="123" k=2 -> 1.23e1 */
            k = 1;  /* generate mantissa with a single leading whole number digit */
        }
    }

    if (neg) {
        *q++ = '-';
    }

    /* Start position (inclusive) and end position (exclusive) */
    pos = (k >= 1 ? k : 1);
    if (nc_ctx->is_fixed) {
        if (nc_ctx->abs_pos) {
            /* toFixed() */
            pos_end = -digits;
        } else {
            pos_end = k - digits;
        }
    } else {
        pos_end = k - nc_ctx->count;
    }
    if (pos_end > 0) {
        pos_end = 0;
    }

    DUK_DDD(DUK_DDDPRINT("expt=%ld, k=%ld, count=%ld, pos=%ld, pos_end=%ld, is_fixed=%ld, "
                         "digits=%ld, abs_pos=%ld",
                         (long) expt, (long) k, (long) nc_ctx->count, (long) pos, (long) pos_end,
                         (long) nc_ctx->is_fixed, (long) digits, (long) nc_ctx->abs_pos));

    /* Digit generation */
    while (pos > pos_end) {
        DUK_DDD(DUK_DDDPRINT("digit generation: pos=%ld, pos_end=%ld",
                             (long) pos, (long) pos_end));
        if (pos == 0) {
            *q++ = (duk_uint8_t) '.';
        }
        if (pos > k) {
            *q++ = (duk_uint8_t) '0';
        } else if (pos <= k - nc_ctx->count) {
            *q++ = (duk_uint8_t) '0';
        } else {
            dig = nc_ctx->digits[k - pos];
            DUK_ASSERT(dig >= 0 && dig < nc_ctx->B);
            *q++ = (duk_uint8_t) DUK__DIGITCHAR(dig);
        }

        pos--;
    }
    DUK_ASSERT(pos <= 1);

    /* Exponent */
    if (expt != DUK__NO_EXP) {
        /*
         *  Exponent notation for non-base-10 numbers isn't specified in ECMAScript
         *  specification, as it never explicitly turns up: non-decimal numbers can
         *  only be formatted with Number.prototype.toString([radix]) and for that,
         *  behavior is not explicitly specified.
         *
         *  Logical choices include formatting the exponent as decimal (e.g. binary
         *  100000 as 1e+5) or in current radix (e.g. binary 100000 as 1e+101).
         *  The Dragon4 algorithm (in the original paper) prints the exponent value
         *  in the target radix B.  However, for radix values 15 and above, the
         *  exponent separator 'e' is no longer easily parseable.  Consider, for
         *  instance, the number "1.faecee+1c".
         */

        duk_size_t len;
        char expt_sign;

        *q++ = 'e';
        if (expt >= 0) {
            expt_sign = '+';
        } else {
            expt_sign = '-';
            expt = -expt;
        }
        *q++ = (duk_uint8_t) expt_sign;
        len = duk__dragon4_format_uint32(q, (duk_uint32_t) expt, radix);
        q += len;
    }

    duk_push_lstring(thr, (const char *) buf, (size_t) (q - buf));
}

/*
 *  Conversion helpers
 */

DUK_LOCAL void duk__dragon4_double_to_ctx(duk__numconv_stringify_ctx *nc_ctx, duk_double_t x) {
    duk_double_union u;
    duk_uint32_t tmp;
    duk_small_int_t expt;

    /*
     *    seeeeeee eeeeffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
     *       A        B        C        D        E        F        G        H
     *
     *    s       sign bit
     *    eee...  exponent field
     *    fff...  fraction
     *
     *    ieee value = 1.ffff... * 2^(e - 1023)  (normal)
     *               = 0.ffff... * 2^(-1022)     (denormal)
     *
     *    algorithm v = f * b^e
     */

    DUK_DBLUNION_SET_DOUBLE(&u, x);

    nc_ctx->f.n = 2;

    tmp = DUK_DBLUNION_GET_LOW32(&u);
    nc_ctx->f.v[0] = tmp;
    tmp = DUK_DBLUNION_GET_HIGH32(&u);
    nc_ctx->f.v[1] = tmp & 0x000fffffUL;
    expt = (duk_small_int_t) ((tmp >> 20) & 0x07ffUL);

    if (expt == 0) {
        /* denormal */
        expt = DUK__IEEE_DOUBLE_EXP_MIN - 52;
        duk__bi_normalize(&nc_ctx->f);
    } else {
        /* normal: implicit leading 1-bit */
        nc_ctx->f.v[1] |= 0x00100000UL;
        expt = expt - DUK__IEEE_DOUBLE_EXP_BIAS - 52;
        DUK_ASSERT(duk__bi_is_valid(&nc_ctx->f));  /* true, because v[1] has at least one bit set */
    }

    DUK_ASSERT(duk__bi_is_valid(&nc_ctx->f));

    nc_ctx->e = expt;
}

DUK_LOCAL void duk__dragon4_ctx_to_double(duk__numconv_stringify_ctx *nc_ctx, duk_double_t *x) {
    duk_double_union u;
    duk_small_int_t expt;
    duk_small_int_t i;
    duk_small_int_t bitstart;
    duk_small_int_t bitround;
    duk_small_int_t bitidx;
    duk_small_int_t skip_round;
    duk_uint32_t t, v;

    DUK_ASSERT(nc_ctx->count == 53 + 1);

    /* Sometimes this assert is not true right now; it will be true after
     * rounding.  See: test-bug-numconv-mantissa-assert.js.
     */
    DUK_ASSERT_DISABLE(nc_ctx->digits[0] == 1);  /* zero handled by caller */

    /* Should not be required because the code below always sets both high
     * and low parts, but at least gcc-4.4.5 fails to deduce this correctly
     * (perhaps because the low part is set (seemingly) conditionally in a
     * loop), so this is here to avoid the bogus warning.
     */
    duk_memzero((void *) &u, sizeof(u));

    /*
     *  Figure out how generated digits match up with the mantissa,
     *  and then perform rounding.  If mantissa overflows, need to
     *  recompute the exponent (it is bumped and may overflow to
     *  infinity).
     *
     *  For normal numbers the leading '1' is hidden and ignored,
     *  and the last bit is used for rounding:
     *
     *                          rounding pt
     *       <--------52------->|
     *     1 x x x x ... x x x x|y  ==>  x x x x ... x x x x
     *
     *  For denormals, the leading '1' is included in the number,
     *  and the rounding point is different:
     *
     *                      rounding pt
     *     <--52 or less--->|
     *     1 x x x x ... x x|x x y  ==>  0 0 ... 1 x x ... x x
     *
     *  The largest denormals will have a mantissa beginning with
     *  a '1' (the explicit leading bit); smaller denormals will
     *  have leading zero bits.
     *
     *  If the exponent would become too high, the result becomes
     *  Infinity.  If the exponent is so small that the entire
     *  mantissa becomes zero, the result becomes zero.
     *
     *  Note: the Dragon4 'k' is off-by-one with respect to the IEEE
     *  exponent.  For instance, k==0 indicates that the leading '1'
     *  digit is at the first binary fraction position (0.1xxx...);
     *  the corresponding IEEE exponent would be -1.
     */

    skip_round = 0;

 recheck_exp:

    expt = nc_ctx->k - 1;   /* IEEE exp without bias */
    if (expt > 1023) {
        /* Infinity */
        bitstart = -255;  /* needed for inf: causes mantissa to become zero,
                           * and rounding to be skipped.
                           */
        expt = 2047;
    } else if (expt >= -1022) {
        /* normal */
        bitstart = 1;  /* skip leading digit */
        expt += DUK__IEEE_DOUBLE_EXP_BIAS;
        DUK_ASSERT(expt >= 1 && expt <= 2046);
    } else {
        /* denormal or zero */
        bitstart = 1023 + expt;  /* expt==-1023 -> bitstart=0 (leading 1);
                                  * expt==-1024 -> bitstart=-1 (one left of leading 1), etc
                                  */
        expt = 0;
    }
    bitround = bitstart + 52;

    DUK_DDD(DUK_DDDPRINT("ieee expt=%ld, bitstart=%ld, bitround=%ld",
                         (long) expt, (long) bitstart, (long) bitround));

    if (!skip_round) {
        if (duk__dragon4_fixed_format_round(nc_ctx, bitround)) {
            /* Corner case: see test-numconv-parse-mant-carry.js.  We could
             * just bump the exponent and update bitstart, but it's more robust
             * to recompute (but avoid rounding twice).
             */
            DUK_DDD(DUK_DDDPRINT("rounding caused exponent to be bumped, recheck exponent"));
            skip_round = 1;
            goto recheck_exp;
        }
    }

    /*
     *  Create mantissa
     */

    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_LOCAL DUK_NOINLINE void duk__numconv_stringify_raw(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 (duk_double_equals((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").
     */

    /* Would be nice to bulk clear the allocation, but the context
     * is 1-2 kilobytes and nothing should rely on it being zeroed.
     */
#if 0
    duk_memzero((void *) nc_ctx, sizeof(*nc_ctx));  /* slow init, do only for slow path cases */
#endif

    nc_ctx->is_s2n = 0;
    nc_ctx->b = 2;
    nc_ctx->B = radix;
    nc_ctx->abs_pos = 0;
    if (flags & DUK_N2S_FLAG_FIXED_FORMAT) {
        nc_ctx->is_fixed = 1;
        if (flags & DUK_N2S_FLAG_FRACTION_DIGITS) {
            /* absolute req_digits; e.g. digits = 1 -> last digit is 0,
             * but add an extra digit for rounding.
             */
            nc_ctx->abs_pos = 1;
            nc_ctx->req_digits = (-digits + 1) - 1;
        } else {
            nc_ctx->req_digits = digits + 1;
        }
    } else {
        nc_ctx->is_fixed = 0;
        nc_ctx->req_digits = 0;
    }

    if (c == DUK_FP_ZERO) {
        /* Zero special case: fake requested number of zero digits; ensure
         * no sign bit is printed.  Relative and absolute fixed format
         * require separate handling.
         */
        duk_small_int_t count;
        if (nc_ctx->is_fixed) {
            if (nc_ctx->abs_pos) {
                count = digits + 2;  /* lead zero + 'digits' fractions + 1 for rounding */
            } else {
                count = digits + 1;  /* + 1 for rounding */
            }
        } else {
            count = 1;
        }
        DUK_DDD(DUK_DDDPRINT("count=%ld", (long) count));
        DUK_ASSERT(count >= 1);
        duk_memzero((void *) nc_ctx->digits, (size_t) count);
        nc_ctx->count = count;
        nc_ctx->k = 1;  /* 0.000... */
        neg = 0;
        goto zero_skip;
    }

    duk__dragon4_double_to_ctx(nc_ctx, x);   /* -> sets 'f' and 'e' */
    DUK__BI_PRINT("f", &nc_ctx->f);
    DUK_DDD(DUK_DDDPRINT("e=%ld", (long) nc_ctx->e));

    /*
     *  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);
}

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_native_stack_check(thr);
    duk__numconv_stringify_raw(thr, radix, digits, flags);
}

/*
 *  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_LOCAL DUK_NOINLINE void duk__numconv_parse_raw(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
             * than radix 10, so this is not an ambiguous check (e.g. hex
             * exponent values may have 'e' either as a significand digit
             * or as an exponent separator).
             *
             * If the exponent separator occurs twice, 'e' will be interpreted
             * as a digit (= 14) and will be rejected as an invalid decimal
             * digit.
             */

            DUK_DDD(DUK_DDDPRINT("start exponent part"));

            /* Exponent without a sign or with a +/- sign is accepted
             * by all call sites (even JSON.parse()).
             */
            ch = *p;
            if (ch == (duk_small_int_t) '-') {
                expt_neg = 1;
                p++;
            } else if (ch == (duk_small_int_t) '+') {
                p++;
            }
            dig_expt = 0;
            continue;
        } else if (ch >= (duk_small_int_t) 'a' && ch <= (duk_small_int_t) 'z') {
            dig = (duk_small_int_t) (ch - (duk_small_int_t) 'a' + 0x0a);
        } else if (ch >= (duk_small_int_t) 'A' && ch <= (duk_small_int_t) 'Z') {
            dig = (duk_small_int_t) (ch - (duk_small_int_t) 'A' + 0x0a);
        } else {
            dig = 255;  /* triggers garbage digit check below */
        }
        DUK_ASSERT((dig >= 0 && dig <= 35) || dig == 255);

        if (dig >= radix) {
            if (flags & DUK_S2N_FLAG_ALLOW_GARBAGE) {
                DUK_DDD(DUK_DDDPRINT("garbage termination"));
                break;
            } else {
                DUK_DDD(DUK_DDDPRINT("parse failed: trailing garbage or invalid digit"));
                goto parse_fail;
            }
        }

        if (dig_expt < 0) {
            /* whole or fraction digit */

            if (dig_prec < duk__str2num_digits_for_radix[radix - 2]) {
                /* significant from precision perspective */

                duk_small_int_t f_zero = duk__bi_is_zero(&nc_ctx->f);
                if (f_zero && dig == 0) {
                    /* Leading zero is not counted towards precision digits; not
                     * in the integer part, nor in the fraction part.
                     */
                    if (dig_frac < 0) {
                        dig_lzero++;
                    }
                } else {
                    /* XXX: join these ops (multiply-accumulate), but only if
                     * code footprint decreases.
                     */
                    duk__bi_mul_small(&nc_ctx->t1, &nc_ctx->f, (duk_uint32_t) radix);
                    duk__bi_add_small(&nc_ctx->f, &nc_ctx->t1, (duk_uint32_t) dig);
                    dig_prec++;
                }
            } else {
                /* Ignore digits beyond a radix-specific limit, but note them
                 * in expt_adj.
                 */
                expt_adj++;
            }

            if (dig_frac >= 0) {
                dig_frac++;
                expt_adj--;
            } else {
                dig_whole++;
            }
        } else {
            /* exponent digit */

            DUK_ASSERT(radix == 10);
            expt = expt * radix + dig;
            if (expt > DUK_S2N_MAX_EXPONENT) {
                /* Impose a reasonable exponent limit, so that exp
                 * doesn't need to get tracked using a bigint.
                 */
                DUK_DDD(DUK_DDDPRINT("parse failed: exponent too large"));
                goto parse_explimit_error;
            }
            dig_expt++;
        }
    }

    /* Leading zero. */

    if (dig_lzero > 0 && dig_whole > 1) {
        if ((flags & DUK_S2N_FLAG_ALLOW_LEADING_ZERO) == 0) {
            DUK_DDD(DUK_DDDPRINT("parse failed: leading zeroes not allowed in integer part"));
            goto parse_fail;
        }
    }

    /* Validity checks for various fraction formats ("0.1", ".1", "1.", "."). */

    if (dig_whole == 0) {
        if (dig_frac == 0) {
            /* "." is not accepted in any format */
            DUK_DDD(DUK_DDDPRINT("parse failed: plain period without leading or trailing digits"));
            goto parse_fail;
        } else if (dig_frac > 0) {
            /* ".123" */
            if ((flags & DUK_S2N_FLAG_ALLOW_NAKED_FRAC) == 0) {
                DUK_DDD(DUK_DDDPRINT("parse failed: fraction part not allowed without "
                                     "leading integer digit(s)"));
                goto parse_fail;
            }
        } else {
            /* Empty ("") is allowed in some formats (e.g. Number(''), as zero,
             * but it must not have a leading +/- sign (GH-2019).  Note that
             * for Number(), h_str is already trimmed so we can check for zero
             * length and still get Number('  +  ') == NaN.
             */
            if ((flags & DUK_S2N_FLAG_ALLOW_EMPTY_AS_ZERO) == 0) {
                DUK_DDD(DUK_DDDPRINT("parse failed: empty string not allowed (as zero)"));
                goto parse_fail;
            } else if (DUK_HSTRING_GET_BYTELEN(h_str) != 0) {
                DUK_DDD(DUK_DDDPRINT("parse failed: no digits, but not empty (had a +/- sign)"));
                goto parse_fail;
            }
        }
    } else {
        if (dig_frac == 0) {
            /* "123." is allowed in some formats */
            if ((flags & DUK_S2N_FLAG_ALLOW_EMPTY_FRAC) == 0) {
                DUK_DDD(DUK_DDDPRINT("parse failed: empty fractions"));
                goto parse_fail;
            }
        } else if (dig_frac > 0) {
            /* "123.456" */
            ;
        } else {
            /* "123" */
            ;
        }
    }

    /* Exponent without digits (e.g. "1e" or "1e+").  If trailing garbage is
     * allowed, ignore exponent part as garbage (= parse as "1", i.e. exp 0).
     */

    if (dig_expt == 0) {
        if ((flags & DUK_S2N_FLAG_ALLOW_GARBAGE) == 0) {
            DUK_DDD(DUK_DDDPRINT("parse failed: empty exponent"));
            goto parse_fail;
        }
        DUK_ASSERT(expt == 0);
    }

    if (expt_neg) {
        expt = -expt;
    }
    DUK_DDD(DUK_DDDPRINT("expt=%ld, expt_adj=%ld, net exponent -> %ld",
                         (long) expt, (long) expt_adj, (long) (expt + expt_adj)));
    expt += expt_adj;

    /* Fast path check. */

    if (nc_ctx->f.n <= 1 &&   /* 32-bit value */
        expt == 0    /* no net exponent */) {
        /* Fast path is triggered for no exponent and also for balanced exponent
         * and fraction parts, e.g. for "1.23e2" == "123".  Remember to respect
         * zero sign.
         */

        /* XXX: could accept numbers larger than 32 bits, e.g. up to 53 bits? */
        DUK_DDD(DUK_DDDPRINT("fast path number parse"));
        if (nc_ctx->f.n == 1) {
            res = (double) nc_ctx->f.v[0];
        } else {
            res = 0.0;
        }
        goto negcheck_and_ret;
    }

    /* Significand ('f') padding. */

    while (dig_prec < duk__str2num_digits_for_radix[radix - 2]) {
        /* Pad significand with "virtual" zero digits so that Dragon4 will
         * have enough (apparent) precision to work with.
         */
        DUK_DDD(DUK_DDDPRINT("dig_prec=%ld, pad significand with zero", (long) dig_prec));
        duk__bi_mul_small_copy(&nc_ctx->f, (duk_uint32_t) radix, &nc_ctx->t1);
        DUK__BI_PRINT("f", &nc_ctx->f);
        expt--;
        dig_prec++;
    }

    DUK_DDD(DUK_DDDPRINT("final exponent: %ld", (long) expt));

    /* Detect zero special case. */

    if (nc_ctx->f.n == 0) {
        /* This may happen even after the fast path check, if exponent is
         * not balanced (e.g. "0e1").  Remember to respect zero sign.
         */
        DUK_DDD(DUK_DDDPRINT("significand is zero"));
        res = 0.0;
        goto negcheck_and_ret;
    }


    /* Quick reject of too large or too small exponents.  This check
     * would be incorrect for zero (e.g. "0e1000" is zero, not Infinity)
     * so zero check must be above.
     */

    explim = &duk__str2num_exp_limits[radix - 2];
    if (expt > explim->upper) {
        DUK_DDD(DUK_DDDPRINT("exponent too large -> infinite"));
        res = (duk_double_t) DUK_DOUBLE_INFINITY;
        goto negcheck_and_ret;
    } else if (expt < explim->lower) {
        DUK_DDD(DUK_DDDPRINT("exponent too small -> zero"));
        res = (duk_double_t) 0.0;
        goto negcheck_and_ret;
    }

    nc_ctx->is_s2n = 1;
    nc_ctx->e = expt;
    nc_ctx->b = radix;
    nc_ctx->B = 2;
    nc_ctx->is_fixed = 1;
    nc_ctx->abs_pos = 0;
    nc_ctx->req_digits = 53 + 1;

    DUK__BI_PRINT("f", &nc_ctx->f);
    DUK_DDD(DUK_DDDPRINT("e=%ld", (long) nc_ctx->e));

    /*
     *  Dragon4 slow path (binary) digit generation.
     *  An extra digit is generated for rounding.
     */

    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);

    DUK_ASSERT(nc_ctx->count == 53 + 1);

    /*
     *  Convert binary digits into an IEEE double.  Need to handle
     *  denormals and rounding correctly.
     *
     *  Some call sites currently assume the result is always a
     *  non-fastint double.  If this is changed, check all call
     *  sites.
     */

    duk__dragon4_ctx_to_double(nc_ctx, &res);
    goto negcheck_and_ret;

 negcheck_and_ret:
    if (neg) {
        res = -res;
    }
    duk_pop(thr);
    duk_push_number(thr, (double) res);
    DUK_DDD(DUK_DDDPRINT("result: %!T", (duk_tval *) duk_get_tval(thr, -1)));
    return;

 parse_fail:
    DUK_DDD(DUK_DDDPRINT("parse failed"));
    duk_pop(thr);
    duk_push_nan(thr);
    return;

 parse_explimit_error:
    DUK_DDD(DUK_DDDPRINT("parse failed, internal error, can't return a value"));
    DUK_ERROR_RANGE(thr, "exponent too large");
    DUK_WO_NORETURN(return;);
}

DUK_INTERNAL void duk_numconv_parse(duk_hthread *thr, duk_small_int_t radix, duk_small_uint_t flags) {
    duk_native_stack_check(thr);
    duk__numconv_parse_raw(thr, radix, flags);
}

/* automatic undefs */
#undef DUK__BI_MAX_PARTS
#undef DUK__BI_PRINT
#undef DUK__DIGITCHAR
#undef DUK__DRAGON4_OUTPUT_PREINC
#undef DUK__IEEE_DOUBLE_EXP_BIAS
#undef DUK__IEEE_DOUBLE_EXP_MIN
#undef DUK__MAX_FORMATTED_LENGTH
#undef DUK__MAX_OUTPUT_DIGITS
#undef DUK__NO_EXP
#undef DUK__NUMCONV_CTX_BIGINTS_SIZE
#undef DUK__NUMCONV_CTX_NUM_BIGINTS
//#line 1 "duk_regexp_compiler.c"
/*
 *  Regexp compilation.
 *
 *  See doc/regexp.rst for a discussion of the compilation approach and
 *  current limitations.
 *
 *  Regexp bytecode assumes jumps can be expressed with signed 32-bit
 *  integers.  Consequently the bytecode size must not exceed 0x7fffffffL.
 *  The implementation casts duk_size_t (buffer size) to duk_(u)int32_t
 *  in many places.  Although this could be changed, the bytecode format
 *  limit would still prevent regexps exceeding the signed 32-bit limit
 *  from working.
 *
 *  XXX: The implementation does not prevent bytecode from exceeding the
 *  maximum supported size.  This could be done by limiting the maximum
 *  input string size (assuming an upper bound can be computed for number
 *  of bytecode bytes emitted per input byte) or checking buffer maximum
 *  size when emitting bytecode (slower).
 */

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

#if defined(DUK_USE_REGEXP_SUPPORT)

/*
 *  Helper macros
 */

#define DUK__RE_INITIAL_BUFSIZE 64

#define DUK__RE_BUFLEN(re_ctx) \
    DUK_BW_GET_SIZE(re_ctx->thr, &re_ctx->bw)

/*
 *  Disjunction struct: result of parsing a disjunction
 */

typedef struct {
    /* Number of characters that the atom matches (e.g. 3 for 'abc'),
     * -1 if atom is complex and number of matched characters either
     * varies or is not known.
     */
    duk_int32_t charlen;

#if 0
    /* These are not needed to implement quantifier capture handling,
     * but might be needed at some point.
     */

    /* re_ctx->captures at start and end of atom parsing.
     * Since 'captures' indicates highest capture number emitted
     * so far in a DUK_REOP_SAVE, the captures numbers saved by
     * the atom are: ]start_captures,end_captures].
     */
    duk_uint32_t start_captures;
    duk_uint32_t end_captures;
#endif
} duk__re_disjunction_info;

/*
 *  Encoding helpers
 *
 *  Some of the typing is bytecode based, e.g. slice sizes are unsigned 32-bit
 *  even though the buffer operations will use duk_size_t.
 */

/* XXX: the insert helpers should ensure that the bytecode result is not
 * larger than expected (or at least assert for it).  Many things in the
 * bytecode, like skip offsets, won't work correctly if the bytecode is
 * larger than say 2G.
 */

DUK_LOCAL duk_uint32_t duk__encode_i32(duk_int32_t x) {
    if (x < 0) {
        return ((duk_uint32_t) (-x)) * 2 + 1;
    } else {
        return ((duk_uint32_t) x) * 2;
    }
}

/* XXX: return type should probably be duk_size_t, or explicit checks are needed for
 * maximum size.
 */
DUK_LOCAL duk_uint32_t duk__insert_u32(duk_re_compiler_ctx *re_ctx, duk_uint32_t offset, duk_uint32_t x) {
    duk_uint8_t buf[DUK_UNICODE_MAX_XUTF8_LENGTH];
    duk_small_int_t len;

    len = duk_unicode_encode_xutf8((duk_ucodepoint_t) x, buf);
    DUK_ASSERT(len >= 0);
    DUK_BW_INSERT_ENSURE_BYTES(re_ctx->thr, &re_ctx->bw, offset, buf, (duk_size_t) len);
    return (duk_uint32_t) len;
}

DUK_LOCAL void duk__append_u32(duk_re_compiler_ctx *re_ctx, duk_uint32_t x) {
    DUK_BW_WRITE_ENSURE_XUTF8(re_ctx->thr, &re_ctx->bw, x);
}

DUK_LOCAL void duk__append_7bit(duk_re_compiler_ctx *re_ctx, duk_uint32_t x) {
#if defined(DUK_USE_PREFER_SIZE)
    duk__append_u32(re_ctx, x);
#else
    DUK_ASSERT(x <= 0x7fU);
    DUK_BW_WRITE_ENSURE_U8(re_ctx->thr, &re_ctx->bw, (duk_uint8_t) x);
#endif
}

#if 0
DUK_LOCAL void duk__append_2bytes(duk_re_compiler_ctx *re_ctx, duk_uint8_t x, duk_uint8_t y) {
    DUK_BW_WRITE_ENSURE_U8_2(re_ctx->thr, &re_ctx->bw, x, y);
}
#endif

DUK_LOCAL duk_uint32_t duk__insert_i32(duk_re_compiler_ctx *re_ctx, duk_uint32_t offset, duk_int32_t x) {
    return duk__insert_u32(re_ctx, offset, duk__encode_i32(x));
}

DUK_LOCAL void duk__append_reop(duk_re_compiler_ctx *re_ctx, duk_uint32_t reop) {
    DUK_ASSERT(reop <= 0x7fU);
    (void) duk__append_7bit(re_ctx, reop);
}

#if 0  /* unused */
DUK_LOCAL void duk__append_i32(duk_re_compiler_ctx *re_ctx, duk_int32_t x) {
    duk__append_u32(re_ctx, duk__encode_i32(x));
}
#endif

/* special helper for emitting u16 lists (used for character ranges for built-in char classes) */
DUK_LOCAL void duk__append_u16_list(duk_re_compiler_ctx *re_ctx, const duk_uint16_t *values, duk_uint32_t count) {
    /* Call sites don't need the result length so it's not accumulated. */
    while (count-- > 0) {
        duk__append_u32(re_ctx, (duk_uint32_t) (*values++));
    }
}

DUK_LOCAL void duk__insert_slice(duk_re_compiler_ctx *re_ctx, duk_uint32_t offset, duk_uint32_t data_offset, duk_uint32_t data_length) {
    DUK_BW_INSERT_ENSURE_SLICE(re_ctx->thr, &re_ctx->bw, offset, data_offset, data_length);
}

DUK_LOCAL void duk__append_slice(duk_re_compiler_ctx *re_ctx, duk_uint32_t data_offset, duk_uint32_t data_length) {
    DUK_BW_WRITE_ENSURE_SLICE(re_ctx->thr, &re_ctx->bw, data_offset, data_length);
}

DUK_LOCAL void duk__remove_slice(duk_re_compiler_ctx *re_ctx, duk_uint32_t data_offset, duk_uint32_t data_length) {
    DUK_BW_REMOVE_ENSURE_SLICE(re_ctx->thr, &re_ctx->bw, data_offset, data_length);
}

/*
 *  Insert a jump offset at 'offset' to complete an instruction
 *  (the jump offset is always the last component of an instruction).
 *  The 'skip' argument must be computed relative to 'offset',
 *  -without- taking into account the skip field being inserted.
 *
 *       ... A B C ins X Y Z ...   (ins may be a JUMP, SPLIT1/SPLIT2, etc)
 *   =>  ... A B C ins SKIP X Y Z
 *
 *  Computing the final (adjusted) skip value, which is relative to the
 *  first byte of the next instruction, is a bit tricky because of the
 *  variable length UTF-8 encoding.  See doc/regexp.rst for discussion.
 */
DUK_LOCAL duk_uint32_t duk__insert_jump_offset(duk_re_compiler_ctx *re_ctx, duk_uint32_t offset, duk_int32_t skip) {
#if 0
    /* Iterative solution. */
    if (skip < 0) {
        duk_small_int_t len;
        /* two encoding attempts suffices */
        len = duk_unicode_get_xutf8_length((duk_codepoint_t) duk__encode_i32(skip));
        len = duk_unicode_get_xutf8_length((duk_codepoint_t) duk__encode_i32(skip - (duk_int32_t) len));
        DUK_ASSERT(duk_unicode_get_xutf8_length(duk__encode_i32(skip - (duk_int32_t) len)) == len);  /* no change */
        skip -= (duk_int32_t) len;
    }
#endif

#if defined(DUK_USE_PREFER_SIZE)
    /* Closed form solution, this produces smallest code.
     * See re_neg_jump_offset (closed2).
     */
    if (skip < 0) {
        skip--;
        if (skip < -0x3fL) {
            skip--;
        }
        if (skip < -0x3ffL) {
            skip--;
        }
        if (skip < -0x7fffL) {
            skip--;
        }
        if (skip < -0xfffffL) {
            skip--;
        }
        if (skip < -0x1ffffffL) {
            skip--;
        }
        if (skip < -0x3fffffffL) {
            skip--;
        }
    }
#else  /* DUK_USE_PREFER_SIZE */
    /* Closed form solution, this produces fastest code.
     * See re_neg_jump_offset (closed1).
     */
    if (skip < 0) {
        if (skip >= -0x3eL) {
            skip -= 1;
        } else if (skip >= -0x3fdL) {
            skip -= 2;
        } else if (skip >= -0x7ffcL) {
            skip -= 3;
        } else if (skip >= -0xffffbL) {
            skip -= 4;
        } else if (skip >= -0x1fffffaL) {
            skip -= 5;
        } else if (skip >= -0x3ffffff9L) {
            skip -= 6;
        } else {
            skip -= 7;
        }
    }
#endif  /* DUK_USE_PREFER_SIZE */

    return duk__insert_i32(re_ctx, offset, skip);
}

DUK_LOCAL duk_uint32_t duk__append_jump_offset(duk_re_compiler_ctx *re_ctx, duk_int32_t skip) {
    return (duk_uint32_t) duk__insert_jump_offset(re_ctx, (duk_uint32_t) DUK__RE_BUFLEN(re_ctx), skip);
}

/*
 *  duk_re_range_callback for generating character class ranges.
 *
 *  When ignoreCase is false, the range is simply emitted as is.  We don't,
 *  for instance, eliminate duplicates or overlapping ranges in a character
 *  class.
 *
 *  When ignoreCase is true but the 'direct' flag is set, the caller knows
 *  that the range canonicalizes to itself for case insensitive matching,
 *  so the range is emitted as is.  This is mainly useful for built-in ranges
 *  like \W.
 *
 *  Otherwise, when ignoreCase is true, the range needs to be normalized
 *  through canonicalization.  Unfortunately a canonicalized version of a
 *  continuous range is not necessarily continuous (e.g. [x-{] is continuous
 *  but [X-{] is not).  As a result, a single input range may expand to a lot
 *  of output ranges.  The current algorithm creates the canonicalized ranges
 *  footprint efficiently at the cost of compile time execution time; see
 *  doc/regexp.rst for discussion, and some more details below.
 *
 *  Note that the ctx->nranges is a context-wide temporary value.  This is OK
 *  because there cannot be multiple character classes being parsed
 *  simultaneously.
 *
 *  More detail on canonicalization:
 *
 *  Conceptually, a range is canonicalized by scanning the entire range,
 *  normalizing each codepoint by converting it to uppercase, and generating
 *  a set of result ranges.
 *
 *  Ideally a minimal set of output ranges would be emitted by merging all
 *  possible ranges even if they're emitted out of sequence.  Because the
 *  input string is also case normalized during matching, some codepoints
 *  never occur at runtime; these "don't care" codepoints can be included or
 *  excluded from ranges when merging/optimizing ranges.
 *
 *  The current algorithm does not do optimal range merging.  Rather, output
 *  codepoints are generated in sequence, and when the output codepoints are
 *  continuous (CP, CP+1, CP+2, ...), they are merged locally into as large a
 *  range as possible.  A small canonicalization bitmap is used to reduce
 *  actual codepoint canonicalizations which are quite slow at present.  The
 *  bitmap provides a "codepoint block is continuous with respect to
 *  canonicalization" for N-codepoint blocks.  This allows blocks to be
 *  skipped quickly.
 *
 *  There are a number of shortcomings and future work here:
 *
 *    - Individual codepoint normalizations are slow because they involve
 *      walking bit-packed rules without a lookup index.
 *
 *    - The conceptual algorithm needs to canonicalize every codepoint in the
 *      input range to figure out the output range(s).  Even with the small
 *      canonicalization bitmap the algorithm runs quite slowly for worst case
 *      inputs.  There are many data structure alternatives to improve this.
 *
 *    - While the current algorithm generates maximal output ranges when the
 *      output codepoints are emitted linearly, output ranges are not sorted or
 *      merged otherwise.  In the worst case a lot of ranges are emitted when
 *      most of the ranges could be merged.  In this process one could take
 *      advantage of "don't care" codepoints, which are never matched against at
 *      runtime due to canonicalization of input codepoints before comparison,
 *      to merge otherwise discontinuous output ranges.
 *
 *    - The runtime data structure is just a linear list of ranges to match
 *      against.  This can be quite slow if there are a lot of output ranges.
 *      There are various ways to make matching against the ranges faster,
 *      e.g. sorting the ranges and using a binary search; skip lists; tree
 *      based representations; full or approximate codepoint bitmaps, etc.
 *
 *    - Only BMP is supported, codepoints above BMP are assumed to canonicalize
 *      to themselves.  For now this is one place where we don't want to
 *      support chars outside the BMP, because the exhaustive search would be
 *      massively larger.  It would be possible to support non-BMP with a
 *      different algorithm, or perhaps doing case normalization only at match
 *      time.
 */

DUK_LOCAL void duk__regexp_emit_range(duk_re_compiler_ctx *re_ctx, duk_codepoint_t r1, duk_codepoint_t r2) {
    DUK_ASSERT(r2 >= r1);
    duk__append_u32(re_ctx, (duk_uint32_t) r1);
    duk__append_u32(re_ctx, (duk_uint32_t) r2);
    re_ctx->nranges++;
}

#if defined(DUK_USE_REGEXP_CANON_BITMAP)
/* Find next canonicalization discontinuity (conservative estimate) starting
 * from 'start', not exceeding 'end'.  If continuity is fine up to 'end'
 * inclusive, returns end.  Minimum possible return value is start.
 */
DUK_LOCAL duk_codepoint_t duk__re_canon_next_discontinuity(duk_codepoint_t start, duk_codepoint_t end) {
    duk_uint_t start_blk;
    duk_uint_t end_blk;
    duk_uint_t blk;
    duk_uint_t offset;
    duk_uint8_t mask;

    /* Inclusive block range. */
    DUK_ASSERT(start >= 0);
    DUK_ASSERT(end >= 0);
    DUK_ASSERT(end >= start);
    start_blk = (duk_uint_t) (start >> DUK_CANON_BITMAP_BLKSHIFT);
    end_blk = (duk_uint_t) (end >> DUK_CANON_BITMAP_BLKSHIFT);

    for (blk = start_blk; blk <= end_blk; blk++) {
        offset = blk >> 3;
        mask = 1U << (blk & 0x07);
        if (offset >= sizeof(duk_unicode_re_canon_bitmap)) {
            /* Reached non-BMP range which is assumed continuous. */
            return end;
        }
        DUK_ASSERT(offset < sizeof(duk_unicode_re_canon_bitmap));
        if ((duk_unicode_re_canon_bitmap[offset] & mask) == 0) {
            /* Block is discontinuous, continuity is guaranteed
             * only up to end of previous block (+1 for exclusive
             * return value => start of current block).  Start
             * block requires special handling.
             */
            if (blk > start_blk) {
                return (duk_codepoint_t) (blk << DUK_CANON_BITMAP_BLKSHIFT);
            } else {
                return start;
            }
        }
    }
    DUK_ASSERT(blk == end_blk + 1);  /* Reached end block which is continuous. */
    return end;
}
#else  /* DUK_USE_REGEXP_CANON_BITMAP */
DUK_LOCAL duk_codepoint_t duk__re_canon_next_discontinuity(duk_codepoint_t start, duk_codepoint_t end) {
    DUK_ASSERT(start >= 0);
    DUK_ASSERT(end >= 0);
    DUK_ASSERT(end >= start);
    if (start >= 0x10000) {
        /* Even without the bitmap, treat non-BMP as continuous. */
        return end;
    }
    return start;
}
#endif  /* DUK_USE_REGEXP_CANON_BITMAP */

DUK_LOCAL void duk__regexp_generate_ranges(void *userdata, duk_codepoint_t r1, duk_codepoint_t r2, duk_bool_t direct) {
    duk_re_compiler_ctx *re_ctx = (duk_re_compiler_ctx *) userdata;
    duk_codepoint_t r_start;
    duk_codepoint_t r_end;
    duk_codepoint_t i;
    duk_codepoint_t t;
    duk_codepoint_t r_disc;

    DUK_DD(DUK_DDPRINT("duk__regexp_generate_ranges(): re_ctx=%p, range=[%ld,%ld] direct=%ld",
                       (void *) re_ctx, (long) r1, (long) r2, (long) direct));

    DUK_ASSERT(r2 >= r1);  /* SyntaxError for out of order range. */

    if (direct || (re_ctx->re_flags & DUK_RE_FLAG_IGNORE_CASE) == 0) {
        DUK_DD(DUK_DDPRINT("direct or not case sensitive, emit range: [%ld,%ld]", (long) r1, (long) r2));
        duk__regexp_emit_range(re_ctx, r1, r2);
        return;
    }

    DUK_DD(DUK_DDPRINT("case sensitive, process range: [%ld,%ld]", (long) r1, (long) r2));

    r_start = duk_unicode_re_canonicalize_char(re_ctx->thr, r1);
    r_end = r_start;

    for (i = r1 + 1; i <= r2;) {
        /* Input codepoint space processed up to i-1, and
         * current range in r_{start,end} is up-to-date
         * (inclusive) and may either break or continue.
         */
        r_disc = duk__re_canon_next_discontinuity(i, r2);
        DUK_ASSERT(r_disc >= i);
        DUK_ASSERT(r_disc <= r2);

        r_end += r_disc - i;  /* May be zero. */
        t = duk_unicode_re_canonicalize_char(re_ctx->thr, r_disc);
        if (t == r_end + 1) {
            /* Not actually a discontinuity, continue range
             * to r_disc and recheck.
             */
            r_end = t;
        } else {
            duk__regexp_emit_range(re_ctx, r_start, r_end);
            r_start = t;
            r_end = t;
        }
        i = r_disc + 1;  /* Guarantees progress. */
    }
    duk__regexp_emit_range(re_ctx, r_start, r_end);

#if 0  /* Exhaustive search, very slow. */
    r_start = duk_unicode_re_canonicalize_char(re_ctx->thr, r1);
    r_end = r_start;
    for (i = r1 + 1; i <= r2; i++) {
        t = duk_unicode_re_canonicalize_char(re_ctx->thr, i);
        if (t == r_end + 1) {
            r_end = t;
        } else {
            DUK_DD(DUK_DDPRINT("canonicalized, emit range: [%ld,%ld]", (long) r_start, (long) r_end));
            duk__append_u32(re_ctx, (duk_uint32_t) r_start);
            duk__append_u32(re_ctx, (duk_uint32_t) r_end);
            re_ctx->nranges++;
            r_start = t;
            r_end = t;
        }
    }
    DUK_DD(DUK_DDPRINT("canonicalized, emit range: [%ld,%ld]", (long) r_start, (long) r_end));
    duk__append_u32(re_ctx, (duk_uint32_t) r_start);
    duk__append_u32(re_ctx, (duk_uint32_t) r_end);
    re_ctx->nranges++;
#endif
}

/*
 *  Parse regexp Disjunction.  Most of regexp compilation happens here.
 *
 *  Handles Disjunction, Alternative, and Term productions directly without
 *  recursion.  The only constructs requiring recursion are positive/negative
 *  lookaheads, capturing parentheses, and non-capturing parentheses.
 *
 *  The function determines whether the entire disjunction is a 'simple atom'
 *  (see doc/regexp.rst discussion on 'simple quantifiers') and if so,
 *  returns the atom character length which is needed by the caller to keep
 *  track of its own atom character length.  A disjunction with more than one
 *  alternative is never considered a simple atom (although in some cases
 *  that might be the case).
 *
 *  Return value: simple atom character length or < 0 if not a simple atom.
 *  Appends the bytecode for the disjunction matcher to the end of the temp
 *  buffer.
 *
 *  Regexp top level structure is:
 *
 *    Disjunction = Term*
 *                | Term* | Disjunction
 *
 *    Term = Assertion
 *         | Atom
 *         | Atom Quantifier
 *
 *  An empty Term sequence is a valid disjunction alternative (e.g. /|||c||/).
 *
 *  Notes:
 *
 *    * Tracking of the 'simple-ness' of the current atom vs. the entire
 *      disjunction are separate matters.  For instance, the disjunction
 *      may be complex, but individual atoms may be simple.  Furthermore,
 *      simple quantifiers are used whenever possible, even if the
 *      disjunction as a whole is complex.
 *
 *    * The estimate of whether an atom is simple is conservative now,
 *      and it would be possible to expand it.  For instance, captures
 *      cause the disjunction to be marked complex, even though captures
 *      -can- be handled by simple quantifiers with some minor modifications.
 *
 *    * Disjunction 'tainting' as 'complex' is handled at the end of the
 *      main for loop collectively for atoms.  Assertions, quantifiers,
 *      and '|' tokens need to taint the result manually if necessary.
 *      Assertions cannot add to result char length, only atoms (and
 *      quantifiers) can; currently quantifiers will taint the result
 *      as complex though.
 */

DUK_LOCAL const duk_uint16_t * const duk__re_range_lookup1[3] = {
    duk_unicode_re_ranges_digit,
    duk_unicode_re_ranges_white,
    duk_unicode_re_ranges_wordchar
};
DUK_LOCAL const duk_uint8_t duk__re_range_lookup2[3] = {
    sizeof(duk_unicode_re_ranges_digit) / (2 * sizeof(duk_uint16_t)),
    sizeof(duk_unicode_re_ranges_white) / (2 * sizeof(duk_uint16_t)),
    sizeof(duk_unicode_re_ranges_wordchar) / (2 * sizeof(duk_uint16_t))
};

DUK_LOCAL void duk__append_range_atom_matcher(duk_re_compiler_ctx *re_ctx, duk_small_uint_t re_op, const duk_uint16_t *ranges, duk_small_uint_t count) {
#if 0
    DUK_ASSERT(re_op <= 0x7fUL);
    DUK_ASSERT(count <= 0x7fUL);
    duk__append_2bytes(re_ctx, (duk_uint8_t) re_op, (duk_uint8_t) count);
#endif
    duk__append_reop(re_ctx, re_op);
    duk__append_7bit(re_ctx, count);
    duk__append_u16_list(re_ctx, ranges, count * 2);
}

DUK_LOCAL void duk__parse_disjunction(duk_re_compiler_ctx *re_ctx, duk_bool_t expect_eof, duk__re_disjunction_info *out_atom_info) {
    duk_int32_t atom_start_offset = -1;                   /* negative -> no atom matched on previous round */
    duk_int32_t atom_char_length = 0;                     /* negative -> complex atom */
    duk_uint32_t atom_start_captures = re_ctx->captures;  /* value of re_ctx->captures at start of atom */
    duk_int32_t unpatched_disjunction_split = -1;
    duk_int32_t unpatched_disjunction_jump = -1;
    duk_uint32_t entry_offset = (duk_uint32_t) DUK__RE_BUFLEN(re_ctx);
    duk_int32_t res_charlen = 0;  /* -1 if disjunction is complex, char length if simple */
    duk__re_disjunction_info tmp_disj;

    DUK_ASSERT(out_atom_info != NULL);

    duk_native_stack_check(re_ctx->thr);
    if (re_ctx->recursion_depth >= re_ctx->recursion_limit) {
        DUK_ERROR_RANGE(re_ctx->thr, DUK_STR_REGEXP_COMPILER_RECURSION_LIMIT);
        DUK_WO_NORETURN(return;);
    }
    re_ctx->recursion_depth++;

#if 0
    out_atom_info->start_captures = re_ctx->captures;
#endif

    for (;;) {
        /* atom_char_length, atom_start_offset, atom_start_offset reflect the
         * atom matched on the previous loop.  If a quantifier is encountered
         * on this loop, these are needed to handle the quantifier correctly.
         * new_atom_char_length etc are for the atom parsed on this round;
         * they're written to atom_char_length etc at the end of the round.
         */
        duk_int32_t new_atom_char_length;   /* char length of the atom parsed in this loop */
        duk_int32_t new_atom_start_offset;  /* bytecode start offset of the atom parsed in this loop
                                             * (allows quantifiers to copy the atom bytecode)
                                             */
        duk_uint32_t new_atom_start_captures;  /* re_ctx->captures at the start of the atom parsed in this loop */

        duk_lexer_parse_re_token(&re_ctx->lex, &re_ctx->curr_token);

        DUK_DD(DUK_DDPRINT("re token: %ld (num=%ld, char=%c)",
                           (long) re_ctx->curr_token.t,
                           (long) re_ctx->curr_token.num,
                           (re_ctx->curr_token.num >= 0x20 && re_ctx->curr_token.num <= 0x7e) ?
                           (int) re_ctx->curr_token.num : (int) '?'));

        /* set by atom case clauses */
        new_atom_start_offset = -1;
        new_atom_char_length = -1;
        new_atom_start_captures = re_ctx->captures;

        switch (re_ctx->curr_token.t) {
        case DUK_RETOK_DISJUNCTION: {
            /*
             *  The handling here is a bit tricky.  If a previous '|' has been processed,
             *  we have a pending split1 and a pending jump (for a previous match).  These
             *  need to be back-patched carefully.  See docs for a detailed example.
             */

            /* patch pending jump and split */
            if (unpatched_disjunction_jump >= 0) {
                duk_uint32_t offset;

                DUK_ASSERT(unpatched_disjunction_split >= 0);
                offset = (duk_uint32_t) unpatched_disjunction_jump;
                offset += duk__insert_jump_offset(re_ctx,
                                                  offset,
                                                  (duk_int32_t) (DUK__RE_BUFLEN(re_ctx) - offset));
                /* offset is now target of the pending split (right after jump) */
                duk__insert_jump_offset(re_ctx,
                                        (duk_uint32_t) unpatched_disjunction_split,
                                        (duk_int32_t) offset - unpatched_disjunction_split);
            }

            /* add a new pending split to the beginning of the entire disjunction */
            (void) duk__insert_u32(re_ctx,
                                   entry_offset,
                                   DUK_REOP_SPLIT1);   /* prefer direct execution */
            unpatched_disjunction_split = (duk_int32_t) (entry_offset + 1);   /* +1 for opcode */

            /* add a new pending match jump for latest finished alternative */
            duk__append_reop(re_ctx, DUK_REOP_JUMP);
            unpatched_disjunction_jump = (duk_int32_t) DUK__RE_BUFLEN(re_ctx);

            /* 'taint' result as complex */
            res_charlen = -1;
            break;
        }
        case DUK_RETOK_QUANTIFIER: {
            if (atom_start_offset < 0) {
                DUK_ERROR_SYNTAX(re_ctx->thr, DUK_STR_INVALID_QUANTIFIER_NO_ATOM);
                DUK_WO_NORETURN(return;);
            }
            if (re_ctx->curr_token.qmin > re_ctx->curr_token.qmax) {
                DUK_ERROR_SYNTAX(re_ctx->thr, DUK_STR_INVALID_QUANTIFIER_VALUES);
                DUK_WO_NORETURN(return;);
            }
            if (atom_char_length >= 0) {
                /*
                 *  Simple atom
                 *
                 *  If atom_char_length is zero, we'll have unbounded execution time for e.g.
                 *  /()*x/.exec('x').  We can't just skip the match because it might have some
                 *  side effects (for instance, if we allowed captures in simple atoms, the
                 *  capture needs to happen).  The simple solution below is to force the
                 *  quantifier to match at most once, since the additional matches have no effect.
                 *
                 *  With a simple atom there can be no capture groups, so no captures need
                 *  to be reset.
                 */
                duk_int32_t atom_code_length;
                duk_uint32_t offset;
                duk_uint32_t qmin, qmax;

                qmin = re_ctx->curr_token.qmin;
                qmax = re_ctx->curr_token.qmax;
                if (atom_char_length == 0) {
                    /* qmin and qmax will be 0 or 1 */
                    if (qmin > 1) {
                        qmin = 1;
                    }
                    if (qmax > 1) {
                        qmax = 1;
                    }
                }

                duk__append_reop(re_ctx, DUK_REOP_MATCH);   /* complete 'sub atom' */
                atom_code_length = (duk_int32_t) (DUK__RE_BUFLEN(re_ctx) - (duk_size_t) atom_start_offset);

                offset = (duk_uint32_t) atom_start_offset;
                if (re_ctx->curr_token.greedy) {
                    offset += duk__insert_u32(re_ctx, offset, DUK_REOP_SQGREEDY);
                    offset += duk__insert_u32(re_ctx, offset, qmin);
                    offset += duk__insert_u32(re_ctx, offset, qmax);
                    offset += duk__insert_u32(re_ctx, offset, (duk_uint32_t) atom_char_length);
                    offset += duk__insert_jump_offset(re_ctx, offset, atom_code_length);
                } else {
                    offset += duk__insert_u32(re_ctx, offset, DUK_REOP_SQMINIMAL);
                    offset += duk__insert_u32(re_ctx, offset, qmin);
                    offset += duk__insert_u32(re_ctx, offset, qmax);
                    offset += duk__insert_jump_offset(re_ctx, offset, atom_code_length);
                }
                DUK_UNREF(offset);  /* silence scan-build warning */
            } else {
                /*
                 *  Complex atom
                 *
                 *  The original code is used as a template, and removed at the end
                 *  (this differs from the handling of simple quantifiers).
                 *
                 *  NOTE: there is no current solution for empty atoms in complex
                 *  quantifiers.  This would need some sort of a 'progress' instruction.
                 *
                 *  XXX: impose limit on maximum result size, i.e. atom_code_len * atom_copies?
                 */
                duk_int32_t atom_code_length;
                duk_uint32_t atom_copies;
                duk_uint32_t tmp_qmin, tmp_qmax;

                /* pre-check how many atom copies we're willing to make (atom_copies not needed below) */
                atom_copies = (re_ctx->curr_token.qmax == DUK_RE_QUANTIFIER_INFINITE) ?
                              re_ctx->curr_token.qmin : re_ctx->curr_token.qmax;
                if (atom_copies > DUK_RE_MAX_ATOM_COPIES) {
                    DUK_ERROR_RANGE(re_ctx->thr, DUK_STR_QUANTIFIER_TOO_MANY_COPIES);
                    DUK_WO_NORETURN(return;);
                }

                /* wipe the capture range made by the atom (if any) */
                DUK_ASSERT(atom_start_captures <= re_ctx->captures);
                if (atom_start_captures != re_ctx->captures) {
                    DUK_ASSERT(atom_start_captures < re_ctx->captures);
                    DUK_DDD(DUK_DDDPRINT("must wipe ]atom_start_captures,re_ctx->captures]: ]%ld,%ld]",
                                         (long) atom_start_captures, (long) re_ctx->captures));

                    /* insert (DUK_REOP_WIPERANGE, start, count) in reverse order so the order ends up right */
                    duk__insert_u32(re_ctx, (duk_uint32_t) atom_start_offset, (re_ctx->captures - atom_start_captures) * 2U);
                    duk__insert_u32(re_ctx, (duk_uint32_t) atom_start_offset, (atom_start_captures + 1) * 2);
                    duk__insert_u32(re_ctx, (duk_uint32_t) atom_start_offset, DUK_REOP_WIPERANGE);
                } else {
                    DUK_DDD(DUK_DDDPRINT("no need to wipe captures: atom_start_captures == re_ctx->captures == %ld",
                                         (long) atom_start_captures));
                }

                atom_code_length = (duk_int32_t) DUK__RE_BUFLEN(re_ctx) - atom_start_offset;

                /* insert the required matches (qmin) by copying the atom */
                tmp_qmin = re_ctx->curr_token.qmin;
                tmp_qmax = re_ctx->curr_token.qmax;
                while (tmp_qmin > 0) {
                    duk__append_slice(re_ctx, (duk_uint32_t) atom_start_offset, (duk_uint32_t) atom_code_length);
                    tmp_qmin--;
                    if (tmp_qmax != DUK_RE_QUANTIFIER_INFINITE) {
                        tmp_qmax--;
                    }
                }
                DUK_ASSERT(tmp_qmin == 0);

                /* insert code for matching the remainder - infinite or finite */
                if (tmp_qmax == DUK_RE_QUANTIFIER_INFINITE) {
                    /* reuse last emitted atom for remaining 'infinite' quantifier */

                    if (re_ctx->curr_token.qmin == 0) {
                        /* Special case: original qmin was zero so there is nothing
                         * to repeat.  Emit an atom copy but jump over it here.
                         */
                        duk__append_reop(re_ctx, DUK_REOP_JUMP);
                        duk__append_jump_offset(re_ctx, atom_code_length);
                        duk__append_slice(re_ctx, (duk_uint32_t) atom_start_offset, (duk_uint32_t) atom_code_length);
                    }
                    if (re_ctx->curr_token.greedy) {
                        duk__append_reop(re_ctx, DUK_REOP_SPLIT2);   /* prefer jump */
                    } else {
                        duk__append_reop(re_ctx, DUK_REOP_SPLIT1);   /* prefer direct */
                    }
                    duk__append_jump_offset(re_ctx, -atom_code_length - 1);  /* -1 for opcode */
                } else {
                    /*
                     *  The remaining matches are emitted as sequence of SPLITs and atom
                     *  copies; the SPLITs skip the remaining copies and match the sequel.
                     *  This sequence needs to be emitted starting from the last copy
                     *  because the SPLITs are variable length due to the variable length
                     *  skip offset.  This causes a lot of memory copying now.
                     *
                     *  Example structure (greedy, match maximum # atoms):
                     *
                     *      SPLIT1 LSEQ
                     *      (atom)
                     *      SPLIT1 LSEQ    ; <- the byte length of this instruction is needed
                     *      (atom)         ; to encode the above SPLIT1 correctly
                     *      ...
                     *   LSEQ:
                     */
                    duk_uint32_t offset = (duk_uint32_t) DUK__RE_BUFLEN(re_ctx);
                    while (tmp_qmax > 0) {
                        duk__insert_slice(re_ctx, offset, (duk_uint32_t) atom_start_offset, (duk_uint32_t) atom_code_length);
                        if (re_ctx->curr_token.greedy) {
                            duk__insert_u32(re_ctx, offset, DUK_REOP_SPLIT1);   /* prefer direct */
                        } else {
                            duk__insert_u32(re_ctx, offset, DUK_REOP_SPLIT2);   /* prefer jump */
                        }
                        duk__insert_jump_offset(re_ctx,
                                                offset + 1,   /* +1 for opcode */
                                                (duk_int32_t) (DUK__RE_BUFLEN(re_ctx) - (offset + 1)));
                        tmp_qmax--;
                    }
                }

                /* remove the original 'template' atom */
                duk__remove_slice(re_ctx, (duk_uint32_t) atom_start_offset, (duk_uint32_t) atom_code_length);
            }

            /* 'taint' result as complex */
            res_charlen = -1;
            break;
        }
        case DUK_RETOK_ASSERT_START: {
            duk__append_reop(re_ctx, DUK_REOP_ASSERT_START);
            break;
        }
        case DUK_RETOK_ASSERT_END: {
            duk__append_reop(re_ctx, DUK_REOP_ASSERT_END);
            break;
        }
        case DUK_RETOK_ASSERT_WORD_BOUNDARY: {
            duk__append_reop(re_ctx, DUK_REOP_ASSERT_WORD_BOUNDARY);
            break;
        }
        case DUK_RETOK_ASSERT_NOT_WORD_BOUNDARY: {
            duk__append_reop(re_ctx, DUK_REOP_ASSERT_NOT_WORD_BOUNDARY);
            break;
        }
        case DUK_RETOK_ASSERT_START_POS_LOOKAHEAD:
        case DUK_RETOK_ASSERT_START_NEG_LOOKAHEAD: {
            duk_uint32_t offset;
            duk_uint32_t opcode = (re_ctx->curr_token.t == DUK_RETOK_ASSERT_START_POS_LOOKAHEAD) ?
                                  DUK_REOP_LOOKPOS : DUK_REOP_LOOKNEG;

            offset = (duk_uint32_t) DUK__RE_BUFLEN(re_ctx);
            duk__parse_disjunction(re_ctx, 0, &tmp_disj);
            duk__append_reop(re_ctx, DUK_REOP_MATCH);

            (void) duk__insert_u32(re_ctx, offset, opcode);
            (void) duk__insert_jump_offset(re_ctx,
                                           offset + 1,   /* +1 for opcode */
                                           (duk_int32_t) (DUK__RE_BUFLEN(re_ctx) - (offset + 1)));

            /* 'taint' result as complex -- this is conservative,
             * as lookaheads do not backtrack.
             */
            res_charlen = -1;
            break;
        }
        case DUK_RETOK_ATOM_PERIOD: {
            new_atom_char_length = 1;
            new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx);
            duk__append_reop(re_ctx, DUK_REOP_PERIOD);
            break;
        }
        case DUK_RETOK_ATOM_CHAR: {
            /* Note: successive characters could be joined into string matches
             * but this is not trivial (consider e.g. '/xyz+/); see docs for
             * more discussion.
             *
             * No support for \u{H+} yet.  While only BMP Unicode escapes are
             * supported for RegExps at present, 'ch' may still be a non-BMP
             * codepoint if it is decoded straight from source text UTF-8.
             * There's no non-BMP support yet so this is handled simply by
             * matching the non-BMP character (which is custom behavior).
             */
            duk_uint32_t ch;

            new_atom_char_length = 1;
            new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx);
            duk__append_reop(re_ctx, DUK_REOP_CHAR);
            ch = re_ctx->curr_token.num;
            if (re_ctx->re_flags & DUK_RE_FLAG_IGNORE_CASE) {
                ch = (duk_uint32_t) duk_unicode_re_canonicalize_char(re_ctx->thr, (duk_codepoint_t) ch);
            }
            duk__append_u32(re_ctx, ch);
            break;
        }
        case DUK_RETOK_ATOM_DIGIT:
        case DUK_RETOK_ATOM_NOT_DIGIT:
        case DUK_RETOK_ATOM_WHITE:
        case DUK_RETOK_ATOM_NOT_WHITE:
        case DUK_RETOK_ATOM_WORD_CHAR:
        case DUK_RETOK_ATOM_NOT_WORD_CHAR: {
            duk_small_uint_t re_op;
            duk_small_uint_t idx;

            new_atom_char_length = 1;
            new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx);

            DUK_ASSERT((DUK_RETOK_ATOM_DIGIT & 0x01) != 0);
            DUK_ASSERT((DUK_RETOK_ATOM_WHITE & 0x01) != 0);
            DUK_ASSERT((DUK_RETOK_ATOM_WORD_CHAR & 0x01) != 0);
            DUK_ASSERT((DUK_RETOK_ATOM_NOT_DIGIT & 0x01) == 0);
            DUK_ASSERT((DUK_RETOK_ATOM_NOT_WHITE & 0x01) == 0);
            DUK_ASSERT((DUK_RETOK_ATOM_NOT_WORD_CHAR & 0x01) == 0);
            re_op = (re_ctx->curr_token.t & 0x01) ? DUK_REOP_RANGES : DUK_REOP_INVRANGES;

            DUK_ASSERT(DUK_RETOK_ATOM_WHITE == DUK_RETOK_ATOM_DIGIT + 2);
            DUK_ASSERT(DUK_RETOK_ATOM_WORD_CHAR == DUK_RETOK_ATOM_DIGIT + 4);
            idx = (duk_small_uint_t) ((re_ctx->curr_token.t - DUK_RETOK_ATOM_DIGIT) >> 1U);
            DUK_ASSERT(idx <= 2U);  /* Assume continuous token numbers; also checks negative underflow. */

            duk__append_range_atom_matcher(re_ctx, re_op, duk__re_range_lookup1[idx], duk__re_range_lookup2[idx]);
            break;
        }
        case DUK_RETOK_ATOM_BACKREFERENCE: {
            duk_uint32_t backref = (duk_uint32_t) re_ctx->curr_token.num;
            if (backref > re_ctx->highest_backref) {
                re_ctx->highest_backref = backref;
            }
            new_atom_char_length = -1;   /* mark as complex */
            new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx);
            duk__append_reop(re_ctx, DUK_REOP_BACKREFERENCE);
            duk__append_u32(re_ctx, backref);
            break;
        }
        case DUK_RETOK_ATOM_START_CAPTURE_GROUP: {
            duk_uint32_t cap;

            new_atom_char_length = -1;   /* mark as complex (capture handling) */
            new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx);
            cap = ++re_ctx->captures;
            duk__append_reop(re_ctx, DUK_REOP_SAVE);
            duk__append_u32(re_ctx, cap * 2);
            duk__parse_disjunction(re_ctx, 0, &tmp_disj);  /* retval (sub-atom char length) unused, tainted as complex above */
            duk__append_reop(re_ctx, DUK_REOP_SAVE);
            duk__append_u32(re_ctx, cap * 2 + 1);
            break;
        }
        case DUK_RETOK_ATOM_START_NONCAPTURE_GROUP: {
            new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx);
            duk__parse_disjunction(re_ctx, 0, &tmp_disj);
            new_atom_char_length = tmp_disj.charlen;
            break;
        }
        case DUK_RETOK_ATOM_START_CHARCLASS:
        case DUK_RETOK_ATOM_START_CHARCLASS_INVERTED: {
            /*
             *  Range parsing is done with a special lexer function which calls
             *  us for every range parsed.  This is different from how rest of
             *  the parsing works, but avoids a heavy, arbitrary size intermediate
             *  value type to hold the ranges.
             *
             *  Another complication is the handling of character ranges when
             *  case insensitive matching is used (see docs for discussion).
             *  The range handler callback given to the lexer takes care of this
             *  as well.
             *
             *  Note that duplicate ranges are not eliminated when parsing character
             *  classes, so that canonicalization of
             *
             *    [0-9a-fA-Fx-{]
             *
             *  creates the result (note the duplicate ranges):
             *
             *    [0-9A-FA-FX-Z{-{]
             *
             *  where [x-{] is split as a result of canonicalization.  The duplicate
             *  ranges are not a semantics issue: they work correctly.
             */

            duk_uint32_t offset;

            DUK_DD(DUK_DDPRINT("character class"));

            /* insert ranges instruction, range count patched in later */
            new_atom_char_length = 1;
            new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx);
            duk__append_reop(re_ctx,
                             (re_ctx->curr_token.t == DUK_RETOK_ATOM_START_CHARCLASS) ?
                             DUK_REOP_RANGES : DUK_REOP_INVRANGES);
            offset = (duk_uint32_t) DUK__RE_BUFLEN(re_ctx);    /* patch in range count later */

            /* parse ranges until character class ends */
            re_ctx->nranges = 0;    /* note: ctx-wide temporary */
            duk_lexer_parse_re_ranges(&re_ctx->lex, duk__regexp_generate_ranges, (void *) re_ctx);

            /* insert range count */
            duk__insert_u32(re_ctx, offset, re_ctx->nranges);
            break;
        }
        case DUK_RETOK_ATOM_END_GROUP: {
            if (expect_eof) {
                DUK_ERROR_SYNTAX(re_ctx->thr, DUK_STR_UNEXPECTED_CLOSING_PAREN);
                DUK_WO_NORETURN(return;);
            }
            goto done;
        }
        case DUK_RETOK_EOF: {
            if (!expect_eof) {
                DUK_ERROR_SYNTAX(re_ctx->thr, DUK_STR_UNEXPECTED_END_OF_PATTERN);
                DUK_WO_NORETURN(return;);
            }
            goto done;
        }
        default: {
            DUK_ERROR_SYNTAX(re_ctx->thr, DUK_STR_UNEXPECTED_REGEXP_TOKEN);
            DUK_WO_NORETURN(return;);
        }
        }

        /* a complex (new) atom taints the result */
        if (new_atom_start_offset >= 0) {
            if (new_atom_char_length < 0) {
                res_charlen = -1;
            } else if (res_charlen >= 0) {
                /* only advance if not tainted */
                res_charlen += new_atom_char_length;
            }
        }

        /* record previous atom info in case next token is a quantifier */
        atom_start_offset = new_atom_start_offset;
        atom_char_length = new_atom_char_length;
        atom_start_captures = new_atom_start_captures;
    }

 done:

    /* finish up pending jump and split for last alternative */
    if (unpatched_disjunction_jump >= 0) {
        duk_uint32_t offset;

        DUK_ASSERT(unpatched_disjunction_split >= 0);
        offset = (duk_uint32_t) unpatched_disjunction_jump;
        offset += duk__insert_jump_offset(re_ctx,
                                          offset,
                                          (duk_int32_t) (DUK__RE_BUFLEN(re_ctx) - offset));
        /* offset is now target of the pending split (right after jump) */
        duk__insert_jump_offset(re_ctx,
                                (duk_uint32_t) unpatched_disjunction_split,
                                (duk_int32_t) offset - unpatched_disjunction_split);
    }

#if 0
    out_atom_info->end_captures = re_ctx->captures;
#endif
    out_atom_info->charlen = res_charlen;
    DUK_DDD(DUK_DDDPRINT("parse disjunction finished: charlen=%ld",
                         (long) out_atom_info->charlen));

    re_ctx->recursion_depth--;
}

/*
 *  Flags parsing (see E5 Section 15.10.4.1).
 */

DUK_LOCAL duk_uint32_t duk__parse_regexp_flags(duk_hthread *thr, duk_hstring *h) {
    const duk_uint8_t *p;
    const duk_uint8_t *p_end;
    duk_uint32_t flags = 0;

    p = DUK_HSTRING_GET_DATA(h);
    p_end = p + DUK_HSTRING_GET_BYTELEN(h);

    /* Note: can be safely scanned as bytes (undecoded) */

    while (p < p_end) {
        duk_uint8_t c = *p++;
        switch (c) {
        case (duk_uint8_t) 'g': {
            if (flags & DUK_RE_FLAG_GLOBAL) {
                goto flags_error;
            }
            flags |= DUK_RE_FLAG_GLOBAL;
            break;
        }
        case (duk_uint8_t) 'i': {
            if (flags & DUK_RE_FLAG_IGNORE_CASE) {
                goto flags_error;
            }
            flags |= DUK_RE_FLAG_IGNORE_CASE;
            break;
        }
        case (duk_uint8_t) 'm': {
            if (flags & DUK_RE_FLAG_MULTILINE) {
                goto flags_error;
            }
            flags |= DUK_RE_FLAG_MULTILINE;
            break;
        }
        default: {
            goto flags_error;
        }
        }
    }

    return flags;

 flags_error:
    DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_REGEXP_FLAGS);
    DUK_WO_NORETURN(return 0U;);
}

/*
 *  Create escaped RegExp source (E5 Section 15.10.3).
 *
 *  The current approach is to special case the empty RegExp
 *  ('' -> '(?:)') and otherwise replace unescaped '/' characters
 *  with '\/' regardless of where they occur in the regexp.
 *
 *  Note that normalization does not seem to be necessary for
 *  RegExp literals (e.g. '/foo/') because to be acceptable as
 *  a RegExp literal, the text between forward slashes must
 *  already match the escaping requirements (e.g. must not contain
 *  unescaped forward slashes or be empty).  Escaping IS needed
 *  for expressions like 'new Regexp("...", "")' however.
 *  Currently, we re-escape in either case.
 *
 *  Also note that we process the source here in UTF-8 encoded
 *  form.  This is correct, because any non-ASCII characters are
 *  passed through without change.
 */

DUK_LOCAL void duk__create_escaped_source(duk_hthread *thr, int idx_pattern) {
    duk_hstring *h;
    const duk_uint8_t *p;
    duk_bufwriter_ctx bw_alloc;
    duk_bufwriter_ctx *bw;
    duk_uint8_t *q;
    duk_size_t i, n;
    duk_uint_fast8_t c_prev, c;

    h = duk_known_hstring(thr, idx_pattern);
    p = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h);
    n = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h);

    if (n == 0) {
        duk_push_literal(thr, "(?:)");
        return;
    }

    bw = &bw_alloc;
    DUK_BW_INIT_PUSHBUF(thr, bw, n);
    q = DUK_BW_GET_PTR(thr, bw);

    c_prev = (duk_uint_fast8_t) 0;

    for (i = 0; i < n; i++) {
        c = p[i];

        q = DUK_BW_ENSURE_RAW(thr, bw, 2, q);

        if (c == (duk_uint_fast8_t) '/' && c_prev != (duk_uint_fast8_t) '\\') {
            /* Unescaped '/' ANYWHERE in the regexp (in disjunction,
             * inside a character class, ...) => same escape works.
             */
            *q++ = DUK_ASC_BACKSLASH;
        }
        *q++ = (duk_uint8_t) c;

        c_prev = c;
    }

    DUK_BW_SETPTR_AND_COMPACT(thr, bw, q);
    (void) duk_buffer_to_string(thr, -1);  /* Safe if input is safe. */

    /* [ ... escaped_source ] */
}

/*
 *  Exposed regexp compilation primitive.
 *
 *  Sets up a regexp compilation context, and calls duk__parse_disjunction() to do the
 *  actual parsing.  Handles generation of the compiled regexp header and the
 *  "boilerplate" capture of the matching substring (save 0 and 1).  Also does some
 *  global level regexp checks after recursive compilation has finished.
 *
 *  An escaped version of the regexp source, suitable for use as a RegExp instance
 *  'source' property (see E5 Section 15.10.3), is also left on the stack.
 *
 *  Input stack:  [ pattern flags ]
 *  Output stack: [ bytecode escaped_source ]  (both as strings)
 */

DUK_INTERNAL void duk_regexp_compile(duk_hthread *thr) {
    duk_re_compiler_ctx re_ctx;
    duk_lexer_point lex_point;
    duk_hstring *h_pattern;
    duk_hstring *h_flags;
    duk__re_disjunction_info ign_disj;

    DUK_ASSERT(thr != NULL);

    /*
     *  Args validation
     */

    /* TypeError if fails */
    h_pattern = duk_require_hstring_notsymbol(thr, -2);
    h_flags = duk_require_hstring_notsymbol(thr, -1);

    /*
     *  Create normalized 'source' property (E5 Section 15.10.3).
     */

    /* [ ... pattern flags ] */

    duk__create_escaped_source(thr, -2);

    /* [ ... pattern flags escaped_source ] */

    /*
     *  Init compilation context
     */

    /* [ ... pattern flags escaped_source buffer ] */

    duk_memzero(&re_ctx, sizeof(re_ctx));
    DUK_LEXER_INITCTX(&re_ctx.lex);  /* duplicate zeroing, expect for (possible) NULL inits */
    re_ctx.thr = thr;
    re_ctx.lex.thr = thr;
    re_ctx.lex.input = DUK_HSTRING_GET_DATA(h_pattern);
    re_ctx.lex.input_length = DUK_HSTRING_GET_BYTELEN(h_pattern);
    re_ctx.lex.token_limit = DUK_RE_COMPILE_TOKEN_LIMIT;
    re_ctx.recursion_limit = DUK_USE_REGEXP_COMPILER_RECLIMIT;
    re_ctx.re_flags = duk__parse_regexp_flags(thr, h_flags);

    DUK_BW_INIT_PUSHBUF(thr, &re_ctx.bw, DUK__RE_INITIAL_BUFSIZE);

    DUK_DD(DUK_DDPRINT("regexp compiler ctx initialized, flags=0x%08lx, recursion_limit=%ld",
                       (unsigned long) re_ctx.re_flags, (long) re_ctx.recursion_limit));

    /*
     *  Init lexer
     */

    lex_point.offset = 0;  /* expensive init, just want to fill window */
    lex_point.line = 1;
    DUK_LEXER_SETPOINT(&re_ctx.lex, &lex_point);

    /*
     *  Compilation
     */

    DUK_DD(DUK_DDPRINT("starting regexp compilation"));

    duk__append_reop(&re_ctx, DUK_REOP_SAVE);
    duk__append_7bit(&re_ctx, 0);
    duk__parse_disjunction(&re_ctx, 1 /*expect_eof*/, &ign_disj);
    duk__append_reop(&re_ctx, DUK_REOP_SAVE);
    duk__append_7bit(&re_ctx, 1);
    duk__append_reop(&re_ctx, DUK_REOP_MATCH);

    /*
     *  Check for invalid backreferences; note that it is NOT an error
     *  to back-reference a capture group which has not yet been introduced
     *  in the pattern (as in /\1(foo)/); in fact, the backreference will
     *  always match!  It IS an error to back-reference a capture group
     *  which will never be introduced in the pattern.  Thus, we can check
     *  for such references only after parsing is complete.
     */

    if (re_ctx.highest_backref > re_ctx.captures) {
        DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_BACKREFS);
        DUK_WO_NORETURN(return;);
    }

    /*
     *  Emit compiled regexp header: flags, ncaptures
     *  (insertion order inverted on purpose)
     */

    duk__insert_u32(&re_ctx, 0, (re_ctx.captures + 1) * 2);
    duk__insert_u32(&re_ctx, 0, re_ctx.re_flags);

    /* [ ... pattern flags escaped_source buffer ] */

    DUK_BW_COMPACT(thr, &re_ctx.bw);
    (void) duk_buffer_to_string(thr, -1);  /* Safe because flags is at most 7 bit. */

    /* [ ... pattern flags escaped_source bytecode ] */

    /*
     *  Finalize stack
     */

    duk_remove(thr, -4);     /* -> [ ... flags escaped_source bytecode ] */
    duk_remove(thr, -3);     /* -> [ ... escaped_source bytecode ] */

    DUK_DD(DUK_DDPRINT("regexp compilation successful, bytecode: %!T, escaped source: %!T",
                       (duk_tval *) duk_get_tval(thr, -1), (duk_tval *) duk_get_tval(thr, -2)));
}

/*
 *  Create a RegExp instance (E5 Section 15.10.7).
 *
 *  Note: the output stack left by duk_regexp_compile() is directly compatible
 *  with the input here.
 *
 *  Input stack:  [ escaped_source bytecode ]  (both as strings)
 *  Output stack: [ RegExp ]
 */

DUK_INTERNAL void duk_regexp_create_instance(duk_hthread *thr) {
    duk_hobject *h;

    /* [ ... escaped_source bytecode ] */

    duk_push_object(thr);
    h = duk_known_hobject(thr, -1);
    duk_insert(thr, -3);

    /* [ ... regexp_object escaped_source bytecode ] */

    DUK_HOBJECT_SET_CLASS_NUMBER(h, DUK_HOBJECT_CLASS_REGEXP);
    DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, thr->builtins[DUK_BIDX_REGEXP_PROTOTYPE]);

    duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_INT_BYTECODE, DUK_PROPDESC_FLAGS_NONE);

    /* [ ... regexp_object escaped_source ] */

    /* In ES2015 .source, and the .global, .multiline, etc flags are
     * inherited getters.  Store the escaped source as an internal
     * property for the getter.
     */

    duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_SOURCE, DUK_PROPDESC_FLAGS_NONE);

    /* [ ... regexp_object ] */

    duk_push_int(thr, 0);
    duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LAST_INDEX, DUK_PROPDESC_FLAGS_W);

    /* [ ... regexp_object ] */
}

#else  /* DUK_USE_REGEXP_SUPPORT */

/* regexp support disabled */

#endif  /* DUK_USE_REGEXP_SUPPORT */

/* automatic undefs */
#undef DUK__RE_BUFLEN
#undef DUK__RE_INITIAL_BUFSIZE
//#line 1 "duk_regexp_executor.c"
/*
 *  Regexp executor.
 *
 *  Safety: the ECMAScript executor should prevent user from reading and
 *  replacing regexp bytecode.  Even so, the executor must validate all
 *  memory accesses etc.  When an invalid access is detected (e.g. a 'save'
 *  opcode to invalid, unallocated index) it should fail with an internal
 *  error but not cause a segmentation fault.
 *
 *  Notes:
 *
 *    - Backtrack counts are limited to unsigned 32 bits but should
 *      technically be duk_size_t for strings longer than 4G chars.
 *      This also requires a regexp bytecode change.
 */

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

#if defined(DUK_USE_REGEXP_SUPPORT)

/*
 *  Helpers for UTF-8 handling
 *
 *  For bytecode readers the duk_uint32_t and duk_int32_t types are correct
 *  because they're used for more than just codepoints.
 */

DUK_LOCAL duk_uint32_t duk__bc_get_u32(duk_re_matcher_ctx *re_ctx, const duk_uint8_t **pc) {
    return (duk_uint32_t) duk_unicode_decode_xutf8_checked(re_ctx->thr, pc, re_ctx->bytecode, re_ctx->bytecode_end);
}

DUK_LOCAL duk_int32_t duk__bc_get_i32(duk_re_matcher_ctx *re_ctx, const duk_uint8_t **pc) {
    duk_uint32_t t;

    /* signed integer encoding needed to work with UTF-8 */
    t = (duk_uint32_t) duk_unicode_decode_xutf8_checked(re_ctx->thr, pc, re_ctx->bytecode, re_ctx->bytecode_end);
    if (t & 1) {
        return -((duk_int32_t) (t >> 1));
    } else {
        return (duk_int32_t) (t >> 1);
    }
}

DUK_LOCAL const duk_uint8_t *duk__utf8_backtrack(duk_hthread *thr, const duk_uint8_t **ptr, const duk_uint8_t *ptr_start, const duk_uint8_t *ptr_end, duk_uint_fast32_t count) {
    const duk_uint8_t *p;

    /* Note: allow backtracking from p == ptr_end */
    p = *ptr;
    if (p < ptr_start || p > ptr_end) {
        goto fail;
    }

    while (count > 0) {
        for (;;) {
            p--;
            if (p < ptr_start) {
                goto fail;
            }
            if ((*p & 0xc0) != 0x80) {
                /* utf-8 continuation bytes have the form 10xx xxxx */
                break;
            }
        }
        count--;
    }
    *ptr = p;
    return p;

 fail:
    DUK_ERROR_INTERNAL(thr);
    DUK_WO_NORETURN(return NULL;);
}

DUK_LOCAL const duk_uint8_t *duk__utf8_advance(duk_hthread *thr, const duk_uint8_t **ptr, const duk_uint8_t *ptr_start, const duk_uint8_t *ptr_end, duk_uint_fast32_t count) {
    const duk_uint8_t *p;

    p = *ptr;
    if (p < ptr_start || p >= ptr_end) {
        goto fail;
    }

    while (count > 0) {
        for (;;) {
            p++;

            /* Note: if encoding ends by hitting end of input, we don't check that
             * the encoding is valid, we just assume it is.
             */
            if (p >= ptr_end || ((*p & 0xc0) != 0x80)) {
                /* utf-8 continuation bytes have the form 10xx xxxx */
                break;
            }
        }
        count--;
    }

    *ptr = p;
    return p;

 fail:
    DUK_ERROR_INTERNAL(thr);
    DUK_WO_NORETURN(return NULL;);
}

/*
 *  Helpers for dealing with the input string
 */

/* Get a (possibly canonicalized) input character from current sp.  The input
 * itself is never modified, and captures always record non-canonicalized
 * characters even in case-insensitive matching.  Return <0 if out of input.
 */
DUK_LOCAL duk_codepoint_t duk__inp_get_cp(duk_re_matcher_ctx *re_ctx, const duk_uint8_t **sp) {
    duk_codepoint_t res;

    if (*sp >= re_ctx->input_end) {
        return -1;
    }
    res = (duk_codepoint_t) duk_unicode_decode_xutf8_checked(re_ctx->thr, sp, re_ctx->input, re_ctx->input_end);
    if (re_ctx->re_flags & DUK_RE_FLAG_IGNORE_CASE) {
        res = duk_unicode_re_canonicalize_char(re_ctx->thr, res);
    }
    return res;
}

DUK_LOCAL const duk_uint8_t *duk__inp_backtrack(duk_re_matcher_ctx *re_ctx, const duk_uint8_t **sp, duk_uint_fast32_t count) {
    return duk__utf8_backtrack(re_ctx->thr, sp, re_ctx->input, re_ctx->input_end, count);
}

/* Backtrack utf-8 input and return a (possibly canonicalized) input character. */
DUK_LOCAL duk_codepoint_t duk__inp_get_prev_cp(duk_re_matcher_ctx *re_ctx, const duk_uint8_t *sp) {
    /* note: caller 'sp' is intentionally not updated here */
    (void) duk__inp_backtrack(re_ctx, &sp, (duk_uint_fast32_t) 1);
    return duk__inp_get_cp(re_ctx, &sp);
}

/*
 *  Regexp recursive matching function.
 *
 *  Returns 'sp' on successful match (points to character after last matched one),
 *  NULL otherwise.
 *
 *  The C recursion depth limit check is only performed in this function, this
 *  suffices because the function is present in all true recursion required by
 *  regexp execution.
 */

DUK_LOCAL const duk_uint8_t *duk__match_regexp(duk_re_matcher_ctx *re_ctx, const duk_uint8_t *pc, const duk_uint8_t *sp) {
    duk_native_stack_check(re_ctx->thr);
    if (re_ctx->recursion_depth >= re_ctx->recursion_limit) {
        DUK_ERROR_RANGE(re_ctx->thr, DUK_STR_REGEXP_EXECUTOR_RECURSION_LIMIT);
        DUK_WO_NORETURN(return NULL;);
    }
    re_ctx->recursion_depth++;

    for (;;) {
        duk_small_int_t op;

        if (re_ctx->steps_count >= re_ctx->steps_limit) {
            DUK_ERROR_RANGE(re_ctx->thr, DUK_STR_REGEXP_EXECUTOR_STEP_LIMIT);
            DUK_WO_NORETURN(return NULL;);
        }
        re_ctx->steps_count++;

        /* Opcodes are at most 7 bits now so they encode to one byte.  If this
         * were not the case or 'pc' is invalid here (due to a bug etc) we'll
         * still fail safely through the switch default case.
         */
        DUK_ASSERT(pc[0] <= 0x7fU);
#if 0
        op = (duk_small_int_t) duk__bc_get_u32(re_ctx, &pc);
#endif
        op = *pc++;

        DUK_DDD(DUK_DDDPRINT("match: rec=%ld, steps=%ld, pc (after op)=%ld, sp=%ld, op=%ld",
                             (long) re_ctx->recursion_depth,
                             (long) re_ctx->steps_count,
                             (long) (pc - re_ctx->bytecode),
                             (long) (sp - re_ctx->input),
                             (long) op));

        switch (op) {
        case DUK_REOP_MATCH: {
            goto match;
        }
        case DUK_REOP_CHAR: {
            /*
             *  Byte-based matching would be possible for case-sensitive
             *  matching but not for case-insensitive matching.  So, we
             *  match by decoding the input and bytecode character normally.
             *
             *  Bytecode characters are assumed to be already canonicalized.
             *  Input characters are canonicalized automatically by
             *  duk__inp_get_cp() if necessary.
             *
             *  There is no opcode for matching multiple characters.  The
             *  regexp compiler has trouble joining strings efficiently
             *  during compilation.  See doc/regexp.rst for more discussion.
             */
            duk_codepoint_t c1, c2;

            c1 = (duk_codepoint_t) duk__bc_get_u32(re_ctx, &pc);
            DUK_ASSERT(!(re_ctx->re_flags & DUK_RE_FLAG_IGNORE_CASE) ||
                       c1 == duk_unicode_re_canonicalize_char(re_ctx->thr, c1));  /* canonicalized by compiler */
            c2 = duk__inp_get_cp(re_ctx, &sp);
            /* No need to check for c2 < 0 (end of input): because c1 >= 0, it
             * will fail the match below automatically and cause goto fail.
             */
#if 0
            if (c2 < 0) {
                goto fail;
            }
#endif
            DUK_ASSERT(c1 >= 0);

            DUK_DDD(DUK_DDDPRINT("char match, c1=%ld, c2=%ld", (long) c1, (long) c2));
            if (c1 != c2) {
                goto fail;
            }
            break;
        }
        case DUK_REOP_PERIOD: {
            duk_codepoint_t c;

            c = duk__inp_get_cp(re_ctx, &sp);
            if (c < 0 || duk_unicode_is_line_terminator(c)) {
                /* E5 Sections 15.10.2.8, 7.3 */
                goto fail;
            }
            break;
        }
        case DUK_REOP_RANGES:
        case DUK_REOP_INVRANGES: {
            duk_uint32_t n;
            duk_codepoint_t c;
            duk_small_int_t match;

            n = duk__bc_get_u32(re_ctx, &pc);
            c = duk__inp_get_cp(re_ctx, &sp);
            if (c < 0) {
                goto fail;
            }

            match = 0;
            while (n) {
                duk_codepoint_t r1, r2;
                r1 = (duk_codepoint_t) duk__bc_get_u32(re_ctx, &pc);
                r2 = (duk_codepoint_t) duk__bc_get_u32(re_ctx, &pc);
                DUK_DDD(DUK_DDDPRINT("matching ranges/invranges, n=%ld, r1=%ld, r2=%ld, c=%ld",
                                     (long) n, (long) r1, (long) r2, (long) c));
                if (c >= r1 && c <= r2) {
                    /* Note: don't bail out early, we must read all the ranges from
                     * bytecode.  Another option is to skip them efficiently after
                     * breaking out of here.  Prefer smallest code.
                     */
                    match = 1;
                }
                n--;
            }

            if (op == DUK_REOP_RANGES) {
                if (!match) {
                    goto fail;
                }
            } else {
                DUK_ASSERT(op == DUK_REOP_INVRANGES);
                if (match) {
                    goto fail;
                }
            }
            break;
        }
        case DUK_REOP_ASSERT_START: {
            duk_codepoint_t c;

            if (sp <= re_ctx->input) {
                break;
            }
            if (!(re_ctx->re_flags & DUK_RE_FLAG_MULTILINE)) {
                goto fail;
            }
            c = duk__inp_get_prev_cp(re_ctx, sp);
            if (duk_unicode_is_line_terminator(c)) {
                /* E5 Sections 15.10.2.8, 7.3 */
                break;
            }
            goto fail;
        }
        case DUK_REOP_ASSERT_END: {
            duk_codepoint_t c;
            const duk_uint8_t *tmp_sp;

            tmp_sp = sp;
            c = duk__inp_get_cp(re_ctx, &tmp_sp);
            if (c < 0) {
                break;
            }
            if (!(re_ctx->re_flags & DUK_RE_FLAG_MULTILINE)) {
                goto fail;
            }
            if (duk_unicode_is_line_terminator(c)) {
                /* E5 Sections 15.10.2.8, 7.3 */
                break;
            }
            goto fail;
        }
        case DUK_REOP_ASSERT_WORD_BOUNDARY:
        case DUK_REOP_ASSERT_NOT_WORD_BOUNDARY: {
            /*
             *  E5 Section 15.10.2.6.  The previous and current character
             *  should -not- be canonicalized as they are now.  However,
             *  canonicalization does not affect the result of IsWordChar()
             *  (which depends on Unicode characters never canonicalizing
             *  into ASCII characters) so this does not matter.
             */
            duk_small_int_t w1, w2;

            if (sp <= re_ctx->input) {
                w1 = 0;  /* not a wordchar */
            } else {
                duk_codepoint_t c;
                c = duk__inp_get_prev_cp(re_ctx, sp);
                w1 = duk_unicode_re_is_wordchar(c);
            }
            if (sp >= re_ctx->input_end) {
                w2 = 0;  /* not a wordchar */
            } else {
                const duk_uint8_t *tmp_sp = sp;  /* dummy so sp won't get updated */
                duk_codepoint_t c;
                c = duk__inp_get_cp(re_ctx, &tmp_sp);
                w2 = duk_unicode_re_is_wordchar(c);
            }

            if (op == DUK_REOP_ASSERT_WORD_BOUNDARY) {
                if (w1 == w2) {
                    goto fail;
                }
            } else {
                DUK_ASSERT(op == DUK_REOP_ASSERT_NOT_WORD_BOUNDARY);
                if (w1 != w2) {
                    goto fail;
                }
            }
            break;
        }
        case DUK_REOP_JUMP: {
            duk_int32_t skip;

            skip = duk__bc_get_i32(re_ctx, &pc);
            pc += skip;
            break;
        }
        case DUK_REOP_SPLIT1: {
            /* split1: prefer direct execution (no jump) */
            const duk_uint8_t *sub_sp;
            duk_int32_t skip;

            skip = duk__bc_get_i32(re_ctx, &pc);
            sub_sp = duk__match_regexp(re_ctx, pc, sp);
            if (sub_sp) {
                sp = sub_sp;
                goto match;
            }
            pc += skip;
            break;
        }
        case DUK_REOP_SPLIT2: {
            /* split2: prefer jump execution (not direct) */
            const duk_uint8_t *sub_sp;
            duk_int32_t skip;

            skip = duk__bc_get_i32(re_ctx, &pc);
            sub_sp = duk__match_regexp(re_ctx, pc + skip, sp);
            if (sub_sp) {
                sp = sub_sp;
                goto match;
            }
            break;
        }
        case DUK_REOP_SQMINIMAL: {
            duk_uint32_t q, qmin, qmax;
            duk_int32_t skip;
            const duk_uint8_t *sub_sp;

            qmin = duk__bc_get_u32(re_ctx, &pc);
            qmax = duk__bc_get_u32(re_ctx, &pc);
            skip = duk__bc_get_i32(re_ctx, &pc);
            DUK_DDD(DUK_DDDPRINT("minimal quantifier, qmin=%lu, qmax=%lu, skip=%ld",
                                 (unsigned long) qmin, (unsigned long) qmax, (long) skip));

            q = 0;
            while (q <= qmax) {
                if (q >= qmin) {
                    sub_sp = duk__match_regexp(re_ctx, pc + skip, sp);
                    if (sub_sp) {
                        sp = sub_sp;
                        goto match;
                    }
                }
                sub_sp = duk__match_regexp(re_ctx, pc, sp);
                if (!sub_sp) {
                    break;
                }
                sp = sub_sp;
                q++;
            }
            goto fail;
        }
        case DUK_REOP_SQGREEDY: {
            duk_uint32_t q, qmin, qmax, atomlen;
            duk_int32_t skip;
            const duk_uint8_t *sub_sp;

            qmin = duk__bc_get_u32(re_ctx, &pc);
            qmax = duk__bc_get_u32(re_ctx, &pc);
            atomlen = duk__bc_get_u32(re_ctx, &pc);
            skip = duk__bc_get_i32(re_ctx, &pc);
            DUK_DDD(DUK_DDDPRINT("greedy quantifier, qmin=%lu, qmax=%lu, atomlen=%lu, skip=%ld",
                                 (unsigned long) qmin, (unsigned long) qmax, (unsigned long) atomlen, (long) skip));

            q = 0;
            while (q < qmax) {
                sub_sp = duk__match_regexp(re_ctx, pc, sp);
                if (!sub_sp) {
                    break;
                }
                sp = sub_sp;
                q++;
            }
            while (q >= qmin) {
                sub_sp = duk__match_regexp(re_ctx, pc + skip, sp);
                if (sub_sp) {
                    sp = sub_sp;
                    goto match;
                }
                if (q == qmin) {
                    break;
                }

                /* Note: if atom were to contain e.g. captures, we would need to
                 * re-match the atom to get correct captures.  Simply quantifiers
                 * do not allow captures in their atom now, so this is not an issue.
                 */

                DUK_DDD(DUK_DDDPRINT("greedy quantifier, backtrack %ld characters (atomlen)",
                                     (long) atomlen));
                sp = duk__inp_backtrack(re_ctx, &sp, (duk_uint_fast32_t) atomlen);
                q--;
            }
            goto fail;
        }
        case DUK_REOP_SAVE: {
            duk_uint32_t idx;
            const duk_uint8_t *old;
            const duk_uint8_t *sub_sp;

            idx = duk__bc_get_u32(re_ctx, &pc);
            if (idx >= re_ctx->nsaved) {
                /* idx is unsigned, < 0 check is not necessary */
                DUK_D(DUK_DPRINT("internal error, regexp save index insane: idx=%ld", (long) idx));
                goto internal_error;
            }
            old = re_ctx->saved[idx];
            re_ctx->saved[idx] = sp;
            sub_sp = duk__match_regexp(re_ctx, pc, sp);
            if (sub_sp) {
                sp = sub_sp;
                goto match;
            }
            re_ctx->saved[idx] = old;
            goto fail;
        }
        case DUK_REOP_WIPERANGE: {
            /* Wipe capture range and save old values for backtracking.
             *
             * XXX: this typically happens with a relatively small idx_count.
             * It might be useful to handle cases where the count is small
             * (say <= 8) by saving the values in stack instead.  This would
             * reduce memory churn and improve performance, at the cost of a
             * slightly higher code footprint.
             */
            duk_uint32_t idx_start, idx_count;
#if defined(DUK_USE_EXPLICIT_NULL_INIT)
            duk_uint32_t idx_end, idx;
#endif
            duk_uint8_t **range_save;
            const duk_uint8_t *sub_sp;

            idx_start = duk__bc_get_u32(re_ctx, &pc);
            idx_count = duk__bc_get_u32(re_ctx, &pc);
            DUK_DDD(DUK_DDDPRINT("wipe saved range: start=%ld, count=%ld -> [%ld,%ld] (captures [%ld,%ld])",
                                 (long) idx_start, (long) idx_count,
                                 (long) idx_start, (long) (idx_start + idx_count - 1),
                                 (long) (idx_start / 2), (long) ((idx_start + idx_count - 1) / 2)));
            if (idx_start + idx_count > re_ctx->nsaved || idx_count == 0) {
                /* idx is unsigned, < 0 check is not necessary */
                DUK_D(DUK_DPRINT("internal error, regexp wipe indices insane: idx_start=%ld, idx_count=%ld",
                                 (long) idx_start, (long) idx_count));
                goto internal_error;
            }
            DUK_ASSERT(idx_count > 0);

            duk_require_stack(re_ctx->thr, 1);
            range_save = (duk_uint8_t **) duk_push_fixed_buffer_nozero(re_ctx->thr,
                                                                       sizeof(duk_uint8_t *) * idx_count);
            DUK_ASSERT(range_save != NULL);
            duk_memcpy(range_save, re_ctx->saved + idx_start, sizeof(duk_uint8_t *) * idx_count);
#if defined(DUK_USE_EXPLICIT_NULL_INIT)
            idx_end = idx_start + idx_count;
            for (idx = idx_start; idx < idx_end; idx++) {
                re_ctx->saved[idx] = NULL;
            }
#else
            duk_memzero((void *) (re_ctx->saved + idx_start), sizeof(duk_uint8_t *) * idx_count);
#endif

            sub_sp = duk__match_regexp(re_ctx, pc, sp);
            if (sub_sp) {
                /* match: keep wiped/resaved values */
                DUK_DDD(DUK_DDDPRINT("match: keep wiped/resaved values [%ld,%ld] (captures [%ld,%ld])",
                                     (long) idx_start, (long) (idx_start + idx_count - 1),
                                         (long) (idx_start / 2), (long) ((idx_start + idx_count - 1) / 2)));
                duk_pop_unsafe(re_ctx->thr);
                sp = sub_sp;
                goto match;
            }

            /* fail: restore saves */
            DUK_DDD(DUK_DDDPRINT("fail: restore wiped/resaved values [%ld,%ld] (captures [%ld,%ld])",
                                 (long) idx_start, (long) (idx_start + idx_count - 1),
                                 (long) (idx_start / 2), (long) ((idx_start + idx_count - 1) / 2)));
            duk_memcpy((void *) (re_ctx->saved + idx_start),
                       (const void *) range_save,
                       sizeof(duk_uint8_t *) * idx_count);
            duk_pop_unsafe(re_ctx->thr);
            goto fail;
        }
        case DUK_REOP_LOOKPOS:
        case DUK_REOP_LOOKNEG: {
            /*
             *  Needs a save of multiple saved[] entries depending on what range
             *  may be overwritten.  Because the regexp parser does no such analysis,
             *  we currently save the entire saved array here.  Lookaheads are thus
             *  a bit expensive.  Note that the saved array is not needed for just
             *  the lookahead sub-match, but for the matching of the entire sequel.
             *
             *  The temporary save buffer is pushed on to the valstack to handle
             *  errors correctly.  Each lookahead causes a C recursion and pushes
             *  more stuff on the value stack.  If the C recursion limit is less
             *  than the value stack slack, there is no need to check the stack.
             *  We do so regardless, just in case.
             */

            duk_int32_t skip;
            duk_uint8_t **full_save;
            const duk_uint8_t *sub_sp;

            DUK_ASSERT(re_ctx->nsaved > 0);

            duk_require_stack(re_ctx->thr, 1);
            full_save = (duk_uint8_t **) duk_push_fixed_buffer_nozero(re_ctx->thr,
                                                                      sizeof(duk_uint8_t *) * re_ctx->nsaved);
            DUK_ASSERT(full_save != NULL);
            duk_memcpy(full_save, re_ctx->saved, sizeof(duk_uint8_t *) * re_ctx->nsaved);

            skip = duk__bc_get_i32(re_ctx, &pc);
            sub_sp = duk__match_regexp(re_ctx, pc, sp);
            if (op == DUK_REOP_LOOKPOS) {
                if (!sub_sp) {
                    goto lookahead_fail;
                }
            } else {
                if (sub_sp) {
                    goto lookahead_fail;
                }
            }
            sub_sp = duk__match_regexp(re_ctx, pc + skip, sp);
            if (sub_sp) {
                /* match: keep saves */
                duk_pop_unsafe(re_ctx->thr);
                sp = sub_sp;
                goto match;
            }

            /* fall through */

         lookahead_fail:
            /* fail: restore saves */
            duk_memcpy((void *) re_ctx->saved,
                       (const void *) full_save,
                       sizeof(duk_uint8_t *) * re_ctx->nsaved);
            duk_pop_unsafe(re_ctx->thr);
            goto fail;
        }
        case DUK_REOP_BACKREFERENCE: {
            /*
             *  Byte matching for back-references would be OK in case-
             *  sensitive matching.  In case-insensitive matching we need
             *  to canonicalize characters, so back-reference matching needs
             *  to be done with codepoints instead.  So, we just decode
             *  everything normally here, too.
             *
             *  Note: back-reference index which is 0 or higher than
             *  NCapturingParens (= number of capturing parens in the
             *  -entire- regexp) is a compile time error.  However, a
             *  backreference referring to a valid capture which has
             *  not matched anything always succeeds!  See E5 Section
             *  15.10.2.9, step 5, sub-step 3.
             */
            duk_uint32_t idx;
            const duk_uint8_t *p;

            idx = duk__bc_get_u32(re_ctx, &pc);
            idx = idx << 1;  /* backref n -> saved indices [n*2, n*2+1] */
            if (idx < 2 || idx + 1 >= re_ctx->nsaved) {
                /* regexp compiler should catch these */
                DUK_D(DUK_DPRINT("internal error, backreference index insane"));
                goto internal_error;
            }
            if (!re_ctx->saved[idx] || !re_ctx->saved[idx+1]) {
                /* capture is 'undefined', always matches! */
                DUK_DDD(DUK_DDDPRINT("backreference: saved[%ld,%ld] not complete, always match",
                                     (long) idx, (long) (idx + 1)));
                break;
            }
            DUK_DDD(DUK_DDDPRINT("backreference: match saved[%ld,%ld]", (long) idx, (long) (idx + 1)));

            p = re_ctx->saved[idx];
            while (p < re_ctx->saved[idx+1]) {
                duk_codepoint_t c1, c2;

                /* Note: not necessary to check p against re_ctx->input_end:
                 * the memory access is checked by duk__inp_get_cp(), while
                 * valid compiled regexps cannot write a saved[] entry
                 * which points to outside the string.
                 */
                c1 = duk__inp_get_cp(re_ctx, &p);
                DUK_ASSERT(c1 >= 0);
                c2 = duk__inp_get_cp(re_ctx, &sp);
                /* No need for an explicit c2 < 0 check: because c1 >= 0,
                 * the comparison will always fail if c2 < 0.
                 */
#if 0
                if (c2 < 0) {
                    goto fail;
                }
#endif
                if (c1 != c2) {
                    goto fail;
                }
            }
            break;
        }
        default: {
            DUK_D(DUK_DPRINT("internal error, regexp opcode error: %ld", (long) op));
            goto internal_error;
        }
        }
    }

 match:
    re_ctx->recursion_depth--;
    return sp;

 fail:
    re_ctx->recursion_depth--;
    return NULL;

 internal_error:
    DUK_ERROR_INTERNAL(re_ctx->thr);
    DUK_WO_NORETURN(return NULL;);
}

/*
 *  Exposed matcher function which provides the semantics of RegExp.prototype.exec().
 *
 *  RegExp.prototype.test() has the same semantics as exec() but does not return the
 *  result object (which contains the matching string and capture groups).  Currently
 *  there is no separate test() helper, so a temporary result object is created and
 *  discarded if test() is needed.  This is intentional, to save code space.
 *
 *  Input stack:  [ ... re_obj input ]
 *  Output stack: [ ... result ]
 */

DUK_LOCAL void duk__regexp_match_helper(duk_hthread *thr, duk_small_int_t force_global) {
    duk_re_matcher_ctx re_ctx;
    duk_hobject *h_regexp;
    duk_hstring *h_bytecode;
    duk_hstring *h_input;
    duk_uint8_t *p_buf;
    const duk_uint8_t *pc;
    const duk_uint8_t *sp;
    duk_small_int_t match = 0;
    duk_small_int_t global;
    duk_uint_fast32_t i;
    double d;
    duk_uint32_t char_offset;

    DUK_ASSERT(thr != NULL);

    DUK_DD(DUK_DDPRINT("regexp match: regexp=%!T, input=%!T",
                       (duk_tval *) duk_get_tval(thr, -2),
                       (duk_tval *) duk_get_tval(thr, -1)));

    /*
     *  Regexp instance check, bytecode check, input coercion.
     *
     *  See E5 Section 15.10.6.
     */

    /* TypeError if wrong; class check, see E5 Section 15.10.6 */
    h_regexp = duk_require_hobject_with_class(thr, -2, DUK_HOBJECT_CLASS_REGEXP);
    DUK_ASSERT(h_regexp != NULL);
    DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(h_regexp) == DUK_HOBJECT_CLASS_REGEXP);
    DUK_UNREF(h_regexp);

    h_input = duk_to_hstring(thr, -1);
    DUK_ASSERT(h_input != NULL);

    duk_xget_owndataprop_stridx_short(thr, -2, DUK_STRIDX_INT_BYTECODE);  /* [ ... re_obj input ] -> [ ... re_obj input bc ] */
    h_bytecode = duk_require_hstring(thr, -1);  /* no regexp instance should exist without a non-configurable bytecode property */
    DUK_ASSERT(h_bytecode != NULL);

    /*
     *  Basic context initialization.
     *
     *  Some init values are read from the bytecode header
     *  whose format is (UTF-8 codepoints):
     *
     *    uint   flags
     *    uint   nsaved (even, 2n+2 where n = num captures)
     */

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

    duk_memzero(&re_ctx, sizeof(re_ctx));

    re_ctx.thr = thr;
    re_ctx.input = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_input);
    re_ctx.input_end = re_ctx.input + DUK_HSTRING_GET_BYTELEN(h_input);
    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
         *      at every non-zero offset.
         */

        if (duk__match_regexp(&re_ctx, re_ctx.bytecode, sp) != NULL) {
            DUK_DDD(DUK_DDDPRINT("match at offset %ld", (long) char_offset));
            match = 1;
            break;
        }

        /* advance by one character (code point) and one char_offset */
        char_offset++;
        if (char_offset > DUK_HSTRING_GET_CHARLEN(h_input)) {
            /*
             *  Note:
             *
             *    - Intentionally attempt (empty) match at char_offset == k_input->clen
             *
             *    - Negative char_offsets have been eliminated and char_offset is duk_uint32_t
             *      -> no need or use for a negative check
             */

            DUK_DDD(DUK_DDDPRINT("no match after trying all sp offsets"));
            break;
        }

        /* avoid calling at end of input, will DUK_ERROR (above check suffices to avoid this) */
        (void) duk__utf8_advance(thr, &sp, re_ctx.input, re_ctx.input_end, (duk_uint_fast32_t) 1);
    }

 match_over:

    /*
     *  Matching complete, create result array or return a 'null'.  Update lastIndex
     *  if necessary.  See E5 Section 15.10.6.2.
     *
     *  Because lastIndex is a character (not byte) offset, we need the character
     *  length of the match which we conveniently get as a side effect of interning
     *  the matching substring (0th index of result array).
     *
     *  saved[0]         start pointer (~ byte offset) of current match
     *  saved[1]         end pointer (~ byte offset) of current match (exclusive)
     *  char_offset      start character offset of current match (-> .index of result)
     *  char_end_offset  end character offset (computed below)
     */

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

    if (match) {
#if defined(DUK_USE_ASSERTIONS)
        duk_hobject *h_res;
#endif
        duk_uint32_t char_end_offset = 0;

        DUK_DDD(DUK_DDDPRINT("regexp matches at char_offset %ld", (long) char_offset));

        DUK_ASSERT(re_ctx.nsaved >= 2);        /* must have start and end */
        DUK_ASSERT((re_ctx.nsaved % 2) == 0);  /* and even number */

        /* XXX: Array size is known before and (2 * re_ctx.nsaved) but not taken
         * advantage of now.  The array is not compacted either, as regexp match
         * objects are usually short lived.
         */

        duk_push_array(thr);

#if defined(DUK_USE_ASSERTIONS)
        h_res = duk_require_hobject(thr, -1);
        DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(h_res));
        DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_ARRAY(h_res));
        DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(h_res) == DUK_HOBJECT_CLASS_ARRAY);
#endif

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

        duk_push_u32(thr, char_offset);
        duk_xdef_prop_stridx_short_wec(thr, -2, DUK_STRIDX_INDEX);

        duk_dup_m4(thr);
        duk_xdef_prop_stridx_short_wec(thr, -2, DUK_STRIDX_INPUT);

        for (i = 0; i < re_ctx.nsaved; i += 2) {
            /* Captures which are undefined have NULL pointers and are returned
             * as 'undefined'.  The same is done when saved[] pointers are insane
             * (this should, of course, never happen in practice).
             */
            duk_push_uarridx(thr, (duk_uarridx_t) (i / 2));

            if (re_ctx.saved[i] && re_ctx.saved[i + 1] && re_ctx.saved[i + 1] >= re_ctx.saved[i]) {
                duk_push_lstring(thr,
                                 (const char *) re_ctx.saved[i],
                                 (duk_size_t) (re_ctx.saved[i + 1] - re_ctx.saved[i]));
                if (i == 0) {
                    /* Assumes that saved[0] and saved[1] are always
                     * set by regexp bytecode (if not, char_end_offset
                     * will be zero).  Also assumes clen reflects the
                     * correct char length.
                     */
                    char_end_offset = char_offset + (duk_uint32_t) duk_get_length(thr, -1);  /* add charlen */
                }
            } else {
                duk_push_undefined(thr);
            }

            /* [ ... re_obj input bc saved_buf res_obj idx val ] */
            duk_def_prop(thr, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WEC);
        }

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

        /* NB: 'length' property is automatically updated by the array setup loop */

        if (global) {
            /* global regexp: lastIndex updated on match */
            duk_push_u32(thr, char_end_offset);
            duk_put_prop_stridx_short(thr, -6, DUK_STRIDX_LAST_INDEX);
        } else {
            /* non-global regexp: lastIndex never updated on match */
            ;
        }
    } else {
        /*
         *  No match, E5 Section 15.10.6.2, step 9.a.i - 9.a.ii apply, regardless
         *  of 'global' flag of the RegExp.  In particular, if lastIndex is invalid
         *  initially, it is reset to zero.
         */

        DUK_DDD(DUK_DDDPRINT("regexp does not match"));

        duk_push_null(thr);

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

        duk_push_int(thr, 0);
        duk_put_prop_stridx_short(thr, -6, DUK_STRIDX_LAST_INDEX);
    }

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

    duk_insert(thr, -5);

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

    duk_pop_n_unsafe(thr, 4);

    /* [ ... res_obj ] */

    /* XXX: these last tricks are unnecessary if the function is made
     * a genuine native function.
     */
}

DUK_INTERNAL void duk_regexp_match(duk_hthread *thr) {
    duk__regexp_match_helper(thr, 0 /*force_global*/);
}

/* This variant is needed by String.prototype.split(); it needs to perform
 * global-style matching on a cloned RegExp which is potentially non-global.
 */
DUK_INTERNAL void duk_regexp_match_force_global(duk_hthread *thr) {
    duk__regexp_match_helper(thr, 1 /*force_global*/);
}

#else  /* DUK_USE_REGEXP_SUPPORT */

/* regexp support disabled */

#endif  /* DUK_USE_REGEXP_SUPPORT */
//#line 1 "duk_selftest.c"
/*
 *  Self tests to ensure execution environment is sane.  Intended to catch
 *  compiler/platform problems which cannot be detected at compile time.
 */

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

#if defined(DUK_USE_SELF_TESTS)

/*
 *  Unions and structs for self tests
 */

typedef union {
    double d;
    duk_uint8_t x[8];
} duk__test_double_union;

/* Self test failed.  Expects a local variable 'error_count' to exist. */
#define DUK__FAILED(msg)  do { \
        DUK_D(DUK_DPRINT("self test failed: " #msg " at " DUK_FILE_MACRO ":" DUK_MACRO_STRINGIFY(DUK_LINE_MACRO))); \
        error_count++; \
    } while (0)

#define DUK__DBLUNION_CMP_TRUE(a,b)  do { \
        if (duk_memcmp((const void *) (a), (const void *) (b), sizeof(duk__test_double_union)) != 0) { \
            DUK__FAILED("double union compares false (expected true)"); \
        } \
    } while (0)

#define DUK__DBLUNION_CMP_FALSE(a,b)  do { \
        if (duk_memcmp((const void *) (a), (const void *) (b), sizeof(duk__test_double_union)) == 0) { \
            DUK__FAILED("double union compares true (expected false)"); \
        } \
    } while (0)

typedef union {
    duk_uint32_t i;
    duk_uint8_t x[8];
} duk__test_u32_union;

#if defined(DUK_USE_INTEGER_LE)
#define DUK__U32_INIT(u, a, b, c, d) do { \
        (u)->x[0] = (d); (u)->x[1] = (c); (u)->x[2] = (b); (u)->x[3] = (a); \
    } while (0)
#elif defined(DUK_USE_INTEGER_ME)
#error integer mixed endian not supported now
#elif defined(DUK_USE_INTEGER_BE)
#define DUK__U32_INIT(u, a, b, c, d) do { \
        (u)->x[0] = (a); (u)->x[1] = (b); (u)->x[2] = (c); (u)->x[3] = (d); \
    } while (0)
#else
#error unknown integer endianness
#endif

#if defined(DUK_USE_DOUBLE_LE)
#define DUK__DOUBLE_INIT(u, a, b, c, d, e, f, g, h) do { \
        (u)->x[0] = (h); (u)->x[1] = (g); (u)->x[2] = (f); (u)->x[3] = (e); \
        (u)->x[4] = (d); (u)->x[5] = (c); (u)->x[6] = (b); (u)->x[7] = (a); \
    } while (0)
#define DUK__DOUBLE_COMPARE(u, a, b, c, d, e, f, g, h) \
    ((u)->x[0] == (h) && (u)->x[1] == (g) && (u)->x[2] == (f) && (u)->x[3] == (e) && \
     (u)->x[4] == (d) && (u)->x[5] == (c) && (u)->x[6] == (b) && (u)->x[7] == (a))
#elif defined(DUK_USE_DOUBLE_ME)
#define DUK__DOUBLE_INIT(u, a, b, c, d, e, f, g, h) do { \
        (u)->x[0] = (d); (u)->x[1] = (c); (u)->x[2] = (b); (u)->x[3] = (a); \
        (u)->x[4] = (h); (u)->x[5] = (g); (u)->x[6] = (f); (u)->x[7] = (e); \
    } while (0)
#define DUK__DOUBLE_COMPARE(u, a, b, c, d, e, f, g, h) \
    ((u)->x[0] == (d) && (u)->x[1] == (c) && (u)->x[2] == (b) && (u)->x[3] == (a) && \
     (u)->x[4] == (h) && (u)->x[5] == (g) && (u)->x[6] == (f) && (u)->x[7] == (e))
#elif defined(DUK_USE_DOUBLE_BE)
#define DUK__DOUBLE_INIT(u, a, b, c, d, e, f, g, h) do { \
        (u)->x[0] = (a); (u)->x[1] = (b); (u)->x[2] = (c); (u)->x[3] = (d); \
        (u)->x[4] = (e); (u)->x[5] = (f); (u)->x[6] = (g); (u)->x[7] = (h); \
    } while (0)
#define DUK__DOUBLE_COMPARE(u, a, b, c, d, e, f, g, h) \
    ((u)->x[0] == (a) && (u)->x[1] == (b) && (u)->x[2] == (c) && (u)->x[3] == (d) && \
     (u)->x[4] == (e) && (u)->x[5] == (f) && (u)->x[6] == (g) && (u)->x[7] == (h))
#else
#error unknown double endianness
#endif

/*
 *  Various sanity checks for typing
 */

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

    if (!(sizeof(duk_int8_t) == 1 &&
          sizeof(duk_uint8_t) == 1 &&
          sizeof(duk_int16_t) == 2 &&
          sizeof(duk_uint16_t) == 2 &&
          sizeof(duk_int32_t) == 4 &&
          sizeof(duk_uint32_t) == 4)) {
        DUK__FAILED("duk_(u)int{8,16,32}_t size");
    }
#if defined(DUK_USE_64BIT_OPS)
    if (!(sizeof(duk_int64_t) == 8 &&
          sizeof(duk_uint64_t) == 8)) {
        DUK__FAILED("duk_(u)int64_t size");
    }
#endif

    if (!(sizeof(duk_size_t) >= sizeof(duk_uint_t))) {
        /* Some internal code now assumes that all duk_uint_t values
         * can be expressed with a duk_size_t.
         */
        DUK__FAILED("duk_size_t is smaller than duk_uint_t");
    }
    if (!(sizeof(duk_int_t) >= 4)) {
        DUK__FAILED("duk_int_t is not 32 bits");
    }

    return error_count;
}

/*
 *  Packed tval sanity
 */

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

#if defined(DUK_USE_PACKED_TVAL)
    if (sizeof(void *) > 4) {
        DUK__FAILED("packed duk_tval in use but sizeof(void *) > 4");
    }
#endif

    return error_count;
}

/*
 *  Two's complement arithmetic.
 */

DUK_LOCAL duk_uint_t duk__selftest_twos_complement(void) {
    duk_uint_t error_count = 0;
    volatile int test;
    test = -1;

    /* Note that byte order doesn't affect this test: all bytes in
     * 'test' will be 0xFF for two's complement.
     */
    if (((volatile duk_uint8_t *) &test)[0] != (duk_uint8_t) 0xff) {
        DUK__FAILED("two's complement arithmetic");
    }

    return error_count;
}

/*
 *  Byte order.  Important to self check, because on some exotic platforms
 *  there is no actual detection but rather assumption based on platform
 *  defines.
 */

DUK_LOCAL duk_uint_t duk__selftest_byte_order(void) {
    duk_uint_t error_count = 0;
    duk__test_u32_union u1;
    duk__test_double_union u2;

    /*
     *  >>> struct.pack('>d', 102030405060).encode('hex')
     *  '4237c17c6dc40000'
     */

    DUK__U32_INIT(&u1, 0xde, 0xad, 0xbe, 0xef);
    DUK__DOUBLE_INIT(&u2, 0x42, 0x37, 0xc1, 0x7c, 0x6d, 0xc4, 0x00, 0x00);

    if (u1.i != (duk_uint32_t) 0xdeadbeefUL) {
        DUK__FAILED("duk_uint32_t byte order");
    }

    if (!duk_double_equals(u2.d, 102030405060.0)) {
        DUK__FAILED("double byte order");
    }

    return error_count;
}

/*
 *  DUK_BSWAP macros
 */

DUK_LOCAL duk_uint_t duk__selftest_bswap_macros(void) {
    duk_uint_t error_count = 0;
    volatile duk_uint32_t x32_input, x32_output;
    duk_uint32_t x32;
    volatile duk_uint16_t x16_input, x16_output;
    duk_uint16_t x16;
    duk_double_union du;
    duk_double_t du_diff;
#if defined(DUK_BSWAP64)
    volatile duk_uint64_t x64_input, x64_output;
    duk_uint64_t x64;
#endif

    /* Cover both compile time and runtime bswap operations, as these
     * may have different bugs.
     */

    x16_input = 0xbeefUL;
    x16 = x16_input;
    x16 = DUK_BSWAP16(x16);
    x16_output = x16;
    if (x16_output != (duk_uint16_t) 0xefbeUL) {
        DUK__FAILED("DUK_BSWAP16");
    }

    x16 = 0xbeefUL;
    x16 = DUK_BSWAP16(x16);
    if (x16 != (duk_uint16_t) 0xefbeUL) {
        DUK__FAILED("DUK_BSWAP16");
    }

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

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

#if defined(DUK_BSWAP64)
    x64_input = DUK_U64_CONSTANT(0x8899aabbccddeeff);
    x64 = x64_input;
    x64 = DUK_BSWAP64(x64);
    x64_output = x64;
    if (x64_output != (duk_uint64_t) DUK_U64_CONSTANT(0xffeeddccbbaa9988)) {
        DUK__FAILED("DUK_BSWAP64");
    }

    x64 = DUK_U64_CONSTANT(0x8899aabbccddeeff);
    x64 = DUK_BSWAP64(x64);
    if (x64 != (duk_uint64_t) DUK_U64_CONSTANT(0xffeeddccbbaa9988)) {
        DUK__FAILED("DUK_BSWAP64");
    }
#endif

    /* >>> 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. */
    fesetround(FE_TONEAREST);
#endif

    /* Rounding tests check that none of the other modes (round to
     * +Inf, round to -Inf, round to zero) can be active:
     * http://www.gnu.org/software/libc/manual/html_node/Rounding.html
     */

    /* 1.0 + 2^(-53): result is midway between 1.0 and 1.0 + ulp.
     * Round to nearest: 1.0
     * Round to +Inf:    1.0 + ulp
     * Round to -Inf:    1.0
     * Round to zero:    1.0
     * => Correct result eliminates round to +Inf.
     */
    DUK__DOUBLE_INIT(&a, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
    DUK__DOUBLE_INIT(&b, 0x3c, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
    duk_memset((void *) &c, 0, sizeof(c));
    c.d = a.d + b.d;
    if (!DUK__DOUBLE_COMPARE(&c, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)) {
        DUK_D(DUK_DPRINT("broken result (native endiannesss): %02x %02x %02x %02x %02x %02x %02x %02x",
                         (unsigned int) c.x[0], (unsigned int) c.x[1],
                         (unsigned int) c.x[2], (unsigned int) c.x[3],
                         (unsigned int) c.x[4], (unsigned int) c.x[5],
                         (unsigned int) c.x[6], (unsigned int) c.x[7]));
        DUK__FAILED("invalid result from 1.0 + 0.5ulp");
    }

    /* (1.0 + ulp) + 2^(-53): result is midway between 1.0 + ulp and 1.0 + 2*ulp.
     * Round to nearest: 1.0 + 2*ulp (round to even mantissa)
     * Round to +Inf:    1.0 + 2*ulp
     * Round to -Inf:    1.0 + ulp
     * Round to zero:    1.0 + ulp
     * => Correct result eliminates round to -Inf and round to zero.
     */
    DUK__DOUBLE_INIT(&a, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01);
    DUK__DOUBLE_INIT(&b, 0x3c, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
    duk_memset((void *) &c, 0, sizeof(c));
    c.d = a.d + b.d;
    if (!DUK__DOUBLE_COMPARE(&c, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02)) {
        DUK_D(DUK_DPRINT("broken result (native endiannesss): %02x %02x %02x %02x %02x %02x %02x %02x",
                         (unsigned int) c.x[0], (unsigned int) c.x[1],
                         (unsigned int) c.x[2], (unsigned int) c.x[3],
                         (unsigned int) c.x[4], (unsigned int) c.x[5],
                         (unsigned int) c.x[6], (unsigned int) c.x[7]));
        DUK__FAILED("invalid result from (1.0 + ulp) + 0.5ulp");
    }

    /* Could do negative number testing too, but the tests above should
     * differentiate between IEEE 754 rounding modes.
     */
    return error_count;
}

/*
 *  fmod(): often a portability issue in embedded or bare platform targets.
 *  Check for at least minimally correct behavior.  Unlike some other math
 *  functions (like cos()) Duktape relies on fmod() internally too.
 */

DUK_LOCAL duk_uint_t duk__selftest_fmod(void) {
    duk_uint_t error_count = 0;
    duk__test_double_union u1, u2;
    volatile duk_double_t t1, t2, t3;

    /* fmod() with integer argument and exponent 2^32 is used by e.g.
     * ToUint32() and some Duktape internals.
     */
    u1.d = DUK_FMOD(10.0, 4294967296.0);
    u2.d = 10.0;
    DUK__DBLUNION_CMP_TRUE(&u1, &u2);

    u1.d = DUK_FMOD(4294967306.0, 4294967296.0);
    u2.d = 10.0;
    DUK__DBLUNION_CMP_TRUE(&u1, &u2);

    u1.d = DUK_FMOD(73014444042.0, 4294967296.0);
    u2.d = 10.0;
    DUK__DBLUNION_CMP_TRUE(&u1, &u2);

    /* 52-bit integer split into two parts:
     * >>> 0x1fedcba9876543
     * 8987183256397123
     * >>> float(0x1fedcba9876543) / float(2**53)
     * 0.9977777777777778
     */
    u1.d = DUK_FMOD(8987183256397123.0, 4294967296.0);
    u2.d = (duk_double_t) 0xa9876543UL;
    DUK__DBLUNION_CMP_TRUE(&u1, &u2);
    t1 = 8987183256397123.0;
    t2 = 4294967296.0;
    t3 = t1 / t2;
    u1.d = DUK_FLOOR(t3);
    u2.d = (duk_double_t) 0x1fedcbUL;
    DUK__DBLUNION_CMP_TRUE(&u1, &u2);

    /* C99 behavior is for fmod() result sign to mathc argument sign. */
    u1.d = DUK_FMOD(-10.0, 4294967296.0);
    u2.d = -10.0;
    DUK__DBLUNION_CMP_TRUE(&u1, &u2);

    u1.d = DUK_FMOD(-4294967306.0, 4294967296.0);
    u2.d = -10.0;
    DUK__DBLUNION_CMP_TRUE(&u1, &u2);

    u1.d = DUK_FMOD(-73014444042.0, 4294967296.0);
    u2.d = -10.0;
    DUK__DBLUNION_CMP_TRUE(&u1, &u2);

    return error_count;
}

/*
 *  Struct size/alignment if platform requires it
 *
 *  There are some compiler specific struct padding pragmas etc in use, this
 *  selftest ensures they're correctly detected and used.
 */

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

#if (DUK_USE_ALIGN_BY == 4)
    if ((sizeof(duk_hbuffer_fixed) % 4) != 0) {
        DUK__FAILED("sizeof(duk_hbuffer_fixed) not aligned to 4");
    }
#elif (DUK_USE_ALIGN_BY == 8)
    if ((sizeof(duk_hbuffer_fixed) % 8) != 0) {
        DUK__FAILED("sizeof(duk_hbuffer_fixed) not aligned to 8");
    }
#elif (DUK_USE_ALIGN_BY == 1)
    /* no check */
#else
#error invalid DUK_USE_ALIGN_BY
#endif
    return error_count;
}

/*
 *  64-bit arithmetic
 *
 *  There are some platforms/compilers where 64-bit types are available
 *  but don't work correctly.  Test for known cases.
 */

DUK_LOCAL duk_uint_t duk__selftest_64bit_arithmetic(void) {
    duk_uint_t error_count = 0;
#if defined(DUK_USE_64BIT_OPS)
    volatile duk_int64_t i;
    volatile duk_double_t d;

    /* Catch a double-to-int64 cast issue encountered in practice. */
    d = 2147483648.0;
    i = (duk_int64_t) d;
    if (i != DUK_I64_CONSTANT(0x80000000)) {
        DUK__FAILED("casting 2147483648.0 to duk_int64_t failed");
    }
#else
    /* nop */
#endif
    return error_count;
}

/*
 *  Casting
 */

DUK_LOCAL duk_uint_t duk__selftest_cast_double_to_small_uint(void) {
    /*
     *  https://github.com/svaarala/duktape/issues/127#issuecomment-77863473
     */

    duk_uint_t error_count = 0;

    duk_double_t d1, d2;
    duk_small_uint_t u;

    duk_double_t d1v, d2v;
    duk_small_uint_t uv;

    /* Test without volatiles */

    d1 = 1.0;
    u = (duk_small_uint_t) d1;
    d2 = (duk_double_t) u;

    if (!(duk_double_equals(d1, 1.0) && u == 1 && duk_double_equals(d2, 1.0) && duk_double_equals(d1, d2))) {
        DUK__FAILED("double to duk_small_uint_t cast failed");
    }

    /* Same test with volatiles */

    d1v = 1.0;
    uv = (duk_small_uint_t) d1v;
    d2v = (duk_double_t) uv;

    if (!(duk_double_equals(d1v, 1.0) && uv == 1 && duk_double_equals(d2v, 1.0) && duk_double_equals(d1v, d2v))) {
        DUK__FAILED("double to duk_small_uint_t cast failed");
    }

    return error_count;
}

DUK_LOCAL duk_uint_t duk__selftest_cast_double_to_uint32(void) {
    /*
     *  This test fails on an exotic ARM target; double-to-uint
     *  cast is incorrectly clamped to -signed- int highest value.
     *
     *  https://github.com/svaarala/duktape/issues/336
     */

    duk_uint_t error_count = 0;
    duk_double_t dv;
    duk_uint32_t uv;

    dv = 3735928559.0;  /* 0xdeadbeef in decimal */
    uv = (duk_uint32_t) dv;

    if (uv != 0xdeadbeefUL) {
        DUK__FAILED("double to duk_uint32_t cast failed");
    }

    return error_count;
}

/*
 *  Minimal test of user supplied allocation functions
 *
 *    - Basic alloc + realloc + free cycle
 *
 *    - Realloc to significantly larger size to (hopefully) trigger a
 *      relocation and check that relocation copying works
 */

DUK_LOCAL duk_uint_t duk__selftest_alloc_funcs(duk_alloc_function alloc_func,
                                               duk_realloc_function realloc_func,
                                               duk_free_function free_func,
                                               void *udata) {
    duk_uint_t error_count = 0;
    void *ptr;
    void *new_ptr;
    duk_small_int_t i, j;
    unsigned char x;

    if (alloc_func == NULL || realloc_func == NULL || free_func == NULL) {
        return 0;
    }

    for (i = 1; i <= 256; i++) {
        ptr = alloc_func(udata, (duk_size_t) i);
        if (ptr == NULL) {
            DUK_D(DUK_DPRINT("alloc failed, ignore"));
            continue;  /* alloc failed, ignore */
        }
        for (j = 0; j < i; j++) {
            ((unsigned char *) ptr)[j] = (unsigned char) (0x80 + j);
        }
        new_ptr = realloc_func(udata, ptr, 1024);
        if (new_ptr == NULL) {
            DUK_D(DUK_DPRINT("realloc failed, ignore"));
            free_func(udata, ptr);
            continue;  /* realloc failed, ignore */
        }
        ptr = new_ptr;
        for (j = 0; j < i; j++) {
            x = ((unsigned char *) ptr)[j];
            if (x != (unsigned char) (0x80 + j)) {
                DUK_D(DUK_DPRINT("byte at index %ld doesn't match after realloc: %02lx",
                                 (long) j, (unsigned long) x));
                DUK__FAILED("byte compare after realloc");
                break;
            }
        }
        free_func(udata, ptr);
    }

    return error_count;
}

/*
 *  Self test main
 */

DUK_INTERNAL duk_uint_t duk_selftest_run_tests(duk_alloc_function alloc_func,
                                               duk_realloc_function realloc_func,
                                               duk_free_function free_func,
                                               void *udata) {
    duk_uint_t error_count = 0;

    DUK_D(DUK_DPRINT("self test starting"));

    error_count += duk__selftest_types();
    error_count += duk__selftest_packed_tval();
    error_count += duk__selftest_twos_complement();
    error_count += duk__selftest_byte_order();
    error_count += duk__selftest_bswap_macros();
    error_count += duk__selftest_double_union_size();
    error_count += duk__selftest_double_aliasing();
    error_count += duk__selftest_double_zero_sign();
    error_count += duk__selftest_double_rounding();
    error_count += duk__selftest_fmod();
    error_count += duk__selftest_struct_align();
    error_count += duk__selftest_64bit_arithmetic();
    error_count += duk__selftest_cast_double_to_small_uint();
    error_count += duk__selftest_cast_double_to_uint32();
    error_count += duk__selftest_alloc_funcs(alloc_func, realloc_func, free_func, udata);

    DUK_D(DUK_DPRINT("self test complete, total error count: %ld", (long) error_count));

    return error_count;
}

#endif  /* DUK_USE_SELF_TESTS */

/* automatic undefs */
#undef DUK__DBLUNION_CMP_FALSE
#undef DUK__DBLUNION_CMP_TRUE
#undef DUK__DOUBLE_COMPARE
#undef DUK__DOUBLE_INIT
#undef DUK__FAILED
#undef DUK__U32_INIT
/* #include duk_internal.h -> already included */
//#line 2 "duk_tval.c"

#if defined(DUK_USE_FASTINT)

/*
 *  Manually optimized double-to-fastint downgrade check.
 *
 *  This check has a large impact on performance, especially for fastint
 *  slow paths, so must be changed carefully.  The code should probably be
 *  optimized for the case where the result does not fit into a fastint,
 *  to minimize the penalty for "slow path code" dealing with fractions etc.
 *
 *  At least on one tested soft float ARM platform double-to-int64 coercion
 *  is very slow (and sometimes produces incorrect results, see self tests).
 *  This algorithm combines a fastint compatibility check and extracting the
 *  integer value from an IEEE double for setting the tagged fastint.  For
 *  other platforms a more naive approach might be better.
 *
 *  See doc/fastint.rst for details.
 */

DUK_INTERNAL DUK_ALWAYS_INLINE void duk_tval_set_number_chkfast_fast(duk_tval *tv, duk_double_t x) {
    duk_double_union du;
    duk_int64_t i;
    duk_small_int_t expt;
    duk_small_int_t shift;

    /* XXX: optimize for packed duk_tval directly? */

    du.d = x;
    i = (duk_int64_t) DUK_DBLUNION_GET_INT64(&du);
    expt = (duk_small_int_t) ((i >> 52) & 0x07ff);
    shift = expt - 1023;

    if (shift >= 0 && shift <= 46) {  /* exponents 1023 to 1069 */
        duk_int64_t t;

        if (((DUK_I64_CONSTANT(0x000fffffffffffff) >> shift) & i) == 0) {
            t = i | DUK_I64_CONSTANT(0x0010000000000000);  /* implicit leading one */
            t = t & DUK_I64_CONSTANT(0x001fffffffffffff);
            t = t >> (52 - shift);
            if (i < 0) {
                t = -t;
            }
            DUK_TVAL_SET_FASTINT(tv, t);
            return;
        }
    } else if (shift == -1023) {  /* exponent 0 */
        if (i >= 0 && (i & DUK_I64_CONSTANT(0x000fffffffffffff)) == 0) {
            /* Note: reject negative zero. */
            DUK_TVAL_SET_FASTINT(tv, (duk_int64_t) 0);
            return;
        }
    } else if (shift == 47) {  /* exponent 1070 */
        if (i < 0 && (i & DUK_I64_CONSTANT(0x000fffffffffffff)) == 0) {
            DUK_TVAL_SET_FASTINT(tv, (duk_int64_t) DUK_FASTINT_MIN);
            return;
        }
    }

    DUK_TVAL_SET_DOUBLE(tv, x);
    return;
}

DUK_INTERNAL DUK_NOINLINE void duk_tval_set_number_chkfast_slow(duk_tval *tv, duk_double_t x) {
    duk_tval_set_number_chkfast_fast(tv, x);
}

/*
 *  Manually optimized number-to-double conversion
 */

#if defined(DUK_USE_FASTINT) && defined(DUK_USE_PACKED_TVAL)
DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t duk_tval_get_number_packed(duk_tval *tv) {
    duk_double_union du;
    duk_uint64_t t;

    t = (duk_uint64_t) DUK_DBLUNION_GET_UINT64(tv);
    if ((t >> 48) != DUK_TAG_FASTINT) {
        return tv->d;
    } else if (t & DUK_U64_CONSTANT(0x0000800000000000)) {
        t = (duk_uint64_t) (-((duk_int64_t) t));  /* avoid unary minus on unsigned */
        t = t & DUK_U64_CONSTANT(0x0000ffffffffffff);  /* negative */
        t |= DUK_U64_CONSTANT(0xc330000000000000);
        DUK_DBLUNION_SET_UINT64(&du, t);
        return du.d + 4503599627370496.0;  /* 1 << 52 */
    } else if (t != 0) {
        t &= DUK_U64_CONSTANT(0x0000ffffffffffff);  /* positive */
        t |= DUK_U64_CONSTANT(0x4330000000000000);
        DUK_DBLUNION_SET_UINT64(&du, t);
        return du.d - 4503599627370496.0;  /* 1 << 52 */
    } else {
        return 0.0;  /* zero */
    }
}
#endif  /* DUK_USE_FASTINT && DUK_USE_PACKED_TVAL */

#if 0  /* unused */
#if defined(DUK_USE_FASTINT) && !defined(DUK_USE_PACKED_TVAL)
DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t duk_tval_get_number_unpacked(duk_tval *tv) {
    duk_double_union du;
    duk_uint64_t t;

    DUK_ASSERT(tv->t == DUK_TAG_NUMBER || tv->t == DUK_TAG_FASTINT);

    if (tv->t == DUK_TAG_FASTINT) {
        if (tv->v.fi >= 0) {
            t = DUK_U64_CONSTANT(0x4330000000000000) | (duk_uint64_t) tv->v.fi;
            DUK_DBLUNION_SET_UINT64(&du, t);
            return du.d - 4503599627370496.0;  /* 1 << 52 */
        } else {
            t = DUK_U64_CONSTANT(0xc330000000000000) | (duk_uint64_t) (-tv->v.fi);
            DUK_DBLUNION_SET_UINT64(&du, t);
            return du.d + 4503599627370496.0;  /* 1 << 52 */
        }
    } else {
        return tv->v.d;
    }
}
#endif  /* DUK_USE_FASTINT && DUK_USE_PACKED_TVAL */
#endif  /* 0 */

#if defined(DUK_USE_FASTINT) && !defined(DUK_USE_PACKED_TVAL)
DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t duk_tval_get_number_unpacked_fastint(duk_tval *tv) {
    duk_double_union du;
    duk_uint64_t t;

    DUK_ASSERT(tv->t == DUK_TAG_FASTINT);

    if (tv->v.fi >= 0) {
        t = DUK_U64_CONSTANT(0x4330000000000000) | (duk_uint64_t) tv->v.fi;
        DUK_DBLUNION_SET_UINT64(&du, t);
        return du.d - 4503599627370496.0;  /* 1 << 52 */
    } else {
        t = DUK_U64_CONSTANT(0xc330000000000000) | (duk_uint64_t) (-tv->v.fi);
        DUK_DBLUNION_SET_UINT64(&du, t);
        return du.d + 4503599627370496.0;  /* 1 << 52 */
    }
}
#endif  /* DUK_USE_FASTINT && DUK_USE_PACKED_TVAL */

#endif  /* DUK_USE_FASTINT */

/*
 *  Assertion helpers.
 */

#if defined(DUK_USE_ASSERTIONS)
DUK_INTERNAL void duk_tval_assert_valid(duk_tval *tv) {
    DUK_ASSERT(tv != NULL);
}
#endif
//#line 1 "duk_unicode_tables.c"
/*
 *  Unicode support tables automatically generated during build.
 */

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

/*
 *  Unicode tables containing ranges of Unicode characters in a
 *  packed format.  These tables are used to match non-ASCII
 *  characters of complex productions by resorting to a linear
 *  range-by-range comparison.  This is very slow, but is expected
 *  to be very rare in practical ECMAScript source code, and thus
 *  compactness is most important.
 *
 *  The tables are matched using uni_range_match() and the format
 *  is described in tools/extract_chars.py.
 */

#if defined(DUK_USE_SOURCE_NONBMP)
/* IdentifierStart production with ASCII excluded */
/* duk_unicode_ids_noa[] */
/*
 *  Automatically generated by extract_chars.py, do not edit!
 */

const duk_uint8_t duk_unicode_ids_noa[1116] = {
249,176,176,80,111,7,47,15,47,254,11,197,191,0,72,2,15,115,66,19,50,7,2,34,
2,240,66,244,50,247,185,249,98,241,99,7,241,159,57,240,181,63,31,241,191,
21,18,245,50,15,1,24,27,35,15,2,2,240,239,15,244,156,15,10,241,26,21,6,240,
101,10,4,15,9,240,152,175,39,240,82,127,56,242,100,15,4,8,159,1,240,5,115,
19,240,98,98,4,52,15,2,14,18,47,0,27,9,85,19,240,98,98,18,18,31,17,50,15,5,
47,2,130,34,240,98,98,18,68,15,4,15,1,31,9,12,115,19,240,98,98,18,68,15,16,
18,47,1,15,3,2,84,34,52,18,2,20,20,36,191,8,15,38,114,34,240,114,240,4,15,
12,38,31,16,5,114,34,240,114,146,68,15,18,2,31,1,31,4,114,34,241,147,15,2,
6,41,47,10,86,240,36,240,130,130,3,111,44,242,2,29,111,44,18,2,66,240,130,
2,146,26,3,66,15,7,63,18,15,49,114,241,79,13,79,101,241,191,6,15,2,85,52,4,
24,37,205,15,3,241,98,6,3,241,178,255,224,63,35,54,32,35,63,25,35,63,17,35,
54,32,35,62,47,41,35,63,51,241,127,0,240,47,70,53,79,254,21,227,240,18,240,
166,243,180,168,194,63,0,240,47,0,240,47,0,194,47,1,242,79,21,5,15,53,244,
152,67,241,34,6,243,107,240,255,35,240,227,76,241,197,240,175,40,240,122,
242,95,68,15,79,241,255,3,111,41,240,238,27,241,207,12,241,79,27,43,241,67,
136,241,179,47,27,50,82,20,6,251,15,50,255,224,8,53,63,22,53,55,32,32,32,
47,15,63,37,38,32,66,38,67,53,92,98,38,246,96,224,240,44,245,112,80,57,32,
68,112,32,32,35,42,51,100,80,240,63,25,255,233,107,241,242,241,242,247,87,
52,29,241,98,6,3,242,136,15,2,240,122,98,98,98,98,98,98,98,111,66,15,254,
12,146,240,184,132,52,95,70,114,47,74,35,111,27,47,78,240,63,11,242,127,0,
255,224,244,255,240,0,138,143,60,255,240,4,14,47,2,255,227,127,243,95,30,
63,253,79,0,177,240,111,31,240,47,15,63,64,241,152,63,87,63,37,52,242,42,
34,35,47,7,240,255,36,240,15,34,243,5,64,33,207,12,191,7,240,191,13,143,31,
240,224,240,36,41,180,47,25,240,146,39,240,111,7,64,79,34,32,65,52,48,32,
240,162,58,130,213,53,53,166,38,47,27,43,159,99,240,255,255,0,26,150,223,7,
95,33,255,240,0,255,143,254,6,3,245,175,24,109,70,2,146,194,66,2,18,18,245,
207,19,255,224,93,240,79,48,63,38,241,171,246,100,47,119,241,111,10,127,10,
207,73,69,53,53,50,241,91,47,10,47,3,33,46,61,241,79,107,243,127,37,255,
223,13,79,33,242,31,16,239,14,111,22,191,14,63,20,87,36,241,207,142,240,79,
20,95,20,95,24,159,36,248,239,254,2,154,240,107,127,138,83,2,241,194,20,3,
240,123,240,122,240,255,51,240,50,27,240,107,240,175,56,242,135,31,50,15,1,
50,34,240,223,28,240,212,240,223,21,114,240,207,13,242,107,240,107,240,62,
240,47,96,243,159,41,242,62,242,62,241,79,254,13,15,13,176,159,6,248,207,7,
223,37,243,223,29,241,47,9,240,207,20,240,240,207,19,64,223,32,240,3,240,
112,32,241,95,2,47,9,244,102,32,35,46,41,143,31,241,135,49,63,6,38,33,36,
64,240,64,212,249,15,37,240,67,240,96,241,47,32,240,97,32,250,175,31,241,
179,241,111,32,240,96,242,223,27,224,243,159,11,253,127,28,246,111,48,241,
16,249,39,63,23,240,32,32,240,224,191,24,128,240,112,207,30,240,80,241,79,
41,255,152,47,21,240,48,242,63,14,246,38,33,47,22,240,112,240,181,33,47,16,
240,0,255,224,59,240,63,254,0,31,254,40,207,88,245,255,3,251,79,254,155,15,
254,50,31,254,236,95,254,19,159,255,0,16,173,255,225,43,143,15,246,63,14,
240,79,32,240,35,241,31,5,111,3,255,225,164,243,15,114,243,182,15,52,207,
50,18,15,14,255,240,0,110,169,255,225,229,255,240,1,64,31,254,1,31,35,47,3,
57,255,224,126,255,231,248,245,182,196,136,159,255,0,6,90,244,82,243,114,
19,3,19,50,178,2,98,243,18,51,114,98,240,194,50,66,4,98,255,224,70,63,9,47,
9,47,15,47,9,47,15,47,9,47,15,47,9,47,15,47,9,39,255,232,40,241,219,111,2,
15,254,6,95,28,255,228,8,251,95,45,243,72,15,254,58,131,47,11,33,32,48,41,
35,32,32,112,80,32,32,34,33,32,48,32,32,32,32,33,32,51,38,35,35,32,41,47,1,
98,36,47,1,255,240,0,3,143,255,0,149,201,241,191,254,242,124,252,227,255,
240,0,87,79,0,255,240,0,194,63,254,177,63,254,17,0,
};
#else
/* IdentifierStart production with ASCII and non-BMP excluded */
/* duk_unicode_ids_noabmp[] */
/*
 *  Automatically generated by extract_chars.py, do not edit!
 */

const duk_uint8_t duk_unicode_ids_noabmp[625] = {
249,176,176,80,111,7,47,15,47,254,11,197,191,0,72,2,15,115,66,19,50,7,2,34,
2,240,66,244,50,247,185,249,98,241,99,7,241,159,57,240,181,63,31,241,191,
21,18,245,50,15,1,24,27,35,15,2,2,240,239,15,244,156,15,10,241,26,21,6,240,
101,10,4,15,9,240,152,175,39,240,82,127,56,242,100,15,4,8,159,1,240,5,115,
19,240,98,98,4,52,15,2,14,18,47,0,27,9,85,19,240,98,98,18,18,31,17,50,15,5,
47,2,130,34,240,98,98,18,68,15,4,15,1,31,9,12,115,19,240,98,98,18,68,15,16,
18,47,1,15,3,2,84,34,52,18,2,20,20,36,191,8,15,38,114,34,240,114,240,4,15,
12,38,31,16,5,114,34,240,114,146,68,15,18,2,31,1,31,4,114,34,241,147,15,2,
6,41,47,10,86,240,36,240,130,130,3,111,44,242,2,29,111,44,18,2,66,240,130,
2,146,26,3,66,15,7,63,18,15,49,114,241,79,13,79,101,241,191,6,15,2,85,52,4,
24,37,205,15,3,241,98,6,3,241,178,255,224,63,35,54,32,35,63,25,35,63,17,35,
54,32,35,62,47,41,35,63,51,241,127,0,240,47,70,53,79,254,21,227,240,18,240,
166,243,180,168,194,63,0,240,47,0,240,47,0,194,47,1,242,79,21,5,15,53,244,
152,67,241,34,6,243,107,240,255,35,240,227,76,241,197,240,175,40,240,122,
242,95,68,15,79,241,255,3,111,41,240,238,27,241,207,12,241,79,27,43,241,67,
136,241,179,47,27,50,82,20,6,251,15,50,255,224,8,53,63,22,53,55,32,32,32,
47,15,63,37,38,32,66,38,67,53,92,98,38,246,96,224,240,44,245,112,80,57,32,
68,112,32,32,35,42,51,100,80,240,63,25,255,233,107,241,242,241,242,247,87,
52,29,241,98,6,3,242,136,15,2,240,122,98,98,98,98,98,98,98,111,66,15,254,
12,146,240,184,132,52,95,70,114,47,74,35,111,27,47,78,240,63,11,242,127,0,
255,224,244,255,240,0,138,143,60,255,240,4,14,47,2,255,227,127,243,95,30,
63,253,79,0,177,240,111,31,240,47,15,63,64,241,152,63,87,63,37,52,242,42,
34,35,47,7,240,255,36,240,15,34,243,5,64,33,207,12,191,7,240,191,13,143,31,
240,224,240,36,41,180,47,25,240,146,39,240,111,7,64,79,34,32,65,52,48,32,
240,162,58,130,213,53,53,166,38,47,27,43,159,99,240,255,255,0,26,150,223,7,
95,33,255,240,0,255,143,254,6,3,245,175,24,109,70,2,146,194,66,2,18,18,245,
207,19,255,224,93,240,79,48,63,38,241,171,246,100,47,119,241,111,10,127,10,
207,73,69,53,53,50,0,
};
#endif

#if defined(DUK_USE_SOURCE_NONBMP)
/* IdentifierStart production with Letter and ASCII excluded */
/* duk_unicode_ids_m_let_noa[] */
/*
 *  Automatically generated by extract_chars.py, do not edit!
 */

const duk_uint8_t duk_unicode_ids_m_let_noa[42] = {
255,240,0,94,18,255,233,99,241,51,63,254,215,32,240,184,240,2,255,240,6,89,
249,255,240,4,148,79,37,255,224,192,9,15,120,79,255,0,15,30,245,240,
};
#else
/* IdentifierStart production with Letter, ASCII, and non-BMP excluded */
/* duk_unicode_ids_m_let_noabmp[] */
/*
 *  Automatically generated by extract_chars.py, do not edit!
 */

const duk_uint8_t duk_unicode_ids_m_let_noabmp[24] = {
255,240,0,94,18,255,233,99,241,51,63,254,215,32,240,184,240,2,255,240,6,89,
249,0,
};
#endif

#if defined(DUK_USE_SOURCE_NONBMP)
/* IdentifierPart production with IdentifierStart and ASCII excluded */
/* duk_unicode_idp_m_ids_noa[] */
/*
 *  Automatically generated by extract_chars.py, do not edit!
 */

const duk_uint8_t duk_unicode_idp_m_ids_noa[576] = {
255,225,243,246,15,254,0,116,255,191,29,32,33,33,32,243,170,242,47,15,112,
245,118,53,49,35,57,240,144,241,15,11,244,218,240,25,241,56,160,240,163,40,
34,36,241,210,246,158,47,17,242,130,47,2,38,177,57,240,50,242,160,38,49,50,
160,177,57,240,0,50,242,160,36,81,50,64,240,107,64,194,242,160,39,34,34,
240,97,57,181,34,242,160,38,49,50,145,177,57,240,64,242,212,66,35,160,240,
9,240,36,242,182,34,35,129,193,57,240,50,242,160,38,34,35,129,193,57,240,
35,242,145,38,34,35,160,177,57,240,65,243,128,85,32,39,121,49,242,240,54,
215,41,244,144,56,197,57,243,1,121,192,32,32,81,242,63,4,33,106,47,20,160,
245,111,4,41,211,82,34,54,67,235,46,255,225,179,47,254,42,98,240,242,240,
241,241,1,243,47,16,160,57,241,50,57,245,209,241,64,246,139,91,185,247,41,
242,244,242,185,47,13,58,121,240,141,243,68,242,31,1,201,240,56,210,241,12,
57,241,237,242,47,4,153,121,246,130,47,5,80,112,50,251,143,42,36,255,225,0,
31,35,31,5,15,109,197,4,191,254,175,34,247,240,245,47,16,255,225,30,95,91,
31,255,0,100,121,159,55,5,159,18,31,66,31,254,0,64,64,80,240,148,244,161,
242,79,2,185,127,2,234,240,231,240,188,241,227,242,29,240,25,192,185,242,
29,208,145,57,241,50,242,64,34,49,97,32,241,180,97,253,231,33,57,255,240,3,
225,128,255,225,213,240,15,2,240,4,31,10,47,178,159,23,15,254,27,16,253,64,
248,116,255,224,25,159,254,68,178,33,99,241,162,80,249,113,255,225,49,57,
159,254,16,10,250,18,242,126,241,25,240,19,241,250,242,121,114,241,109,41,
97,241,224,210,242,45,147,73,244,75,112,249,43,105,115,242,145,38,49,50,
160,177,54,68,251,47,2,169,80,244,63,4,217,252,118,56,240,209,244,79,1,240,
25,244,60,153,244,94,89,254,78,249,121,253,150,54,64,240,233,241,166,35,
144,170,242,15,0,255,224,137,114,127,2,159,42,240,98,223,108,84,2,18,98,9,
159,34,66,18,73,159,254,3,211,255,240,3,165,217,247,132,242,214,240,185,
255,226,233,2,242,120,63,255,0,59,254,31,255,0,3,186,68,89,115,111,16,63,
134,47,254,71,223,34,255,224,244,242,117,242,41,15,0,15,8,66,239,254,68,70,
47,1,54,33,36,255,118,169,255,224,150,223,254,76,166,245,246,105,255,240,
192,105,175,224,0,
};
#else
/* IdentifierPart production with IdentifierStart, ASCII, and non-BMP excluded */
/* duk_unicode_idp_m_ids_noabmp[] */
/*
 *  Automatically generated by extract_chars.py, do not edit!
 */

const duk_uint8_t duk_unicode_idp_m_ids_noabmp[358] = {
255,225,243,246,15,254,0,116,255,191,29,32,33,33,32,243,170,242,47,15,112,
245,118,53,49,35,57,240,144,241,15,11,244,218,240,25,241,56,160,240,163,40,
34,36,241,210,246,158,47,17,242,130,47,2,38,177,57,240,50,242,160,38,49,50,
160,177,57,240,0,50,242,160,36,81,50,64,240,107,64,194,242,160,39,34,34,
240,97,57,181,34,242,160,38,49,50,145,177,57,240,64,242,212,66,35,160,240,
9,240,36,242,182,34,35,129,193,57,240,50,242,160,38,34,35,129,193,57,240,
35,242,145,38,34,35,160,177,57,240,65,243,128,85,32,39,121,49,242,240,54,
215,41,244,144,56,197,57,243,1,121,192,32,32,81,242,63,4,33,106,47,20,160,
245,111,4,41,211,82,34,54,67,235,46,255,225,179,47,254,42,98,240,242,240,
241,241,1,243,47,16,160,57,241,50,57,245,209,241,64,246,139,91,185,247,41,
242,244,242,185,47,13,58,121,240,141,243,68,242,31,1,201,240,56,210,241,12,
57,241,237,242,47,4,153,121,246,130,47,5,80,112,50,251,143,42,36,255,225,0,
31,35,31,5,15,109,197,4,191,254,175,34,247,240,245,47,16,255,225,30,95,91,
31,255,0,100,121,159,55,5,159,18,31,66,31,254,0,64,64,80,240,148,244,161,
242,79,2,185,127,2,234,240,231,240,188,241,227,242,29,240,25,192,185,242,
29,208,145,57,241,50,242,64,34,49,97,32,241,180,97,253,231,33,57,255,240,3,
225,128,255,225,213,240,15,2,240,4,31,10,47,178,159,23,0,
};
#endif

/*
 *  Case conversion tables generated using tools/extract_caseconv.py.
 */

/* duk_unicode_caseconv_uc[] */
/* duk_unicode_caseconv_lc[] */

/*
 *  Automatically generated by extract_caseconv.py, do not edit!
 */

const duk_uint8_t duk_unicode_caseconv_uc[1411] = {
152,3,128,3,0,184,7,192,6,192,112,35,242,199,224,64,74,192,49,32,128,162,
128,108,65,1,189,129,254,131,3,173,3,136,6,7,98,7,34,68,15,12,14,140,72,30,
104,28,112,32,67,0,65,4,0,138,0,128,4,1,88,65,76,83,8,104,14,72,43,16,253,
28,189,6,39,240,39,224,24,114,12,16,132,16,248,0,248,64,129,241,1,241,128,
195,228,3,229,2,7,204,7,206,4,15,160,15,164,6,31,96,31,104,16,62,224,63,
116,8,125,200,127,32,32,251,176,254,208,33,247,129,255,128,67,239,67,253,
64,135,223,7,254,129,15,216,15,220,2,31,208,31,216,4,63,192,63,208,8,133,
192,133,128,129,38,129,37,177,162,195,2,192,5,229,160,2,20,9,170,220,4,232,
40,127,160,255,144,154,136,4,4,4,0,192,9,152,9,144,48,19,160,19,145,0,41,
96,41,69,192,94,128,94,65,128,193,128,193,2,1,161,1,160,6,3,104,3,102,8,7,
56,7,52,64,14,248,14,240,144,31,144,31,130,128,68,96,68,66,64,145,192,145,
130,129,184,129,184,2,3,217,3,216,24,8,194,8,192,68,18,44,18,40,216,38,16,
38,8,112,77,16,77,6,3,192,35,192,18,199,168,71,168,24,15,168,143,172,132,
44,104,44,103,6,89,2,89,0,200,179,176,179,172,21,50,13,50,1,122,104,26,104,
1,212,228,116,228,65,233,204,233,204,143,211,189,83,188,130,167,127,167,
126,11,79,35,79,32,10,158,94,158,88,85,61,173,61,160,97,192,107,64,107,1,0,
226,128,226,3,1,198,1,196,6,3,228,3,226,8,10,0,6,152,16,31,192,31,184,34,
199,50,199,32,65,128,196,0,195,130,1,185,1,184,4,4,205,79,84,8,0,192,143,0,
142,193,1,52,128,203,2,45,39,16,199,5,253,0,11,80,57,192,15,240,23,128,19,
16,4,144,23,240,5,48,24,0,36,48,25,32,25,16,25,80,31,96,25,144,25,128,25,
160,35,208,25,224,34,0,26,128,26,112,27,240,31,112,29,208,24,224,31,48,31,
16,37,2,198,240,37,18,198,208,37,34,199,0,37,48,24,16,37,64,24,96,37,144,
24,240,37,176,25,0,37,202,122,176,38,0,25,48,38,26,122,192,38,48,25,64,38,
90,120,208,38,128,25,112,38,178,198,32,38,202,122,208,39,18,198,224,39,32,
25,208,39,80,25,240,39,210,198,64,40,42,124,80,40,122,123,16,40,128,26,224,
40,144,36,64,40,192,36,80,41,32,27,112,41,218,123,32,41,234,123,0,52,80,57,
144,55,112,55,96,58,192,56,96,60,32,58,48,60,192,56,192,61,0,57,32,61,16,
57,128,61,80,58,96,61,96,58,0,61,112,60,240,63,0,57,160,63,16,58,16,63,32,
63,144,63,48,55,240,63,80,57,80,76,240,76,1,200,0,65,33,200,16,65,65,200,
32,65,225,200,80,66,33,200,96,66,161,200,112,70,33,200,138,100,161,215,154,
119,209,215,210,198,49,216,234,124,97,233,177,230,1,251,224,57,145,254,81,
254,194,20,226,19,34,24,66,24,50,198,18,198,2,198,80,35,162,198,96,35,226,
207,50,207,42,120,202,120,186,121,74,124,74,124,58,124,42,181,58,123,60,
192,27,240,2,152,2,152,10,76,5,120,0,156,3,225,0,37,1,134,1,200,96,115,32,
97,0,96,32,118,24,29,40,24,64,24,8,44,60,10,106,10,164,61,45,0,36,1,152,
143,75,192,10,128,97,3,211,16,2,184,24,80,244,204,0,178,6,20,61,53,0,32,
129,95,15,168,64,116,160,98,99,234,88,29,40,24,152,24,0,250,166,7,74,6,38,
6,2,62,173,129,210,129,137,129,161,15,192,67,225,0,115,35,240,48,248,72,28,
200,252,20,62,20,7,50,63,7,15,133,129,204,143,194,67,225,128,115,35,240,
176,248,104,28,200,252,52,62,28,7,50,63,15,15,135,129,204,143,196,67,225,0,
115,35,241,48,248,72,28,200,252,84,62,20,7,50,63,23,15,133,129,204,143,198,
67,225,128,115,35,241,176,248,104,28,200,252,116,62,28,7,50,63,31,15,135,
129,204,143,200,67,229,0,115,35,242,48,249,72,28,200,252,148,62,84,7,50,63,
39,15,149,129,204,143,202,67,229,128,115,35,242,176,249,104,28,200,252,180,
62,92,7,50,63,47,15,151,129,204,143,204,67,229,0,115,35,243,48,249,72,28,
200,252,212,62,84,7,50,63,55,15,149,129,204,143,206,67,229,128,115,35,243,
176,249,104,28,200,252,244,62,92,7,50,63,63,15,151,129,204,143,208,67,237,
0,115,35,244,48,251,72,28,200,253,20,62,212,7,50,63,71,15,181,129,204,143,
210,67,237,128,115,35,244,176,251,104,28,200,253,52,62,220,7,50,63,79,15,
183,129,204,143,212,67,237,0,115,35,245,48,251,72,28,200,253,84,62,212,7,
50,63,87,15,181,129,204,143,214,67,237,128,115,35,245,176,251,104,28,200,
253,116,62,220,7,50,63,95,15,183,129,204,143,217,67,247,64,115,35,246,112,
28,136,28,200,253,164,7,12,7,50,63,109,1,200,129,161,15,219,224,114,32,104,
64,115,35,247,144,28,136,28,200,254,20,63,148,7,50,63,135,1,203,129,204,
143,226,64,113,32,115,35,248,208,28,184,26,16,254,62,7,46,6,132,7,50,63,
153,1,203,129,204,143,233,96,115,32,97,0,96,3,250,120,28,200,24,64,24,8,
254,180,7,50,6,132,63,175,129,204,129,132,1,161,15,241,96,116,160,97,0,96,
3,252,120,29,40,24,64,24,8,255,36,7,66,6,38,63,205,1,210,129,161,15,243,
224,116,160,97,0,104,67,254,80,255,208,28,200,255,156,7,82,7,50,63,233,1,
199,129,204,143,251,64,117,32,104,67,254,248,29,72,26,16,28,200,255,228,7,
82,7,51,246,1,0,35,0,35,125,128,192,8,192,9,63,96,80,2,48,2,103,216,30,0,
140,0,140,0,147,246,9,128,35,0,35,0,38,125,130,192,10,96,10,159,96,208,2,
152,2,167,216,156,10,136,10,141,246,41,2,162,2,154,253,138,192,168,128,167,
127,98,208,42,112,42,55,216,188,10,136,10,122,
};
const duk_uint8_t duk_unicode_caseconv_lc[706] = {
160,3,0,3,128,184,6,192,7,192,112,24,144,37,96,64,54,32,81,64,128,226,0,
235,65,129,199,1,230,130,3,145,3,177,34,7,70,7,134,36,15,244,13,236,24,32,
0,34,129,0,65,0,67,4,0,166,32,172,41,132,40,11,64,19,9,208,85,184,80,19,
240,19,248,12,57,32,33,160,172,114,244,67,244,24,248,64,248,0,129,241,129,
241,0,195,229,3,228,2,7,206,7,204,4,15,164,15,160,6,31,104,31,96,16,63,16,
63,0,32,126,96,126,64,64,253,64,253,0,129,251,129,251,0,67,247,67,238,0,
135,242,7,220,130,15,236,15,232,2,31,218,31,118,4,63,208,63,192,8,127,168,
125,232,16,255,192,251,192,33,255,161,247,192,68,44,4,46,4,9,45,137,52,13,
22,0,22,24,47,44,126,2,63,5,254,67,254,130,106,48,16,0,16,19,0,38,64,38,96,
192,78,64,78,132,0,165,0,165,151,1,121,1,122,6,3,4,3,6,8,6,128,6,132,24,13,
152,13,160,32,28,176,28,193,32,59,192,59,226,64,124,128,124,193,0,252,0,
252,148,2,34,2,35,18,4,140,4,142,20,13,192,13,196,16,30,192,30,200,192,70,
0,70,18,32,145,64,145,102,193,48,65,48,131,130,104,2,104,176,30,0,30,1,150,
61,64,61,66,192,125,100,125,68,33,99,57,99,64,50,200,2,200,22,69,157,101,
157,128,169,144,41,144,75,211,64,83,64,142,167,34,167,35,15,78,101,78,102,
126,157,230,157,232,21,59,245,59,248,90,121,10,121,16,84,242,212,242,226,
169,237,41,237,67,12,3,76,5,0,8,6,176,6,180,16,14,32,14,48,48,28,80,28,96,
64,126,224,127,0,139,28,139,28,193,6,3,14,3,16,8,6,224,6,228,21,61,80,19,
48,32,3,1,150,2,105,4,4,118,4,120,8,67,28,180,156,23,240,192,94,0,63,192,
96,64,148,192,97,128,149,0,99,128,119,64,99,192,150,64,100,0,150,192,100,
64,100,128,100,192,152,0,101,0,152,192,101,192,154,0,102,0,102,64,103,64,
156,128,103,192,157,64,105,192,106,0,107,128,162,0,109,192,164,128,124,64,
124,192,125,128,101,64,125,192,111,192,136,0,103,128,142,139,25,64,143,64,
102,128,143,139,25,128,144,192,96,0,145,0,162,64,145,64,163,0,221,128,221,
192,223,192,252,192,225,128,235,0,227,0,243,0,243,192,245,192,253,0,238,0,
254,64,252,129,48,1,51,199,167,128,55,199,239,7,236,199,243,7,240,199,251,
7,249,71,255,7,252,200,73,128,242,72,74,128,26,200,74,192,57,72,76,136,83,
136,96,200,97,11,24,11,24,75,24,128,154,203,24,199,95,75,25,0,159,75,27,64,
148,75,27,128,156,75,27,192,148,11,28,0,148,139,60,139,60,233,223,71,94,
105,226,233,227,41,227,64,153,105,234,192,151,41,235,0,152,105,235,64,155,
41,236,0,167,169,236,64,161,233,236,128,167,105,236,234,212,233,240,169,
240,233,241,41,229,41,241,64,160,169,241,135,99,128,128,152,64,13,32,96,
224,
};

#if defined(DUK_USE_REGEXP_CANON_WORKAROUND)
/*
 *  Automatically generated by extract_caseconv.py, do not edit!
 */

const duk_uint16_t duk_unicode_re_canon_lookup[65536] = {
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,
28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,
53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,
78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,65,66,67,68,69,70,
71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,123,124,125,
126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,
162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,
180,924,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,
198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,
216,217,218,219,220,221,222,223,192,193,194,195,196,197,198,199,200,201,
202,203,204,205,206,207,208,209,210,211,212,213,214,247,216,217,218,219,
220,221,222,376,256,256,258,258,260,260,262,262,264,264,266,266,268,268,
270,270,272,272,274,274,276,276,278,278,280,280,282,282,284,284,286,286,
288,288,290,290,292,292,294,294,296,296,298,298,300,300,302,302,304,305,
306,306,308,308,310,310,312,313,313,315,315,317,317,319,319,321,321,323,
323,325,325,327,327,329,330,330,332,332,334,334,336,336,338,338,340,340,
342,342,344,344,346,346,348,348,350,350,352,352,354,354,356,356,358,358,
360,360,362,362,364,364,366,366,368,368,370,370,372,372,374,374,376,377,
377,379,379,381,381,383,579,385,386,386,388,388,390,391,391,393,394,395,
395,397,398,399,400,401,401,403,404,502,406,407,408,408,573,411,412,413,
544,415,416,416,418,418,420,420,422,423,423,425,426,427,428,428,430,431,
431,433,434,435,435,437,437,439,440,440,442,443,444,444,446,503,448,449,
450,451,452,452,452,455,455,455,458,458,458,461,461,463,463,465,465,467,
467,469,469,471,471,473,473,475,475,398,478,478,480,480,482,482,484,484,
486,486,488,488,490,490,492,492,494,494,496,497,497,497,500,500,502,503,
504,504,506,506,508,508,510,510,512,512,514,514,516,516,518,518,520,520,
522,522,524,524,526,526,528,528,530,530,532,532,534,534,536,536,538,538,
540,540,542,542,544,545,546,546,548,548,550,550,552,552,554,554,556,556,
558,558,560,560,562,562,564,565,566,567,568,569,570,571,571,573,574,11390,
11391,577,577,579,580,581,582,582,584,584,586,586,588,588,590,590,11375,
11373,11376,385,390,597,393,394,600,399,602,400,42923L,605,606,607,403,
42924L,610,404,612,42893L,42922L,615,407,406,42926L,11362,42925L,621,622,
412,624,11374,413,627,628,415,630,631,632,633,634,635,636,11364,638,639,
422,641,42949L,425,644,645,646,42929L,430,580,433,434,581,653,654,655,656,
657,439,659,660,661,662,663,664,665,666,667,668,42930L,42928L,671,672,673,
674,675,676,677,678,679,680,681,682,683,684,685,686,687,688,689,690,691,
692,693,694,695,696,697,698,699,700,701,702,703,704,705,706,707,708,709,
710,711,712,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727,
728,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,
746,747,748,749,750,751,752,753,754,755,756,757,758,759,760,761,762,763,
764,765,766,767,768,769,770,771,772,773,774,775,776,777,778,779,780,781,
782,783,784,785,786,787,788,789,790,791,792,793,794,795,796,797,798,799,
800,801,802,803,804,805,806,807,808,809,810,811,812,813,814,815,816,817,
818,819,820,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835,
836,921,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,
854,855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,
872,873,874,875,876,877,878,879,880,880,882,882,884,885,886,886,888,889,
890,1021,1022,1023,894,895,896,897,898,899,900,901,902,903,904,905,906,907,
908,909,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,
926,927,928,929,930,931,932,933,934,935,936,937,938,939,902,904,905,906,
944,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,
931,931,932,933,934,935,936,937,938,939,908,910,911,975,914,920,978,979,
980,934,928,975,984,984,986,986,988,988,990,990,992,992,994,994,996,996,
998,998,1000,1000,1002,1002,1004,1004,1006,1006,922,929,1017,895,1012,917,
1014,1015,1015,1017,1018,1018,1020,1021,1022,1023,1024,1025,1026,1027,1028,
1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,
1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,
1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1040,1041,
1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,
1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,
1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,
1039,1120,1120,1122,1122,1124,1124,1126,1126,1128,1128,1130,1130,1132,1132,
1134,1134,1136,1136,1138,1138,1140,1140,1142,1142,1144,1144,1146,1146,1148,
1148,1150,1150,1152,1152,1154,1155,1156,1157,1158,1159,1160,1161,1162,1162,
1164,1164,1166,1166,1168,1168,1170,1170,1172,1172,1174,1174,1176,1176,1178,
1178,1180,1180,1182,1182,1184,1184,1186,1186,1188,1188,1190,1190,1192,1192,
1194,1194,1196,1196,1198,1198,1200,1200,1202,1202,1204,1204,1206,1206,1208,
1208,1210,1210,1212,1212,1214,1214,1216,1217,1217,1219,1219,1221,1221,1223,
1223,1225,1225,1227,1227,1229,1229,1216,1232,1232,1234,1234,1236,1236,1238,
1238,1240,1240,1242,1242,1244,1244,1246,1246,1248,1248,1250,1250,1252,1252,
1254,1254,1256,1256,1258,1258,1260,1260,1262,1262,1264,1264,1266,1266,1268,
1268,1270,1270,1272,1272,1274,1274,1276,1276,1278,1278,1280,1280,1282,1282,
1284,1284,1286,1286,1288,1288,1290,1290,1292,1292,1294,1294,1296,1296,1298,
1298,1300,1300,1302,1302,1304,1304,1306,1306,1308,1308,1310,1310,1312,1312,
1314,1314,1316,1316,1318,1318,1320,1320,1322,1322,1324,1324,1326,1326,1328,
1329,1330,1331,1332,1333,1334,1335,1336,1337,1338,1339,1340,1341,1342,1343,
1344,1345,1346,1347,1348,1349,1350,1351,1352,1353,1354,1355,1356,1357,1358,
1359,1360,1361,1362,1363,1364,1365,1366,1367,1368,1369,1370,1371,1372,1373,
1374,1375,1376,1329,1330,1331,1332,1333,1334,1335,1336,1337,1338,1339,1340,
1341,1342,1343,1344,1345,1346,1347,1348,1349,1350,1351,1352,1353,1354,1355,
1356,1357,1358,1359,1360,1361,1362,1363,1364,1365,1366,1415,1416,1417,1418,
1419,1420,1421,1422,1423,1424,1425,1426,1427,1428,1429,1430,1431,1432,1433,
1434,1435,1436,1437,1438,1439,1440,1441,1442,1443,1444,1445,1446,1447,1448,
1449,1450,1451,1452,1453,1454,1455,1456,1457,1458,1459,1460,1461,1462,1463,
1464,1465,1466,1467,1468,1469,1470,1471,1472,1473,1474,1475,1476,1477,1478,
1479,1480,1481,1482,1483,1484,1485,1486,1487,1488,1489,1490,1491,1492,1493,
1494,1495,1496,1497,1498,1499,1500,1501,1502,1503,1504,1505,1506,1507,1508,
1509,1510,1511,1512,1513,1514,1515,1516,1517,1518,1519,1520,1521,1522,1523,
1524,1525,1526,1527,1528,1529,1530,1531,1532,1533,1534,1535,1536,1537,1538,
1539,1540,1541,1542,1543,1544,1545,1546,1547,1548,1549,1550,1551,1552,1553,
1554,1555,1556,1557,1558,1559,1560,1561,1562,1563,1564,1565,1566,1567,1568,
1569,1570,1571,1572,1573,1574,1575,1576,1577,1578,1579,1580,1581,1582,1583,
1584,1585,1586,1587,1588,1589,1590,1591,1592,1593,1594,1595,1596,1597,1598,
1599,1600,1601,1602,1603,1604,1605,1606,1607,1608,1609,1610,1611,1612,1613,
1614,1615,1616,1617,1618,1619,1620,1621,1622,1623,1624,1625,1626,1627,1628,
1629,1630,1631,1632,1633,1634,1635,1636,1637,1638,1639,1640,1641,1642,1643,
1644,1645,1646,1647,1648,1649,1650,1651,1652,1653,1654,1655,1656,1657,1658,
1659,1660,1661,1662,1663,1664,1665,1666,1667,1668,1669,1670,1671,1672,1673,
1674,1675,1676,1677,1678,1679,1680,1681,1682,1683,1684,1685,1686,1687,1688,
1689,1690,1691,1692,1693,1694,1695,1696,1697,1698,1699,1700,1701,1702,1703,
1704,1705,1706,1707,1708,1709,1710,1711,1712,1713,1714,1715,1716,1717,1718,
1719,1720,1721,1722,1723,1724,1725,1726,1727,1728,1729,1730,1731,1732,1733,
1734,1735,1736,1737,1738,1739,1740,1741,1742,1743,1744,1745,1746,1747,1748,
1749,1750,1751,1752,1753,1754,1755,1756,1757,1758,1759,1760,1761,1762,1763,
1764,1765,1766,1767,1768,1769,1770,1771,1772,1773,1774,1775,1776,1777,1778,
1779,1780,1781,1782,1783,1784,1785,1786,1787,1788,1789,1790,1791,1792,1793,
1794,1795,1796,1797,1798,1799,1800,1801,1802,1803,1804,1805,1806,1807,1808,
1809,1810,1811,1812,1813,1814,1815,1816,1817,1818,1819,1820,1821,1822,1823,
1824,1825,1826,1827,1828,1829,1830,1831,1832,1833,1834,1835,1836,1837,1838,
1839,1840,1841,1842,1843,1844,1845,1846,1847,1848,1849,1850,1851,1852,1853,
1854,1855,1856,1857,1858,1859,1860,1861,1862,1863,1864,1865,1866,1867,1868,
1869,1870,1871,1872,1873,1874,1875,1876,1877,1878,1879,1880,1881,1882,1883,
1884,1885,1886,1887,1888,1889,1890,1891,1892,1893,1894,1895,1896,1897,1898,
1899,1900,1901,1902,1903,1904,1905,1906,1907,1908,1909,1910,1911,1912,1913,
1914,1915,1916,1917,1918,1919,1920,1921,1922,1923,1924,1925,1926,1927,1928,
1929,1930,1931,1932,1933,1934,1935,1936,1937,1938,1939,1940,1941,1942,1943,
1944,1945,1946,1947,1948,1949,1950,1951,1952,1953,1954,1955,1956,1957,1958,
1959,1960,1961,1962,1963,1964,1965,1966,1967,1968,1969,1970,1971,1972,1973,
1974,1975,1976,1977,1978,1979,1980,1981,1982,1983,1984,1985,1986,1987,1988,
1989,1990,1991,1992,1993,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003,
2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,
2019,2020,2021,2022,2023,2024,2025,2026,2027,2028,2029,2030,2031,2032,2033,
2034,2035,2036,2037,2038,2039,2040,2041,2042,2043,2044,2045,2046,2047,2048,
2049,2050,2051,2052,2053,2054,2055,2056,2057,2058,2059,2060,2061,2062,2063,
2064,2065,2066,2067,2068,2069,2070,2071,2072,2073,2074,2075,2076,2077,2078,
2079,2080,2081,2082,2083,2084,2085,2086,2087,2088,2089,2090,2091,2092,2093,
2094,2095,2096,2097,2098,2099,2100,2101,2102,2103,2104,2105,2106,2107,2108,
2109,2110,2111,2112,2113,2114,2115,2116,2117,2118,2119,2120,2121,2122,2123,
2124,2125,2126,2127,2128,2129,2130,2131,2132,2133,2134,2135,2136,2137,2138,
2139,2140,2141,2142,2143,2144,2145,2146,2147,2148,2149,2150,2151,2152,2153,
2154,2155,2156,2157,2158,2159,2160,2161,2162,2163,2164,2165,2166,2167,2168,
2169,2170,2171,2172,2173,2174,2175,2176,2177,2178,2179,2180,2181,2182,2183,
2184,2185,2186,2187,2188,2189,2190,2191,2192,2193,2194,2195,2196,2197,2198,
2199,2200,2201,2202,2203,2204,2205,2206,2207,2208,2209,2210,2211,2212,2213,
2214,2215,2216,2217,2218,2219,2220,2221,2222,2223,2224,2225,2226,2227,2228,
2229,2230,2231,2232,2233,2234,2235,2236,2237,2238,2239,2240,2241,2242,2243,
2244,2245,2246,2247,2248,2249,2250,2251,2252,2253,2254,2255,2256,2257,2258,
2259,2260,2261,2262,2263,2264,2265,2266,2267,2268,2269,2270,2271,2272,2273,
2274,2275,2276,2277,2278,2279,2280,2281,2282,2283,2284,2285,2286,2287,2288,
2289,2290,2291,2292,2293,2294,2295,2296,2297,2298,2299,2300,2301,2302,2303,
2304,2305,2306,2307,2308,2309,2310,2311,2312,2313,2314,2315,2316,2317,2318,
2319,2320,2321,2322,2323,2324,2325,2326,2327,2328,2329,2330,2331,2332,2333,
2334,2335,2336,2337,2338,2339,2340,2341,2342,2343,2344,2345,2346,2347,2348,
2349,2350,2351,2352,2353,2354,2355,2356,2357,2358,2359,2360,2361,2362,2363,
2364,2365,2366,2367,2368,2369,2370,2371,2372,2373,2374,2375,2376,2377,2378,
2379,2380,2381,2382,2383,2384,2385,2386,2387,2388,2389,2390,2391,2392,2393,
2394,2395,2396,2397,2398,2399,2400,2401,2402,2403,2404,2405,2406,2407,2408,
2409,2410,2411,2412,2413,2414,2415,2416,2417,2418,2419,2420,2421,2422,2423,
2424,2425,2426,2427,2428,2429,2430,2431,2432,2433,2434,2435,2436,2437,2438,
2439,2440,2441,2442,2443,2444,2445,2446,2447,2448,2449,2450,2451,2452,2453,
2454,2455,2456,2457,2458,2459,2460,2461,2462,2463,2464,2465,2466,2467,2468,
2469,2470,2471,2472,2473,2474,2475,2476,2477,2478,2479,2480,2481,2482,2483,
2484,2485,2486,2487,2488,2489,2490,2491,2492,2493,2494,2495,2496,2497,2498,
2499,2500,2501,2502,2503,2504,2505,2506,2507,2508,2509,2510,2511,2512,2513,
2514,2515,2516,2517,2518,2519,2520,2521,2522,2523,2524,2525,2526,2527,2528,
2529,2530,2531,2532,2533,2534,2535,2536,2537,2538,2539,2540,2541,2542,2543,
2544,2545,2546,2547,2548,2549,2550,2551,2552,2553,2554,2555,2556,2557,2558,
2559,2560,2561,2562,2563,2564,2565,2566,2567,2568,2569,2570,2571,2572,2573,
2574,2575,2576,2577,2578,2579,2580,2581,2582,2583,2584,2585,2586,2587,2588,
2589,2590,2591,2592,2593,2594,2595,2596,2597,2598,2599,2600,2601,2602,2603,
2604,2605,2606,2607,2608,2609,2610,2611,2612,2613,2614,2615,2616,2617,2618,
2619,2620,2621,2622,2623,2624,2625,2626,2627,2628,2629,2630,2631,2632,2633,
2634,2635,2636,2637,2638,2639,2640,2641,2642,2643,2644,2645,2646,2647,2648,
2649,2650,2651,2652,2653,2654,2655,2656,2657,2658,2659,2660,2661,2662,2663,
2664,2665,2666,2667,2668,2669,2670,2671,2672,2673,2674,2675,2676,2677,2678,
2679,2680,2681,2682,2683,2684,2685,2686,2687,2688,2689,2690,2691,2692,2693,
2694,2695,2696,2697,2698,2699,2700,2701,2702,2703,2704,2705,2706,2707,2708,
2709,2710,2711,2712,2713,2714,2715,2716,2717,2718,2719,2720,2721,2722,2723,
2724,2725,2726,2727,2728,2729,2730,2731,2732,2733,2734,2735,2736,2737,2738,
2739,2740,2741,2742,2743,2744,2745,2746,2747,2748,2749,2750,2751,2752,2753,
2754,2755,2756,2757,2758,2759,2760,2761,2762,2763,2764,2765,2766,2767,2768,
2769,2770,2771,2772,2773,2774,2775,2776,2777,2778,2779,2780,2781,2782,2783,
2784,2785,2786,2787,2788,2789,2790,2791,2792,2793,2794,2795,2796,2797,2798,
2799,2800,2801,2802,2803,2804,2805,2806,2807,2808,2809,2810,2811,2812,2813,
2814,2815,2816,2817,2818,2819,2820,2821,2822,2823,2824,2825,2826,2827,2828,
2829,2830,2831,2832,2833,2834,2835,2836,2837,2838,2839,2840,2841,2842,2843,
2844,2845,2846,2847,2848,2849,2850,2851,2852,2853,2854,2855,2856,2857,2858,
2859,2860,2861,2862,2863,2864,2865,2866,2867,2868,2869,2870,2871,2872,2873,
2874,2875,2876,2877,2878,2879,2880,2881,2882,2883,2884,2885,2886,2887,2888,
2889,2890,2891,2892,2893,2894,2895,2896,2897,2898,2899,2900,2901,2902,2903,
2904,2905,2906,2907,2908,2909,2910,2911,2912,2913,2914,2915,2916,2917,2918,
2919,2920,2921,2922,2923,2924,2925,2926,2927,2928,2929,2930,2931,2932,2933,
2934,2935,2936,2937,2938,2939,2940,2941,2942,2943,2944,2945,2946,2947,2948,
2949,2950,2951,2952,2953,2954,2955,2956,2957,2958,2959,2960,2961,2962,2963,
2964,2965,2966,2967,2968,2969,2970,2971,2972,2973,2974,2975,2976,2977,2978,
2979,2980,2981,2982,2983,2984,2985,2986,2987,2988,2989,2990,2991,2992,2993,
2994,2995,2996,2997,2998,2999,3000,3001,3002,3003,3004,3005,3006,3007,3008,
3009,3010,3011,3012,3013,3014,3015,3016,3017,3018,3019,3020,3021,3022,3023,
3024,3025,3026,3027,3028,3029,3030,3031,3032,3033,3034,3035,3036,3037,3038,
3039,3040,3041,3042,3043,3044,3045,3046,3047,3048,3049,3050,3051,3052,3053,
3054,3055,3056,3057,3058,3059,3060,3061,3062,3063,3064,3065,3066,3067,3068,
3069,3070,3071,3072,3073,3074,3075,3076,3077,3078,3079,3080,3081,3082,3083,
3084,3085,3086,3087,3088,3089,3090,3091,3092,3093,3094,3095,3096,3097,3098,
3099,3100,3101,3102,3103,3104,3105,3106,3107,3108,3109,3110,3111,3112,3113,
3114,3115,3116,3117,3118,3119,3120,3121,3122,3123,3124,3125,3126,3127,3128,
3129,3130,3131,3132,3133,3134,3135,3136,3137,3138,3139,3140,3141,3142,3143,
3144,3145,3146,3147,3148,3149,3150,3151,3152,3153,3154,3155,3156,3157,3158,
3159,3160,3161,3162,3163,3164,3165,3166,3167,3168,3169,3170,3171,3172,3173,
3174,3175,3176,3177,3178,3179,3180,3181,3182,3183,3184,3185,3186,3187,3188,
3189,3190,3191,3192,3193,3194,3195,3196,3197,3198,3199,3200,3201,3202,3203,
3204,3205,3206,3207,3208,3209,3210,3211,3212,3213,3214,3215,3216,3217,3218,
3219,3220,3221,3222,3223,3224,3225,3226,3227,3228,3229,3230,3231,3232,3233,
3234,3235,3236,3237,3238,3239,3240,3241,3242,3243,3244,3245,3246,3247,3248,
3249,3250,3251,3252,3253,3254,3255,3256,3257,3258,3259,3260,3261,3262,3263,
3264,3265,3266,3267,3268,3269,3270,3271,3272,3273,3274,3275,3276,3277,3278,
3279,3280,3281,3282,3283,3284,3285,3286,3287,3288,3289,3290,3291,3292,3293,
3294,3295,3296,3297,3298,3299,3300,3301,3302,3303,3304,3305,3306,3307,3308,
3309,3310,3311,3312,3313,3314,3315,3316,3317,3318,3319,3320,3321,3322,3323,
3324,3325,3326,3327,3328,3329,3330,3331,3332,3333,3334,3335,3336,3337,3338,
3339,3340,3341,3342,3343,3344,3345,3346,3347,3348,3349,3350,3351,3352,3353,
3354,3355,3356,3357,3358,3359,3360,3361,3362,3363,3364,3365,3366,3367,3368,
3369,3370,3371,3372,3373,3374,3375,3376,3377,3378,3379,3380,3381,3382,3383,
3384,3385,3386,3387,3388,3389,3390,3391,3392,3393,3394,3395,3396,3397,3398,
3399,3400,3401,3402,3403,3404,3405,3406,3407,3408,3409,3410,3411,3412,3413,
3414,3415,3416,3417,3418,3419,3420,3421,3422,3423,3424,3425,3426,3427,3428,
3429,3430,3431,3432,3433,3434,3435,3436,3437,3438,3439,3440,3441,3442,3443,
3444,3445,3446,3447,3448,3449,3450,3451,3452,3453,3454,3455,3456,3457,3458,
3459,3460,3461,3462,3463,3464,3465,3466,3467,3468,3469,3470,3471,3472,3473,
3474,3475,3476,3477,3478,3479,3480,3481,3482,3483,3484,3485,3486,3487,3488,
3489,3490,3491,3492,3493,3494,3495,3496,3497,3498,3499,3500,3501,3502,3503,
3504,3505,3506,3507,3508,3509,3510,3511,3512,3513,3514,3515,3516,3517,3518,
3519,3520,3521,3522,3523,3524,3525,3526,3527,3528,3529,3530,3531,3532,3533,
3534,3535,3536,3537,3538,3539,3540,3541,3542,3543,3544,3545,3546,3547,3548,
3549,3550,3551,3552,3553,3554,3555,3556,3557,3558,3559,3560,3561,3562,3563,
3564,3565,3566,3567,3568,3569,3570,3571,3572,3573,3574,3575,3576,3577,3578,
3579,3580,3581,3582,3583,3584,3585,3586,3587,3588,3589,3590,3591,3592,3593,
3594,3595,3596,3597,3598,3599,3600,3601,3602,3603,3604,3605,3606,3607,3608,
3609,3610,3611,3612,3613,3614,3615,3616,3617,3618,3619,3620,3621,3622,3623,
3624,3625,3626,3627,3628,3629,3630,3631,3632,3633,3634,3635,3636,3637,3638,
3639,3640,3641,3642,3643,3644,3645,3646,3647,3648,3649,3650,3651,3652,3653,
3654,3655,3656,3657,3658,3659,3660,3661,3662,3663,3664,3665,3666,3667,3668,
3669,3670,3671,3672,3673,3674,3675,3676,3677,3678,3679,3680,3681,3682,3683,
3684,3685,3686,3687,3688,3689,3690,3691,3692,3693,3694,3695,3696,3697,3698,
3699,3700,3701,3702,3703,3704,3705,3706,3707,3708,3709,3710,3711,3712,3713,
3714,3715,3716,3717,3718,3719,3720,3721,3722,3723,3724,3725,3726,3727,3728,
3729,3730,3731,3732,3733,3734,3735,3736,3737,3738,3739,3740,3741,3742,3743,
3744,3745,3746,3747,3748,3749,3750,3751,3752,3753,3754,3755,3756,3757,3758,
3759,3760,3761,3762,3763,3764,3765,3766,3767,3768,3769,3770,3771,3772,3773,
3774,3775,3776,3777,3778,3779,3780,3781,3782,3783,3784,3785,3786,3787,3788,
3789,3790,3791,3792,3793,3794,3795,3796,3797,3798,3799,3800,3801,3802,3803,
3804,3805,3806,3807,3808,3809,3810,3811,3812,3813,3814,3815,3816,3817,3818,
3819,3820,3821,3822,3823,3824,3825,3826,3827,3828,3829,3830,3831,3832,3833,
3834,3835,3836,3837,3838,3839,3840,3841,3842,3843,3844,3845,3846,3847,3848,
3849,3850,3851,3852,3853,3854,3855,3856,3857,3858,3859,3860,3861,3862,3863,
3864,3865,3866,3867,3868,3869,3870,3871,3872,3873,3874,3875,3876,3877,3878,
3879,3880,3881,3882,3883,3884,3885,3886,3887,3888,3889,3890,3891,3892,3893,
3894,3895,3896,3897,3898,3899,3900,3901,3902,3903,3904,3905,3906,3907,3908,
3909,3910,3911,3912,3913,3914,3915,3916,3917,3918,3919,3920,3921,3922,3923,
3924,3925,3926,3927,3928,3929,3930,3931,3932,3933,3934,3935,3936,3937,3938,
3939,3940,3941,3942,3943,3944,3945,3946,3947,3948,3949,3950,3951,3952,3953,
3954,3955,3956,3957,3958,3959,3960,3961,3962,3963,3964,3965,3966,3967,3968,
3969,3970,3971,3972,3973,3974,3975,3976,3977,3978,3979,3980,3981,3982,3983,
3984,3985,3986,3987,3988,3989,3990,3991,3992,3993,3994,3995,3996,3997,3998,
3999,4000,4001,4002,4003,4004,4005,4006,4007,4008,4009,4010,4011,4012,4013,
4014,4015,4016,4017,4018,4019,4020,4021,4022,4023,4024,4025,4026,4027,4028,
4029,4030,4031,4032,4033,4034,4035,4036,4037,4038,4039,4040,4041,4042,4043,
4044,4045,4046,4047,4048,4049,4050,4051,4052,4053,4054,4055,4056,4057,4058,
4059,4060,4061,4062,4063,4064,4065,4066,4067,4068,4069,4070,4071,4072,4073,
4074,4075,4076,4077,4078,4079,4080,4081,4082,4083,4084,4085,4086,4087,4088,
4089,4090,4091,4092,4093,4094,4095,4096,4097,4098,4099,4100,4101,4102,4103,
4104,4105,4106,4107,4108,4109,4110,4111,4112,4113,4114,4115,4116,4117,4118,
4119,4120,4121,4122,4123,4124,4125,4126,4127,4128,4129,4130,4131,4132,4133,
4134,4135,4136,4137,4138,4139,4140,4141,4142,4143,4144,4145,4146,4147,4148,
4149,4150,4151,4152,4153,4154,4155,4156,4157,4158,4159,4160,4161,4162,4163,
4164,4165,4166,4167,4168,4169,4170,4171,4172,4173,4174,4175,4176,4177,4178,
4179,4180,4181,4182,4183,4184,4185,4186,4187,4188,4189,4190,4191,4192,4193,
4194,4195,4196,4197,4198,4199,4200,4201,4202,4203,4204,4205,4206,4207,4208,
4209,4210,4211,4212,4213,4214,4215,4216,4217,4218,4219,4220,4221,4222,4223,
4224,4225,4226,4227,4228,4229,4230,4231,4232,4233,4234,4235,4236,4237,4238,
4239,4240,4241,4242,4243,4244,4245,4246,4247,4248,4249,4250,4251,4252,4253,
4254,4255,4256,4257,4258,4259,4260,4261,4262,4263,4264,4265,4266,4267,4268,
4269,4270,4271,4272,4273,4274,4275,4276,4277,4278,4279,4280,4281,4282,4283,
4284,4285,4286,4287,4288,4289,4290,4291,4292,4293,4294,4295,4296,4297,4298,
4299,4300,4301,4302,4303,7312,7313,7314,7315,7316,7317,7318,7319,7320,7321,
7322,7323,7324,7325,7326,7327,7328,7329,7330,7331,7332,7333,7334,7335,7336,
7337,7338,7339,7340,7341,7342,7343,7344,7345,7346,7347,7348,7349,7350,7351,
7352,7353,7354,4347,4348,7357,7358,7359,4352,4353,4354,4355,4356,4357,4358,
4359,4360,4361,4362,4363,4364,4365,4366,4367,4368,4369,4370,4371,4372,4373,
4374,4375,4376,4377,4378,4379,4380,4381,4382,4383,4384,4385,4386,4387,4388,
4389,4390,4391,4392,4393,4394,4395,4396,4397,4398,4399,4400,4401,4402,4403,
4404,4405,4406,4407,4408,4409,4410,4411,4412,4413,4414,4415,4416,4417,4418,
4419,4420,4421,4422,4423,4424,4425,4426,4427,4428,4429,4430,4431,4432,4433,
4434,4435,4436,4437,4438,4439,4440,4441,4442,4443,4444,4445,4446,4447,4448,
4449,4450,4451,4452,4453,4454,4455,4456,4457,4458,4459,4460,4461,4462,4463,
4464,4465,4466,4467,4468,4469,4470,4471,4472,4473,4474,4475,4476,4477,4478,
4479,4480,4481,4482,4483,4484,4485,4486,4487,4488,4489,4490,4491,4492,4493,
4494,4495,4496,4497,4498,4499,4500,4501,4502,4503,4504,4505,4506,4507,4508,
4509,4510,4511,4512,4513,4514,4515,4516,4517,4518,4519,4520,4521,4522,4523,
4524,4525,4526,4527,4528,4529,4530,4531,4532,4533,4534,4535,4536,4537,4538,
4539,4540,4541,4542,4543,4544,4545,4546,4547,4548,4549,4550,4551,4552,4553,
4554,4555,4556,4557,4558,4559,4560,4561,4562,4563,4564,4565,4566,4567,4568,
4569,4570,4571,4572,4573,4574,4575,4576,4577,4578,4579,4580,4581,4582,4583,
4584,4585,4586,4587,4588,4589,4590,4591,4592,4593,4594,4595,4596,4597,4598,
4599,4600,4601,4602,4603,4604,4605,4606,4607,4608,4609,4610,4611,4612,4613,
4614,4615,4616,4617,4618,4619,4620,4621,4622,4623,4624,4625,4626,4627,4628,
4629,4630,4631,4632,4633,4634,4635,4636,4637,4638,4639,4640,4641,4642,4643,
4644,4645,4646,4647,4648,4649,4650,4651,4652,4653,4654,4655,4656,4657,4658,
4659,4660,4661,4662,4663,4664,4665,4666,4667,4668,4669,4670,4671,4672,4673,
4674,4675,4676,4677,4678,4679,4680,4681,4682,4683,4684,4685,4686,4687,4688,
4689,4690,4691,4692,4693,4694,4695,4696,4697,4698,4699,4700,4701,4702,4703,
4704,4705,4706,4707,4708,4709,4710,4711,4712,4713,4714,4715,4716,4717,4718,
4719,4720,4721,4722,4723,4724,4725,4726,4727,4728,4729,4730,4731,4732,4733,
4734,4735,4736,4737,4738,4739,4740,4741,4742,4743,4744,4745,4746,4747,4748,
4749,4750,4751,4752,4753,4754,4755,4756,4757,4758,4759,4760,4761,4762,4763,
4764,4765,4766,4767,4768,4769,4770,4771,4772,4773,4774,4775,4776,4777,4778,
4779,4780,4781,4782,4783,4784,4785,4786,4787,4788,4789,4790,4791,4792,4793,
4794,4795,4796,4797,4798,4799,4800,4801,4802,4803,4804,4805,4806,4807,4808,
4809,4810,4811,4812,4813,4814,4815,4816,4817,4818,4819,4820,4821,4822,4823,
4824,4825,4826,4827,4828,4829,4830,4831,4832,4833,4834,4835,4836,4837,4838,
4839,4840,4841,4842,4843,4844,4845,4846,4847,4848,4849,4850,4851,4852,4853,
4854,4855,4856,4857,4858,4859,4860,4861,4862,4863,4864,4865,4866,4867,4868,
4869,4870,4871,4872,4873,4874,4875,4876,4877,4878,4879,4880,4881,4882,4883,
4884,4885,4886,4887,4888,4889,4890,4891,4892,4893,4894,4895,4896,4897,4898,
4899,4900,4901,4902,4903,4904,4905,4906,4907,4908,4909,4910,4911,4912,4913,
4914,4915,4916,4917,4918,4919,4920,4921,4922,4923,4924,4925,4926,4927,4928,
4929,4930,4931,4932,4933,4934,4935,4936,4937,4938,4939,4940,4941,4942,4943,
4944,4945,4946,4947,4948,4949,4950,4951,4952,4953,4954,4955,4956,4957,4958,
4959,4960,4961,4962,4963,4964,4965,4966,4967,4968,4969,4970,4971,4972,4973,
4974,4975,4976,4977,4978,4979,4980,4981,4982,4983,4984,4985,4986,4987,4988,
4989,4990,4991,4992,4993,4994,4995,4996,4997,4998,4999,5000,5001,5002,5003,
5004,5005,5006,5007,5008,5009,5010,5011,5012,5013,5014,5015,5016,5017,5018,
5019,5020,5021,5022,5023,5024,5025,5026,5027,5028,5029,5030,5031,5032,5033,
5034,5035,5036,5037,5038,5039,5040,5041,5042,5043,5044,5045,5046,5047,5048,
5049,5050,5051,5052,5053,5054,5055,5056,5057,5058,5059,5060,5061,5062,5063,
5064,5065,5066,5067,5068,5069,5070,5071,5072,5073,5074,5075,5076,5077,5078,
5079,5080,5081,5082,5083,5084,5085,5086,5087,5088,5089,5090,5091,5092,5093,
5094,5095,5096,5097,5098,5099,5100,5101,5102,5103,5104,5105,5106,5107,5108,
5109,5110,5111,5104,5105,5106,5107,5108,5109,5118,5119,5120,5121,5122,5123,
5124,5125,5126,5127,5128,5129,5130,5131,5132,5133,5134,5135,5136,5137,5138,
5139,5140,5141,5142,5143,5144,5145,5146,5147,5148,5149,5150,5151,5152,5153,
5154,5155,5156,5157,5158,5159,5160,5161,5162,5163,5164,5165,5166,5167,5168,
5169,5170,5171,5172,5173,5174,5175,5176,5177,5178,5179,5180,5181,5182,5183,
5184,5185,5186,5187,5188,5189,5190,5191,5192,5193,5194,5195,5196,5197,5198,
5199,5200,5201,5202,5203,5204,5205,5206,5207,5208,5209,5210,5211,5212,5213,
5214,5215,5216,5217,5218,5219,5220,5221,5222,5223,5224,5225,5226,5227,5228,
5229,5230,5231,5232,5233,5234,5235,5236,5237,5238,5239,5240,5241,5242,5243,
5244,5245,5246,5247,5248,5249,5250,5251,5252,5253,5254,5255,5256,5257,5258,
5259,5260,5261,5262,5263,5264,5265,5266,5267,5268,5269,5270,5271,5272,5273,
5274,5275,5276,5277,5278,5279,5280,5281,5282,5283,5284,5285,5286,5287,5288,
5289,5290,5291,5292,5293,5294,5295,5296,5297,5298,5299,5300,5301,5302,5303,
5304,5305,5306,5307,5308,5309,5310,5311,5312,5313,5314,5315,5316,5317,5318,
5319,5320,5321,5322,5323,5324,5325,5326,5327,5328,5329,5330,5331,5332,5333,
5334,5335,5336,5337,5338,5339,5340,5341,5342,5343,5344,5345,5346,5347,5348,
5349,5350,5351,5352,5353,5354,5355,5356,5357,5358,5359,5360,5361,5362,5363,
5364,5365,5366,5367,5368,5369,5370,5371,5372,5373,5374,5375,5376,5377,5378,
5379,5380,5381,5382,5383,5384,5385,5386,5387,5388,5389,5390,5391,5392,5393,
5394,5395,5396,5397,5398,5399,5400,5401,5402,5403,5404,5405,5406,5407,5408,
5409,5410,5411,5412,5413,5414,5415,5416,5417,5418,5419,5420,5421,5422,5423,
5424,5425,5426,5427,5428,5429,5430,5431,5432,5433,5434,5435,5436,5437,5438,
5439,5440,5441,5442,5443,5444,5445,5446,5447,5448,5449,5450,5451,5452,5453,
5454,5455,5456,5457,5458,5459,5460,5461,5462,5463,5464,5465,5466,5467,5468,
5469,5470,5471,5472,5473,5474,5475,5476,5477,5478,5479,5480,5481,5482,5483,
5484,5485,5486,5487,5488,5489,5490,5491,5492,5493,5494,5495,5496,5497,5498,
5499,5500,5501,5502,5503,5504,5505,5506,5507,5508,5509,5510,5511,5512,5513,
5514,5515,5516,5517,5518,5519,5520,5521,5522,5523,5524,5525,5526,5527,5528,
5529,5530,5531,5532,5533,5534,5535,5536,5537,5538,5539,5540,5541,5542,5543,
5544,5545,5546,5547,5548,5549,5550,5551,5552,5553,5554,5555,5556,5557,5558,
5559,5560,5561,5562,5563,5564,5565,5566,5567,5568,5569,5570,5571,5572,5573,
5574,5575,5576,5577,5578,5579,5580,5581,5582,5583,5584,5585,5586,5587,5588,
5589,5590,5591,5592,5593,5594,5595,5596,5597,5598,5599,5600,5601,5602,5603,
5604,5605,5606,5607,5608,5609,5610,5611,5612,5613,5614,5615,5616,5617,5618,
5619,5620,5621,5622,5623,5624,5625,5626,5627,5628,5629,5630,5631,5632,5633,
5634,5635,5636,5637,5638,5639,5640,5641,5642,5643,5644,5645,5646,5647,5648,
5649,5650,5651,5652,5653,5654,5655,5656,5657,5658,5659,5660,5661,5662,5663,
5664,5665,5666,5667,5668,5669,5670,5671,5672,5673,5674,5675,5676,5677,5678,
5679,5680,5681,5682,5683,5684,5685,5686,5687,5688,5689,5690,5691,5692,5693,
5694,5695,5696,5697,5698,5699,5700,5701,5702,5703,5704,5705,5706,5707,5708,
5709,5710,5711,5712,5713,5714,5715,5716,5717,5718,5719,5720,5721,5722,5723,
5724,5725,5726,5727,5728,5729,5730,5731,5732,5733,5734,5735,5736,5737,5738,
5739,5740,5741,5742,5743,5744,5745,5746,5747,5748,5749,5750,5751,5752,5753,
5754,5755,5756,5757,5758,5759,5760,5761,5762,5763,5764,5765,5766,5767,5768,
5769,5770,5771,5772,5773,5774,5775,5776,5777,5778,5779,5780,5781,5782,5783,
5784,5785,5786,5787,5788,5789,5790,5791,5792,5793,5794,5795,5796,5797,5798,
5799,5800,5801,5802,5803,5804,5805,5806,5807,5808,5809,5810,5811,5812,5813,
5814,5815,5816,5817,5818,5819,5820,5821,5822,5823,5824,5825,5826,5827,5828,
5829,5830,5831,5832,5833,5834,5835,5836,5837,5838,5839,5840,5841,5842,5843,
5844,5845,5846,5847,5848,5849,5850,5851,5852,5853,5854,5855,5856,5857,5858,
5859,5860,5861,5862,5863,5864,5865,5866,5867,5868,5869,5870,5871,5872,5873,
5874,5875,5876,5877,5878,5879,5880,5881,5882,5883,5884,5885,5886,5887,5888,
5889,5890,5891,5892,5893,5894,5895,5896,5897,5898,5899,5900,5901,5902,5903,
5904,5905,5906,5907,5908,5909,5910,5911,5912,5913,5914,5915,5916,5917,5918,
5919,5920,5921,5922,5923,5924,5925,5926,5927,5928,5929,5930,5931,5932,5933,
5934,5935,5936,5937,5938,5939,5940,5941,5942,5943,5944,5945,5946,5947,5948,
5949,5950,5951,5952,5953,5954,5955,5956,5957,5958,5959,5960,5961,5962,5963,
5964,5965,5966,5967,5968,5969,5970,5971,5972,5973,5974,5975,5976,5977,5978,
5979,5980,5981,5982,5983,5984,5985,5986,5987,5988,5989,5990,5991,5992,5993,
5994,5995,5996,5997,5998,5999,6000,6001,6002,6003,6004,6005,6006,6007,6008,
6009,6010,6011,6012,6013,6014,6015,6016,6017,6018,6019,6020,6021,6022,6023,
6024,6025,6026,6027,6028,6029,6030,6031,6032,6033,6034,6035,6036,6037,6038,
6039,6040,6041,6042,6043,6044,6045,6046,6047,6048,6049,6050,6051,6052,6053,
6054,6055,6056,6057,6058,6059,6060,6061,6062,6063,6064,6065,6066,6067,6068,
6069,6070,6071,6072,6073,6074,6075,6076,6077,6078,6079,6080,6081,6082,6083,
6084,6085,6086,6087,6088,6089,6090,6091,6092,6093,6094,6095,6096,6097,6098,
6099,6100,6101,6102,6103,6104,6105,6106,6107,6108,6109,6110,6111,6112,6113,
6114,6115,6116,6117,6118,6119,6120,6121,6122,6123,6124,6125,6126,6127,6128,
6129,6130,6131,6132,6133,6134,6135,6136,6137,6138,6139,6140,6141,6142,6143,
6144,6145,6146,6147,6148,6149,6150,6151,6152,6153,6154,6155,6156,6157,6158,
6159,6160,6161,6162,6163,6164,6165,6166,6167,6168,6169,6170,6171,6172,6173,
6174,6175,6176,6177,6178,6179,6180,6181,6182,6183,6184,6185,6186,6187,6188,
6189,6190,6191,6192,6193,6194,6195,6196,6197,6198,6199,6200,6201,6202,6203,
6204,6205,6206,6207,6208,6209,6210,6211,6212,6213,6214,6215,6216,6217,6218,
6219,6220,6221,6222,6223,6224,6225,6226,6227,6228,6229,6230,6231,6232,6233,
6234,6235,6236,6237,6238,6239,6240,6241,6242,6243,6244,6245,6246,6247,6248,
6249,6250,6251,6252,6253,6254,6255,6256,6257,6258,6259,6260,6261,6262,6263,
6264,6265,6266,6267,6268,6269,6270,6271,6272,6273,6274,6275,6276,6277,6278,
6279,6280,6281,6282,6283,6284,6285,6286,6287,6288,6289,6290,6291,6292,6293,
6294,6295,6296,6297,6298,6299,6300,6301,6302,6303,6304,6305,6306,6307,6308,
6309,6310,6311,6312,6313,6314,6315,6316,6317,6318,6319,6320,6321,6322,6323,
6324,6325,6326,6327,6328,6329,6330,6331,6332,6333,6334,6335,6336,6337,6338,
6339,6340,6341,6342,6343,6344,6345,6346,6347,6348,6349,6350,6351,6352,6353,
6354,6355,6356,6357,6358,6359,6360,6361,6362,6363,6364,6365,6366,6367,6368,
6369,6370,6371,6372,6373,6374,6375,6376,6377,6378,6379,6380,6381,6382,6383,
6384,6385,6386,6387,6388,6389,6390,6391,6392,6393,6394,6395,6396,6397,6398,
6399,6400,6401,6402,6403,6404,6405,6406,6407,6408,6409,6410,6411,6412,6413,
6414,6415,6416,6417,6418,6419,6420,6421,6422,6423,6424,6425,6426,6427,6428,
6429,6430,6431,6432,6433,6434,6435,6436,6437,6438,6439,6440,6441,6442,6443,
6444,6445,6446,6447,6448,6449,6450,6451,6452,6453,6454,6455,6456,6457,6458,
6459,6460,6461,6462,6463,6464,6465,6466,6467,6468,6469,6470,6471,6472,6473,
6474,6475,6476,6477,6478,6479,6480,6481,6482,6483,6484,6485,6486,6487,6488,
6489,6490,6491,6492,6493,6494,6495,6496,6497,6498,6499,6500,6501,6502,6503,
6504,6505,6506,6507,6508,6509,6510,6511,6512,6513,6514,6515,6516,6517,6518,
6519,6520,6521,6522,6523,6524,6525,6526,6527,6528,6529,6530,6531,6532,6533,
6534,6535,6536,6537,6538,6539,6540,6541,6542,6543,6544,6545,6546,6547,6548,
6549,6550,6551,6552,6553,6554,6555,6556,6557,6558,6559,6560,6561,6562,6563,
6564,6565,6566,6567,6568,6569,6570,6571,6572,6573,6574,6575,6576,6577,6578,
6579,6580,6581,6582,6583,6584,6585,6586,6587,6588,6589,6590,6591,6592,6593,
6594,6595,6596,6597,6598,6599,6600,6601,6602,6603,6604,6605,6606,6607,6608,
6609,6610,6611,6612,6613,6614,6615,6616,6617,6618,6619,6620,6621,6622,6623,
6624,6625,6626,6627,6628,6629,6630,6631,6632,6633,6634,6635,6636,6637,6638,
6639,6640,6641,6642,6643,6644,6645,6646,6647,6648,6649,6650,6651,6652,6653,
6654,6655,6656,6657,6658,6659,6660,6661,6662,6663,6664,6665,6666,6667,6668,
6669,6670,6671,6672,6673,6674,6675,6676,6677,6678,6679,6680,6681,6682,6683,
6684,6685,6686,6687,6688,6689,6690,6691,6692,6693,6694,6695,6696,6697,6698,
6699,6700,6701,6702,6703,6704,6705,6706,6707,6708,6709,6710,6711,6712,6713,
6714,6715,6716,6717,6718,6719,6720,6721,6722,6723,6724,6725,6726,6727,6728,
6729,6730,6731,6732,6733,6734,6735,6736,6737,6738,6739,6740,6741,6742,6743,
6744,6745,6746,6747,6748,6749,6750,6751,6752,6753,6754,6755,6756,6757,6758,
6759,6760,6761,6762,6763,6764,6765,6766,6767,6768,6769,6770,6771,6772,6773,
6774,6775,6776,6777,6778,6779,6780,6781,6782,6783,6784,6785,6786,6787,6788,
6789,6790,6791,6792,6793,6794,6795,6796,6797,6798,6799,6800,6801,6802,6803,
6804,6805,6806,6807,6808,6809,6810,6811,6812,6813,6814,6815,6816,6817,6818,
6819,6820,6821,6822,6823,6824,6825,6826,6827,6828,6829,6830,6831,6832,6833,
6834,6835,6836,6837,6838,6839,6840,6841,6842,6843,6844,6845,6846,6847,6848,
6849,6850,6851,6852,6853,6854,6855,6856,6857,6858,6859,6860,6861,6862,6863,
6864,6865,6866,6867,6868,6869,6870,6871,6872,6873,6874,6875,6876,6877,6878,
6879,6880,6881,6882,6883,6884,6885,6886,6887,6888,6889,6890,6891,6892,6893,
6894,6895,6896,6897,6898,6899,6900,6901,6902,6903,6904,6905,6906,6907,6908,
6909,6910,6911,6912,6913,6914,6915,6916,6917,6918,6919,6920,6921,6922,6923,
6924,6925,6926,6927,6928,6929,6930,6931,6932,6933,6934,6935,6936,6937,6938,
6939,6940,6941,6942,6943,6944,6945,6946,6947,6948,6949,6950,6951,6952,6953,
6954,6955,6956,6957,6958,6959,6960,6961,6962,6963,6964,6965,6966,6967,6968,
6969,6970,6971,6972,6973,6974,6975,6976,6977,6978,6979,6980,6981,6982,6983,
6984,6985,6986,6987,6988,6989,6990,6991,6992,6993,6994,6995,6996,6997,6998,
6999,7000,7001,7002,7003,7004,7005,7006,7007,7008,7009,7010,7011,7012,7013,
7014,7015,7016,7017,7018,7019,7020,7021,7022,7023,7024,7025,7026,7027,7028,
7029,7030,7031,7032,7033,7034,7035,7036,7037,7038,7039,7040,7041,7042,7043,
7044,7045,7046,7047,7048,7049,7050,7051,7052,7053,7054,7055,7056,7057,7058,
7059,7060,7061,7062,7063,7064,7065,7066,7067,7068,7069,7070,7071,7072,7073,
7074,7075,7076,7077,7078,7079,7080,7081,7082,7083,7084,7085,7086,7087,7088,
7089,7090,7091,7092,7093,7094,7095,7096,7097,7098,7099,7100,7101,7102,7103,
7104,7105,7106,7107,7108,7109,7110,7111,7112,7113,7114,7115,7116,7117,7118,
7119,7120,7121,7122,7123,7124,7125,7126,7127,7128,7129,7130,7131,7132,7133,
7134,7135,7136,7137,7138,7139,7140,7141,7142,7143,7144,7145,7146,7147,7148,
7149,7150,7151,7152,7153,7154,7155,7156,7157,7158,7159,7160,7161,7162,7163,
7164,7165,7166,7167,7168,7169,7170,7171,7172,7173,7174,7175,7176,7177,7178,
7179,7180,7181,7182,7183,7184,7185,7186,7187,7188,7189,7190,7191,7192,7193,
7194,7195,7196,7197,7198,7199,7200,7201,7202,7203,7204,7205,7206,7207,7208,
7209,7210,7211,7212,7213,7214,7215,7216,7217,7218,7219,7220,7221,7222,7223,
7224,7225,7226,7227,7228,7229,7230,7231,7232,7233,7234,7235,7236,7237,7238,
7239,7240,7241,7242,7243,7244,7245,7246,7247,7248,7249,7250,7251,7252,7253,
7254,7255,7256,7257,7258,7259,7260,7261,7262,7263,7264,7265,7266,7267,7268,
7269,7270,7271,7272,7273,7274,7275,7276,7277,7278,7279,7280,7281,7282,7283,
7284,7285,7286,7287,7288,7289,7290,7291,7292,7293,7294,7295,1042,1044,1054,
1057,1058,1058,1066,1122,42570L,7305,7306,7307,7308,7309,7310,7311,7312,
7313,7314,7315,7316,7317,7318,7319,7320,7321,7322,7323,7324,7325,7326,7327,
7328,7329,7330,7331,7332,7333,7334,7335,7336,7337,7338,7339,7340,7341,7342,
7343,7344,7345,7346,7347,7348,7349,7350,7351,7352,7353,7354,7355,7356,7357,
7358,7359,7360,7361,7362,7363,7364,7365,7366,7367,7368,7369,7370,7371,7372,
7373,7374,7375,7376,7377,7378,7379,7380,7381,7382,7383,7384,7385,7386,7387,
7388,7389,7390,7391,7392,7393,7394,7395,7396,7397,7398,7399,7400,7401,7402,
7403,7404,7405,7406,7407,7408,7409,7410,7411,7412,7413,7414,7415,7416,7417,
7418,7419,7420,7421,7422,7423,7424,7425,7426,7427,7428,7429,7430,7431,7432,
7433,7434,7435,7436,7437,7438,7439,7440,7441,7442,7443,7444,7445,7446,7447,
7448,7449,7450,7451,7452,7453,7454,7455,7456,7457,7458,7459,7460,7461,7462,
7463,7464,7465,7466,7467,7468,7469,7470,7471,7472,7473,7474,7475,7476,7477,
7478,7479,7480,7481,7482,7483,7484,7485,7486,7487,7488,7489,7490,7491,7492,
7493,7494,7495,7496,7497,7498,7499,7500,7501,7502,7503,7504,7505,7506,7507,
7508,7509,7510,7511,7512,7513,7514,7515,7516,7517,7518,7519,7520,7521,7522,
7523,7524,7525,7526,7527,7528,7529,7530,7531,7532,7533,7534,7535,7536,7537,
7538,7539,7540,7541,7542,7543,7544,42877L,7546,7547,7548,11363,7550,7551,
7552,7553,7554,7555,7556,7557,7558,7559,7560,7561,7562,7563,7564,7565,
42950L,7567,7568,7569,7570,7571,7572,7573,7574,7575,7576,7577,7578,7579,
7580,7581,7582,7583,7584,7585,7586,7587,7588,7589,7590,7591,7592,7593,7594,
7595,7596,7597,7598,7599,7600,7601,7602,7603,7604,7605,7606,7607,7608,7609,
7610,7611,7612,7613,7614,7615,7616,7617,7618,7619,7620,7621,7622,7623,7624,
7625,7626,7627,7628,7629,7630,7631,7632,7633,7634,7635,7636,7637,7638,7639,
7640,7641,7642,7643,7644,7645,7646,7647,7648,7649,7650,7651,7652,7653,7654,
7655,7656,7657,7658,7659,7660,7661,7662,7663,7664,7665,7666,7667,7668,7669,
7670,7671,7672,7673,7674,7675,7676,7677,7678,7679,7680,7680,7682,7682,7684,
7684,7686,7686,7688,7688,7690,7690,7692,7692,7694,7694,7696,7696,7698,7698,
7700,7700,7702,7702,7704,7704,7706,7706,7708,7708,7710,7710,7712,7712,7714,
7714,7716,7716,7718,7718,7720,7720,7722,7722,7724,7724,7726,7726,7728,7728,
7730,7730,7732,7732,7734,7734,7736,7736,7738,7738,7740,7740,7742,7742,7744,
7744,7746,7746,7748,7748,7750,7750,7752,7752,7754,7754,7756,7756,7758,7758,
7760,7760,7762,7762,7764,7764,7766,7766,7768,7768,7770,7770,7772,7772,7774,
7774,7776,7776,7778,7778,7780,7780,7782,7782,7784,7784,7786,7786,7788,7788,
7790,7790,7792,7792,7794,7794,7796,7796,7798,7798,7800,7800,7802,7802,7804,
7804,7806,7806,7808,7808,7810,7810,7812,7812,7814,7814,7816,7816,7818,7818,
7820,7820,7822,7822,7824,7824,7826,7826,7828,7828,7830,7831,7832,7833,7834,
7776,7836,7837,7838,7839,7840,7840,7842,7842,7844,7844,7846,7846,7848,7848,
7850,7850,7852,7852,7854,7854,7856,7856,7858,7858,7860,7860,7862,7862,7864,
7864,7866,7866,7868,7868,7870,7870,7872,7872,7874,7874,7876,7876,7878,7878,
7880,7880,7882,7882,7884,7884,7886,7886,7888,7888,7890,7890,7892,7892,7894,
7894,7896,7896,7898,7898,7900,7900,7902,7902,7904,7904,7906,7906,7908,7908,
7910,7910,7912,7912,7914,7914,7916,7916,7918,7918,7920,7920,7922,7922,7924,
7924,7926,7926,7928,7928,7930,7930,7932,7932,7934,7934,7944,7945,7946,7947,
7948,7949,7950,7951,7944,7945,7946,7947,7948,7949,7950,7951,7960,7961,7962,
7963,7964,7965,7958,7959,7960,7961,7962,7963,7964,7965,7966,7967,7976,7977,
7978,7979,7980,7981,7982,7983,7976,7977,7978,7979,7980,7981,7982,7983,7992,
7993,7994,7995,7996,7997,7998,7999,7992,7993,7994,7995,7996,7997,7998,7999,
8008,8009,8010,8011,8012,8013,8006,8007,8008,8009,8010,8011,8012,8013,8014,
8015,8016,8025,8018,8027,8020,8029,8022,8031,8024,8025,8026,8027,8028,8029,
8030,8031,8040,8041,8042,8043,8044,8045,8046,8047,8040,8041,8042,8043,8044,
8045,8046,8047,8122,8123,8136,8137,8138,8139,8154,8155,8184,8185,8170,8171,
8186,8187,8062,8063,8064,8065,8066,8067,8068,8069,8070,8071,8072,8073,8074,
8075,8076,8077,8078,8079,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,
8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8102,8103,8104,
8105,8106,8107,8108,8109,8110,8111,8120,8121,8114,8115,8116,8117,8118,8119,
8120,8121,8122,8123,8124,8125,921,8127,8128,8129,8130,8131,8132,8133,8134,
8135,8136,8137,8138,8139,8140,8141,8142,8143,8152,8153,8146,8147,8148,8149,
8150,8151,8152,8153,8154,8155,8156,8157,8158,8159,8168,8169,8162,8163,8164,
8172,8166,8167,8168,8169,8170,8171,8172,8173,8174,8175,8176,8177,8178,8179,
8180,8181,8182,8183,8184,8185,8186,8187,8188,8189,8190,8191,8192,8193,8194,
8195,8196,8197,8198,8199,8200,8201,8202,8203,8204,8205,8206,8207,8208,8209,
8210,8211,8212,8213,8214,8215,8216,8217,8218,8219,8220,8221,8222,8223,8224,
8225,8226,8227,8228,8229,8230,8231,8232,8233,8234,8235,8236,8237,8238,8239,
8240,8241,8242,8243,8244,8245,8246,8247,8248,8249,8250,8251,8252,8253,8254,
8255,8256,8257,8258,8259,8260,8261,8262,8263,8264,8265,8266,8267,8268,8269,
8270,8271,8272,8273,8274,8275,8276,8277,8278,8279,8280,8281,8282,8283,8284,
8285,8286,8287,8288,8289,8290,8291,8292,8293,8294,8295,8296,8297,8298,8299,
8300,8301,8302,8303,8304,8305,8306,8307,8308,8309,8310,8311,8312,8313,8314,
8315,8316,8317,8318,8319,8320,8321,8322,8323,8324,8325,8326,8327,8328,8329,
8330,8331,8332,8333,8334,8335,8336,8337,8338,8339,8340,8341,8342,8343,8344,
8345,8346,8347,8348,8349,8350,8351,8352,8353,8354,8355,8356,8357,8358,8359,
8360,8361,8362,8363,8364,8365,8366,8367,8368,8369,8370,8371,8372,8373,8374,
8375,8376,8377,8378,8379,8380,8381,8382,8383,8384,8385,8386,8387,8388,8389,
8390,8391,8392,8393,8394,8395,8396,8397,8398,8399,8400,8401,8402,8403,8404,
8405,8406,8407,8408,8409,8410,8411,8412,8413,8414,8415,8416,8417,8418,8419,
8420,8421,8422,8423,8424,8425,8426,8427,8428,8429,8430,8431,8432,8433,8434,
8435,8436,8437,8438,8439,8440,8441,8442,8443,8444,8445,8446,8447,8448,8449,
8450,8451,8452,8453,8454,8455,8456,8457,8458,8459,8460,8461,8462,8463,8464,
8465,8466,8467,8468,8469,8470,8471,8472,8473,8474,8475,8476,8477,8478,8479,
8480,8481,8482,8483,8484,8485,8486,8487,8488,8489,8490,8491,8492,8493,8494,
8495,8496,8497,8498,8499,8500,8501,8502,8503,8504,8505,8506,8507,8508,8509,
8510,8511,8512,8513,8514,8515,8516,8517,8518,8519,8520,8521,8522,8523,8524,
8525,8498,8527,8528,8529,8530,8531,8532,8533,8534,8535,8536,8537,8538,8539,
8540,8541,8542,8543,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,8554,
8555,8556,8557,8558,8559,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,
8554,8555,8556,8557,8558,8559,8576,8577,8578,8579,8579,8581,8582,8583,8584,
8585,8586,8587,8588,8589,8590,8591,8592,8593,8594,8595,8596,8597,8598,8599,
8600,8601,8602,8603,8604,8605,8606,8607,8608,8609,8610,8611,8612,8613,8614,
8615,8616,8617,8618,8619,8620,8621,8622,8623,8624,8625,8626,8627,8628,8629,
8630,8631,8632,8633,8634,8635,8636,8637,8638,8639,8640,8641,8642,8643,8644,
8645,8646,8647,8648,8649,8650,8651,8652,8653,8654,8655,8656,8657,8658,8659,
8660,8661,8662,8663,8664,8665,8666,8667,8668,8669,8670,8671,8672,8673,8674,
8675,8676,8677,8678,8679,8680,8681,8682,8683,8684,8685,8686,8687,8688,8689,
8690,8691,8692,8693,8694,8695,8696,8697,8698,8699,8700,8701,8702,8703,8704,
8705,8706,8707,8708,8709,8710,8711,8712,8713,8714,8715,8716,8717,8718,8719,
8720,8721,8722,8723,8724,8725,8726,8727,8728,8729,8730,8731,8732,8733,8734,
8735,8736,8737,8738,8739,8740,8741,8742,8743,8744,8745,8746,8747,8748,8749,
8750,8751,8752,8753,8754,8755,8756,8757,8758,8759,8760,8761,8762,8763,8764,
8765,8766,8767,8768,8769,8770,8771,8772,8773,8774,8775,8776,8777,8778,8779,
8780,8781,8782,8783,8784,8785,8786,8787,8788,8789,8790,8791,8792,8793,8794,
8795,8796,8797,8798,8799,8800,8801,8802,8803,8804,8805,8806,8807,8808,8809,
8810,8811,8812,8813,8814,8815,8816,8817,8818,8819,8820,8821,8822,8823,8824,
8825,8826,8827,8828,8829,8830,8831,8832,8833,8834,8835,8836,8837,8838,8839,
8840,8841,8842,8843,8844,8845,8846,8847,8848,8849,8850,8851,8852,8853,8854,
8855,8856,8857,8858,8859,8860,8861,8862,8863,8864,8865,8866,8867,8868,8869,
8870,8871,8872,8873,8874,8875,8876,8877,8878,8879,8880,8881,8882,8883,8884,
8885,8886,8887,8888,8889,8890,8891,8892,8893,8894,8895,8896,8897,8898,8899,
8900,8901,8902,8903,8904,8905,8906,8907,8908,8909,8910,8911,8912,8913,8914,
8915,8916,8917,8918,8919,8920,8921,8922,8923,8924,8925,8926,8927,8928,8929,
8930,8931,8932,8933,8934,8935,8936,8937,8938,8939,8940,8941,8942,8943,8944,
8945,8946,8947,8948,8949,8950,8951,8952,8953,8954,8955,8956,8957,8958,8959,
8960,8961,8962,8963,8964,8965,8966,8967,8968,8969,8970,8971,8972,8973,8974,
8975,8976,8977,8978,8979,8980,8981,8982,8983,8984,8985,8986,8987,8988,8989,
8990,8991,8992,8993,8994,8995,8996,8997,8998,8999,9000,9001,9002,9003,9004,
9005,9006,9007,9008,9009,9010,9011,9012,9013,9014,9015,9016,9017,9018,9019,
9020,9021,9022,9023,9024,9025,9026,9027,9028,9029,9030,9031,9032,9033,9034,
9035,9036,9037,9038,9039,9040,9041,9042,9043,9044,9045,9046,9047,9048,9049,
9050,9051,9052,9053,9054,9055,9056,9057,9058,9059,9060,9061,9062,9063,9064,
9065,9066,9067,9068,9069,9070,9071,9072,9073,9074,9075,9076,9077,9078,9079,
9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,
9095,9096,9097,9098,9099,9100,9101,9102,9103,9104,9105,9106,9107,9108,9109,
9110,9111,9112,9113,9114,9115,9116,9117,9118,9119,9120,9121,9122,9123,9124,
9125,9126,9127,9128,9129,9130,9131,9132,9133,9134,9135,9136,9137,9138,9139,
9140,9141,9142,9143,9144,9145,9146,9147,9148,9149,9150,9151,9152,9153,9154,
9155,9156,9157,9158,9159,9160,9161,9162,9163,9164,9165,9166,9167,9168,9169,
9170,9171,9172,9173,9174,9175,9176,9177,9178,9179,9180,9181,9182,9183,9184,
9185,9186,9187,9188,9189,9190,9191,9192,9193,9194,9195,9196,9197,9198,9199,
9200,9201,9202,9203,9204,9205,9206,9207,9208,9209,9210,9211,9212,9213,9214,
9215,9216,9217,9218,9219,9220,9221,9222,9223,9224,9225,9226,9227,9228,9229,
9230,9231,9232,9233,9234,9235,9236,9237,9238,9239,9240,9241,9242,9243,9244,
9245,9246,9247,9248,9249,9250,9251,9252,9253,9254,9255,9256,9257,9258,9259,
9260,9261,9262,9263,9264,9265,9266,9267,9268,9269,9270,9271,9272,9273,9274,
9275,9276,9277,9278,9279,9280,9281,9282,9283,9284,9285,9286,9287,9288,9289,
9290,9291,9292,9293,9294,9295,9296,9297,9298,9299,9300,9301,9302,9303,9304,
9305,9306,9307,9308,9309,9310,9311,9312,9313,9314,9315,9316,9317,9318,9319,
9320,9321,9322,9323,9324,9325,9326,9327,9328,9329,9330,9331,9332,9333,9334,
9335,9336,9337,9338,9339,9340,9341,9342,9343,9344,9345,9346,9347,9348,9349,
9350,9351,9352,9353,9354,9355,9356,9357,9358,9359,9360,9361,9362,9363,9364,
9365,9366,9367,9368,9369,9370,9371,9372,9373,9374,9375,9376,9377,9378,9379,
9380,9381,9382,9383,9384,9385,9386,9387,9388,9389,9390,9391,9392,9393,9394,
9395,9396,9397,9398,9399,9400,9401,9402,9403,9404,9405,9406,9407,9408,9409,
9410,9411,9412,9413,9414,9415,9416,9417,9418,9419,9420,9421,9422,9423,9398,
9399,9400,9401,9402,9403,9404,9405,9406,9407,9408,9409,9410,9411,9412,9413,
9414,9415,9416,9417,9418,9419,9420,9421,9422,9423,9450,9451,9452,9453,9454,
9455,9456,9457,9458,9459,9460,9461,9462,9463,9464,9465,9466,9467,9468,9469,
9470,9471,9472,9473,9474,9475,9476,9477,9478,9479,9480,9481,9482,9483,9484,
9485,9486,9487,9488,9489,9490,9491,9492,9493,9494,9495,9496,9497,9498,9499,
9500,9501,9502,9503,9504,9505,9506,9507,9508,9509,9510,9511,9512,9513,9514,
9515,9516,9517,9518,9519,9520,9521,9522,9523,9524,9525,9526,9527,9528,9529,
9530,9531,9532,9533,9534,9535,9536,9537,9538,9539,9540,9541,9542,9543,9544,
9545,9546,9547,9548,9549,9550,9551,9552,9553,9554,9555,9556,9557,9558,9559,
9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,9570,9571,9572,9573,9574,
9575,9576,9577,9578,9579,9580,9581,9582,9583,9584,9585,9586,9587,9588,9589,
9590,9591,9592,9593,9594,9595,9596,9597,9598,9599,9600,9601,9602,9603,9604,
9605,9606,9607,9608,9609,9610,9611,9612,9613,9614,9615,9616,9617,9618,9619,
9620,9621,9622,9623,9624,9625,9626,9627,9628,9629,9630,9631,9632,9633,9634,
9635,9636,9637,9638,9639,9640,9641,9642,9643,9644,9645,9646,9647,9648,9649,
9650,9651,9652,9653,9654,9655,9656,9657,9658,9659,9660,9661,9662,9663,9664,
9665,9666,9667,9668,9669,9670,9671,9672,9673,9674,9675,9676,9677,9678,9679,
9680,9681,9682,9683,9684,9685,9686,9687,9688,9689,9690,9691,9692,9693,9694,
9695,9696,9697,9698,9699,9700,9701,9702,9703,9704,9705,9706,9707,9708,9709,
9710,9711,9712,9713,9714,9715,9716,9717,9718,9719,9720,9721,9722,9723,9724,
9725,9726,9727,9728,9729,9730,9731,9732,9733,9734,9735,9736,9737,9738,9739,
9740,9741,9742,9743,9744,9745,9746,9747,9748,9749,9750,9751,9752,9753,9754,
9755,9756,9757,9758,9759,9760,9761,9762,9763,9764,9765,9766,9767,9768,9769,
9770,9771,9772,9773,9774,9775,9776,9777,9778,9779,9780,9781,9782,9783,9784,
9785,9786,9787,9788,9789,9790,9791,9792,9793,9794,9795,9796,9797,9798,9799,
9800,9801,9802,9803,9804,9805,9806,9807,9808,9809,9810,9811,9812,9813,9814,
9815,9816,9817,9818,9819,9820,9821,9822,9823,9824,9825,9826,9827,9828,9829,
9830,9831,9832,9833,9834,9835,9836,9837,9838,9839,9840,9841,9842,9843,9844,
9845,9846,9847,9848,9849,9850,9851,9852,9853,9854,9855,9856,9857,9858,9859,
9860,9861,9862,9863,9864,9865,9866,9867,9868,9869,9870,9871,9872,9873,9874,
9875,9876,9877,9878,9879,9880,9881,9882,9883,9884,9885,9886,9887,9888,9889,
9890,9891,9892,9893,9894,9895,9896,9897,9898,9899,9900,9901,9902,9903,9904,
9905,9906,9907,9908,9909,9910,9911,9912,9913,9914,9915,9916,9917,9918,9919,
9920,9921,9922,9923,9924,9925,9926,9927,9928,9929,9930,9931,9932,9933,9934,
9935,9936,9937,9938,9939,9940,9941,9942,9943,9944,9945,9946,9947,9948,9949,
9950,9951,9952,9953,9954,9955,9956,9957,9958,9959,9960,9961,9962,9963,9964,
9965,9966,9967,9968,9969,9970,9971,9972,9973,9974,9975,9976,9977,9978,9979,
9980,9981,9982,9983,9984,9985,9986,9987,9988,9989,9990,9991,9992,9993,9994,
9995,9996,9997,9998,9999,10000,10001,10002,10003,10004,10005,10006,10007,
10008,10009,10010,10011,10012,10013,10014,10015,10016,10017,10018,10019,
10020,10021,10022,10023,10024,10025,10026,10027,10028,10029,10030,10031,
10032,10033,10034,10035,10036,10037,10038,10039,10040,10041,10042,10043,
10044,10045,10046,10047,10048,10049,10050,10051,10052,10053,10054,10055,
10056,10057,10058,10059,10060,10061,10062,10063,10064,10065,10066,10067,
10068,10069,10070,10071,10072,10073,10074,10075,10076,10077,10078,10079,
10080,10081,10082,10083,10084,10085,10086,10087,10088,10089,10090,10091,
10092,10093,10094,10095,10096,10097,10098,10099,10100,10101,10102,10103,
10104,10105,10106,10107,10108,10109,10110,10111,10112,10113,10114,10115,
10116,10117,10118,10119,10120,10121,10122,10123,10124,10125,10126,10127,
10128,10129,10130,10131,10132,10133,10134,10135,10136,10137,10138,10139,
10140,10141,10142,10143,10144,10145,10146,10147,10148,10149,10150,10151,
10152,10153,10154,10155,10156,10157,10158,10159,10160,10161,10162,10163,
10164,10165,10166,10167,10168,10169,10170,10171,10172,10173,10174,10175,
10176,10177,10178,10179,10180,10181,10182,10183,10184,10185,10186,10187,
10188,10189,10190,10191,10192,10193,10194,10195,10196,10197,10198,10199,
10200,10201,10202,10203,10204,10205,10206,10207,10208,10209,10210,10211,
10212,10213,10214,10215,10216,10217,10218,10219,10220,10221,10222,10223,
10224,10225,10226,10227,10228,10229,10230,10231,10232,10233,10234,10235,
10236,10237,10238,10239,10240,10241,10242,10243,10244,10245,10246,10247,
10248,10249,10250,10251,10252,10253,10254,10255,10256,10257,10258,10259,
10260,10261,10262,10263,10264,10265,10266,10267,10268,10269,10270,10271,
10272,10273,10274,10275,10276,10277,10278,10279,10280,10281,10282,10283,
10284,10285,10286,10287,10288,10289,10290,10291,10292,10293,10294,10295,
10296,10297,10298,10299,10300,10301,10302,10303,10304,10305,10306,10307,
10308,10309,10310,10311,10312,10313,10314,10315,10316,10317,10318,10319,
10320,10321,10322,10323,10324,10325,10326,10327,10328,10329,10330,10331,
10332,10333,10334,10335,10336,10337,10338,10339,10340,10341,10342,10343,
10344,10345,10346,10347,10348,10349,10350,10351,10352,10353,10354,10355,
10356,10357,10358,10359,10360,10361,10362,10363,10364,10365,10366,10367,
10368,10369,10370,10371,10372,10373,10374,10375,10376,10377,10378,10379,
10380,10381,10382,10383,10384,10385,10386,10387,10388,10389,10390,10391,
10392,10393,10394,10395,10396,10397,10398,10399,10400,10401,10402,10403,
10404,10405,10406,10407,10408,10409,10410,10411,10412,10413,10414,10415,
10416,10417,10418,10419,10420,10421,10422,10423,10424,10425,10426,10427,
10428,10429,10430,10431,10432,10433,10434,10435,10436,10437,10438,10439,
10440,10441,10442,10443,10444,10445,10446,10447,10448,10449,10450,10451,
10452,10453,10454,10455,10456,10457,10458,10459,10460,10461,10462,10463,
10464,10465,10466,10467,10468,10469,10470,10471,10472,10473,10474,10475,
10476,10477,10478,10479,10480,10481,10482,10483,10484,10485,10486,10487,
10488,10489,10490,10491,10492,10493,10494,10495,10496,10497,10498,10499,
10500,10501,10502,10503,10504,10505,10506,10507,10508,10509,10510,10511,
10512,10513,10514,10515,10516,10517,10518,10519,10520,10521,10522,10523,
10524,10525,10526,10527,10528,10529,10530,10531,10532,10533,10534,10535,
10536,10537,10538,10539,10540,10541,10542,10543,10544,10545,10546,10547,
10548,10549,10550,10551,10552,10553,10554,10555,10556,10557,10558,10559,
10560,10561,10562,10563,10564,10565,10566,10567,10568,10569,10570,10571,
10572,10573,10574,10575,10576,10577,10578,10579,10580,10581,10582,10583,
10584,10585,10586,10587,10588,10589,10590,10591,10592,10593,10594,10595,
10596,10597,10598,10599,10600,10601,10602,10603,10604,10605,10606,10607,
10608,10609,10610,10611,10612,10613,10614,10615,10616,10617,10618,10619,
10620,10621,10622,10623,10624,10625,10626,10627,10628,10629,10630,10631,
10632,10633,10634,10635,10636,10637,10638,10639,10640,10641,10642,10643,
10644,10645,10646,10647,10648,10649,10650,10651,10652,10653,10654,10655,
10656,10657,10658,10659,10660,10661,10662,10663,10664,10665,10666,10667,
10668,10669,10670,10671,10672,10673,10674,10675,10676,10677,10678,10679,
10680,10681,10682,10683,10684,10685,10686,10687,10688,10689,10690,10691,
10692,10693,10694,10695,10696,10697,10698,10699,10700,10701,10702,10703,
10704,10705,10706,10707,10708,10709,10710,10711,10712,10713,10714,10715,
10716,10717,10718,10719,10720,10721,10722,10723,10724,10725,10726,10727,
10728,10729,10730,10731,10732,10733,10734,10735,10736,10737,10738,10739,
10740,10741,10742,10743,10744,10745,10746,10747,10748,10749,10750,10751,
10752,10753,10754,10755,10756,10757,10758,10759,10760,10761,10762,10763,
10764,10765,10766,10767,10768,10769,10770,10771,10772,10773,10774,10775,
10776,10777,10778,10779,10780,10781,10782,10783,10784,10785,10786,10787,
10788,10789,10790,10791,10792,10793,10794,10795,10796,10797,10798,10799,
10800,10801,10802,10803,10804,10805,10806,10807,10808,10809,10810,10811,
10812,10813,10814,10815,10816,10817,10818,10819,10820,10821,10822,10823,
10824,10825,10826,10827,10828,10829,10830,10831,10832,10833,10834,10835,
10836,10837,10838,10839,10840,10841,10842,10843,10844,10845,10846,10847,
10848,10849,10850,10851,10852,10853,10854,10855,10856,10857,10858,10859,
10860,10861,10862,10863,10864,10865,10866,10867,10868,10869,10870,10871,
10872,10873,10874,10875,10876,10877,10878,10879,10880,10881,10882,10883,
10884,10885,10886,10887,10888,10889,10890,10891,10892,10893,10894,10895,
10896,10897,10898,10899,10900,10901,10902,10903,10904,10905,10906,10907,
10908,10909,10910,10911,10912,10913,10914,10915,10916,10917,10918,10919,
10920,10921,10922,10923,10924,10925,10926,10927,10928,10929,10930,10931,
10932,10933,10934,10935,10936,10937,10938,10939,10940,10941,10942,10943,
10944,10945,10946,10947,10948,10949,10950,10951,10952,10953,10954,10955,
10956,10957,10958,10959,10960,10961,10962,10963,10964,10965,10966,10967,
10968,10969,10970,10971,10972,10973,10974,10975,10976,10977,10978,10979,
10980,10981,10982,10983,10984,10985,10986,10987,10988,10989,10990,10991,
10992,10993,10994,10995,10996,10997,10998,10999,11000,11001,11002,11003,
11004,11005,11006,11007,11008,11009,11010,11011,11012,11013,11014,11015,
11016,11017,11018,11019,11020,11021,11022,11023,11024,11025,11026,11027,
11028,11029,11030,11031,11032,11033,11034,11035,11036,11037,11038,11039,
11040,11041,11042,11043,11044,11045,11046,11047,11048,11049,11050,11051,
11052,11053,11054,11055,11056,11057,11058,11059,11060,11061,11062,11063,
11064,11065,11066,11067,11068,11069,11070,11071,11072,11073,11074,11075,
11076,11077,11078,11079,11080,11081,11082,11083,11084,11085,11086,11087,
11088,11089,11090,11091,11092,11093,11094,11095,11096,11097,11098,11099,
11100,11101,11102,11103,11104,11105,11106,11107,11108,11109,11110,11111,
11112,11113,11114,11115,11116,11117,11118,11119,11120,11121,11122,11123,
11124,11125,11126,11127,11128,11129,11130,11131,11132,11133,11134,11135,
11136,11137,11138,11139,11140,11141,11142,11143,11144,11145,11146,11147,
11148,11149,11150,11151,11152,11153,11154,11155,11156,11157,11158,11159,
11160,11161,11162,11163,11164,11165,11166,11167,11168,11169,11170,11171,
11172,11173,11174,11175,11176,11177,11178,11179,11180,11181,11182,11183,
11184,11185,11186,11187,11188,11189,11190,11191,11192,11193,11194,11195,
11196,11197,11198,11199,11200,11201,11202,11203,11204,11205,11206,11207,
11208,11209,11210,11211,11212,11213,11214,11215,11216,11217,11218,11219,
11220,11221,11222,11223,11224,11225,11226,11227,11228,11229,11230,11231,
11232,11233,11234,11235,11236,11237,11238,11239,11240,11241,11242,11243,
11244,11245,11246,11247,11248,11249,11250,11251,11252,11253,11254,11255,
11256,11257,11258,11259,11260,11261,11262,11263,11264,11265,11266,11267,
11268,11269,11270,11271,11272,11273,11274,11275,11276,11277,11278,11279,
11280,11281,11282,11283,11284,11285,11286,11287,11288,11289,11290,11291,
11292,11293,11294,11295,11296,11297,11298,11299,11300,11301,11302,11303,
11304,11305,11306,11307,11308,11309,11310,11311,11264,11265,11266,11267,
11268,11269,11270,11271,11272,11273,11274,11275,11276,11277,11278,11279,
11280,11281,11282,11283,11284,11285,11286,11287,11288,11289,11290,11291,
11292,11293,11294,11295,11296,11297,11298,11299,11300,11301,11302,11303,
11304,11305,11306,11307,11308,11309,11310,11359,11360,11360,11362,11363,
11364,570,574,11367,11367,11369,11369,11371,11371,11373,11374,11375,11376,
11377,11378,11378,11380,11381,11381,11383,11384,11385,11386,11387,11388,
11389,11390,11391,11392,11392,11394,11394,11396,11396,11398,11398,11400,
11400,11402,11402,11404,11404,11406,11406,11408,11408,11410,11410,11412,
11412,11414,11414,11416,11416,11418,11418,11420,11420,11422,11422,11424,
11424,11426,11426,11428,11428,11430,11430,11432,11432,11434,11434,11436,
11436,11438,11438,11440,11440,11442,11442,11444,11444,11446,11446,11448,
11448,11450,11450,11452,11452,11454,11454,11456,11456,11458,11458,11460,
11460,11462,11462,11464,11464,11466,11466,11468,11468,11470,11470,11472,
11472,11474,11474,11476,11476,11478,11478,11480,11480,11482,11482,11484,
11484,11486,11486,11488,11488,11490,11490,11492,11493,11494,11495,11496,
11497,11498,11499,11499,11501,11501,11503,11504,11505,11506,11506,11508,
11509,11510,11511,11512,11513,11514,11515,11516,11517,11518,11519,4256,
4257,4258,4259,4260,4261,4262,4263,4264,4265,4266,4267,4268,4269,4270,4271,
4272,4273,4274,4275,4276,4277,4278,4279,4280,4281,4282,4283,4284,4285,4286,
4287,4288,4289,4290,4291,4292,4293,11558,4295,11560,11561,11562,11563,
11564,4301,11566,11567,11568,11569,11570,11571,11572,11573,11574,11575,
11576,11577,11578,11579,11580,11581,11582,11583,11584,11585,11586,11587,
11588,11589,11590,11591,11592,11593,11594,11595,11596,11597,11598,11599,
11600,11601,11602,11603,11604,11605,11606,11607,11608,11609,11610,11611,
11612,11613,11614,11615,11616,11617,11618,11619,11620,11621,11622,11623,
11624,11625,11626,11627,11628,11629,11630,11631,11632,11633,11634,11635,
11636,11637,11638,11639,11640,11641,11642,11643,11644,11645,11646,11647,
11648,11649,11650,11651,11652,11653,11654,11655,11656,11657,11658,11659,
11660,11661,11662,11663,11664,11665,11666,11667,11668,11669,11670,11671,
11672,11673,11674,11675,11676,11677,11678,11679,11680,11681,11682,11683,
11684,11685,11686,11687,11688,11689,11690,11691,11692,11693,11694,11695,
11696,11697,11698,11699,11700,11701,11702,11703,11704,11705,11706,11707,
11708,11709,11710,11711,11712,11713,11714,11715,11716,11717,11718,11719,
11720,11721,11722,11723,11724,11725,11726,11727,11728,11729,11730,11731,
11732,11733,11734,11735,11736,11737,11738,11739,11740,11741,11742,11743,
11744,11745,11746,11747,11748,11749,11750,11751,11752,11753,11754,11755,
11756,11757,11758,11759,11760,11761,11762,11763,11764,11765,11766,11767,
11768,11769,11770,11771,11772,11773,11774,11775,11776,11777,11778,11779,
11780,11781,11782,11783,11784,11785,11786,11787,11788,11789,11790,11791,
11792,11793,11794,11795,11796,11797,11798,11799,11800,11801,11802,11803,
11804,11805,11806,11807,11808,11809,11810,11811,11812,11813,11814,11815,
11816,11817,11818,11819,11820,11821,11822,11823,11824,11825,11826,11827,
11828,11829,11830,11831,11832,11833,11834,11835,11836,11837,11838,11839,
11840,11841,11842,11843,11844,11845,11846,11847,11848,11849,11850,11851,
11852,11853,11854,11855,11856,11857,11858,11859,11860,11861,11862,11863,
11864,11865,11866,11867,11868,11869,11870,11871,11872,11873,11874,11875,
11876,11877,11878,11879,11880,11881,11882,11883,11884,11885,11886,11887,
11888,11889,11890,11891,11892,11893,11894,11895,11896,11897,11898,11899,
11900,11901,11902,11903,11904,11905,11906,11907,11908,11909,11910,11911,
11912,11913,11914,11915,11916,11917,11918,11919,11920,11921,11922,11923,
11924,11925,11926,11927,11928,11929,11930,11931,11932,11933,11934,11935,
11936,11937,11938,11939,11940,11941,11942,11943,11944,11945,11946,11947,
11948,11949,11950,11951,11952,11953,11954,11955,11956,11957,11958,11959,
11960,11961,11962,11963,11964,11965,11966,11967,11968,11969,11970,11971,
11972,11973,11974,11975,11976,11977,11978,11979,11980,11981,11982,11983,
11984,11985,11986,11987,11988,11989,11990,11991,11992,11993,11994,11995,
11996,11997,11998,11999,12000,12001,12002,12003,12004,12005,12006,12007,
12008,12009,12010,12011,12012,12013,12014,12015,12016,12017,12018,12019,
12020,12021,12022,12023,12024,12025,12026,12027,12028,12029,12030,12031,
12032,12033,12034,12035,12036,12037,12038,12039,12040,12041,12042,12043,
12044,12045,12046,12047,12048,12049,12050,12051,12052,12053,12054,12055,
12056,12057,12058,12059,12060,12061,12062,12063,12064,12065,12066,12067,
12068,12069,12070,12071,12072,12073,12074,12075,12076,12077,12078,12079,
12080,12081,12082,12083,12084,12085,12086,12087,12088,12089,12090,12091,
12092,12093,12094,12095,12096,12097,12098,12099,12100,12101,12102,12103,
12104,12105,12106,12107,12108,12109,12110,12111,12112,12113,12114,12115,
12116,12117,12118,12119,12120,12121,12122,12123,12124,12125,12126,12127,
12128,12129,12130,12131,12132,12133,12134,12135,12136,12137,12138,12139,
12140,12141,12142,12143,12144,12145,12146,12147,12148,12149,12150,12151,
12152,12153,12154,12155,12156,12157,12158,12159,12160,12161,12162,12163,
12164,12165,12166,12167,12168,12169,12170,12171,12172,12173,12174,12175,
12176,12177,12178,12179,12180,12181,12182,12183,12184,12185,12186,12187,
12188,12189,12190,12191,12192,12193,12194,12195,12196,12197,12198,12199,
12200,12201,12202,12203,12204,12205,12206,12207,12208,12209,12210,12211,
12212,12213,12214,12215,12216,12217,12218,12219,12220,12221,12222,12223,
12224,12225,12226,12227,12228,12229,12230,12231,12232,12233,12234,12235,
12236,12237,12238,12239,12240,12241,12242,12243,12244,12245,12246,12247,
12248,12249,12250,12251,12252,12253,12254,12255,12256,12257,12258,12259,
12260,12261,12262,12263,12264,12265,12266,12267,12268,12269,12270,12271,
12272,12273,12274,12275,12276,12277,12278,12279,12280,12281,12282,12283,
12284,12285,12286,12287,12288,12289,12290,12291,12292,12293,12294,12295,
12296,12297,12298,12299,12300,12301,12302,12303,12304,12305,12306,12307,
12308,12309,12310,12311,12312,12313,12314,12315,12316,12317,12318,12319,
12320,12321,12322,12323,12324,12325,12326,12327,12328,12329,12330,12331,
12332,12333,12334,12335,12336,12337,12338,12339,12340,12341,12342,12343,
12344,12345,12346,12347,12348,12349,12350,12351,12352,12353,12354,12355,
12356,12357,12358,12359,12360,12361,12362,12363,12364,12365,12366,12367,
12368,12369,12370,12371,12372,12373,12374,12375,12376,12377,12378,12379,
12380,12381,12382,12383,12384,12385,12386,12387,12388,12389,12390,12391,
12392,12393,12394,12395,12396,12397,12398,12399,12400,12401,12402,12403,
12404,12405,12406,12407,12408,12409,12410,12411,12412,12413,12414,12415,
12416,12417,12418,12419,12420,12421,12422,12423,12424,12425,12426,12427,
12428,12429,12430,12431,12432,12433,12434,12435,12436,12437,12438,12439,
12440,12441,12442,12443,12444,12445,12446,12447,12448,12449,12450,12451,
12452,12453,12454,12455,12456,12457,12458,12459,12460,12461,12462,12463,
12464,12465,12466,12467,12468,12469,12470,12471,12472,12473,12474,12475,
12476,12477,12478,12479,12480,12481,12482,12483,12484,12485,12486,12487,
12488,12489,12490,12491,12492,12493,12494,12495,12496,12497,12498,12499,
12500,12501,12502,12503,12504,12505,12506,12507,12508,12509,12510,12511,
12512,12513,12514,12515,12516,12517,12518,12519,12520,12521,12522,12523,
12524,12525,12526,12527,12528,12529,12530,12531,12532,12533,12534,12535,
12536,12537,12538,12539,12540,12541,12542,12543,12544,12545,12546,12547,
12548,12549,12550,12551,12552,12553,12554,12555,12556,12557,12558,12559,
12560,12561,12562,12563,12564,12565,12566,12567,12568,12569,12570,12571,
12572,12573,12574,12575,12576,12577,12578,12579,12580,12581,12582,12583,
12584,12585,12586,12587,12588,12589,12590,12591,12592,12593,12594,12595,
12596,12597,12598,12599,12600,12601,12602,12603,12604,12605,12606,12607,
12608,12609,12610,12611,12612,12613,12614,12615,12616,12617,12618,12619,
12620,12621,12622,12623,12624,12625,12626,12627,12628,12629,12630,12631,
12632,12633,12634,12635,12636,12637,12638,12639,12640,12641,12642,12643,
12644,12645,12646,12647,12648,12649,12650,12651,12652,12653,12654,12655,
12656,12657,12658,12659,12660,12661,12662,12663,12664,12665,12666,12667,
12668,12669,12670,12671,12672,12673,12674,12675,12676,12677,12678,12679,
12680,12681,12682,12683,12684,12685,12686,12687,12688,12689,12690,12691,
12692,12693,12694,12695,12696,12697,12698,12699,12700,12701,12702,12703,
12704,12705,12706,12707,12708,12709,12710,12711,12712,12713,12714,12715,
12716,12717,12718,12719,12720,12721,12722,12723,12724,12725,12726,12727,
12728,12729,12730,12731,12732,12733,12734,12735,12736,12737,12738,12739,
12740,12741,12742,12743,12744,12745,12746,12747,12748,12749,12750,12751,
12752,12753,12754,12755,12756,12757,12758,12759,12760,12761,12762,12763,
12764,12765,12766,12767,12768,12769,12770,12771,12772,12773,12774,12775,
12776,12777,12778,12779,12780,12781,12782,12783,12784,12785,12786,12787,
12788,12789,12790,12791,12792,12793,12794,12795,12796,12797,12798,12799,
12800,12801,12802,12803,12804,12805,12806,12807,12808,12809,12810,12811,
12812,12813,12814,12815,12816,12817,12818,12819,12820,12821,12822,12823,
12824,12825,12826,12827,12828,12829,12830,12831,12832,12833,12834,12835,
12836,12837,12838,12839,12840,12841,12842,12843,12844,12845,12846,12847,
12848,12849,12850,12851,12852,12853,12854,12855,12856,12857,12858,12859,
12860,12861,12862,12863,12864,12865,12866,12867,12868,12869,12870,12871,
12872,12873,12874,12875,12876,12877,12878,12879,12880,12881,12882,12883,
12884,12885,12886,12887,12888,12889,12890,12891,12892,12893,12894,12895,
12896,12897,12898,12899,12900,12901,12902,12903,12904,12905,12906,12907,
12908,12909,12910,12911,12912,12913,12914,12915,12916,12917,12918,12919,
12920,12921,12922,12923,12924,12925,12926,12927,12928,12929,12930,12931,
12932,12933,12934,12935,12936,12937,12938,12939,12940,12941,12942,12943,
12944,12945,12946,12947,12948,12949,12950,12951,12952,12953,12954,12955,
12956,12957,12958,12959,12960,12961,12962,12963,12964,12965,12966,12967,
12968,12969,12970,12971,12972,12973,12974,12975,12976,12977,12978,12979,
12980,12981,12982,12983,12984,12985,12986,12987,12988,12989,12990,12991,
12992,12993,12994,12995,12996,12997,12998,12999,13000,13001,13002,13003,
13004,13005,13006,13007,13008,13009,13010,13011,13012,13013,13014,13015,
13016,13017,13018,13019,13020,13021,13022,13023,13024,13025,13026,13027,
13028,13029,13030,13031,13032,13033,13034,13035,13036,13037,13038,13039,
13040,13041,13042,13043,13044,13045,13046,13047,13048,13049,13050,13051,
13052,13053,13054,13055,13056,13057,13058,13059,13060,13061,13062,13063,
13064,13065,13066,13067,13068,13069,13070,13071,13072,13073,13074,13075,
13076,13077,13078,13079,13080,13081,13082,13083,13084,13085,13086,13087,
13088,13089,13090,13091,13092,13093,13094,13095,13096,13097,13098,13099,
13100,13101,13102,13103,13104,13105,13106,13107,13108,13109,13110,13111,
13112,13113,13114,13115,13116,13117,13118,13119,13120,13121,13122,13123,
13124,13125,13126,13127,13128,13129,13130,13131,13132,13133,13134,13135,
13136,13137,13138,13139,13140,13141,13142,13143,13144,13145,13146,13147,
13148,13149,13150,13151,13152,13153,13154,13155,13156,13157,13158,13159,
13160,13161,13162,13163,13164,13165,13166,13167,13168,13169,13170,13171,
13172,13173,13174,13175,13176,13177,13178,13179,13180,13181,13182,13183,
13184,13185,13186,13187,13188,13189,13190,13191,13192,13193,13194,13195,
13196,13197,13198,13199,13200,13201,13202,13203,13204,13205,13206,13207,
13208,13209,13210,13211,13212,13213,13214,13215,13216,13217,13218,13219,
13220,13221,13222,13223,13224,13225,13226,13227,13228,13229,13230,13231,
13232,13233,13234,13235,13236,13237,13238,13239,13240,13241,13242,13243,
13244,13245,13246,13247,13248,13249,13250,13251,13252,13253,13254,13255,
13256,13257,13258,13259,13260,13261,13262,13263,13264,13265,13266,13267,
13268,13269,13270,13271,13272,13273,13274,13275,13276,13277,13278,13279,
13280,13281,13282,13283,13284,13285,13286,13287,13288,13289,13290,13291,
13292,13293,13294,13295,13296,13297,13298,13299,13300,13301,13302,13303,
13304,13305,13306,13307,13308,13309,13310,13311,13312,13313,13314,13315,
13316,13317,13318,13319,13320,13321,13322,13323,13324,13325,13326,13327,
13328,13329,13330,13331,13332,13333,13334,13335,13336,13337,13338,13339,
13340,13341,13342,13343,13344,13345,13346,13347,13348,13349,13350,13351,
13352,13353,13354,13355,13356,13357,13358,13359,13360,13361,13362,13363,
13364,13365,13366,13367,13368,13369,13370,13371,13372,13373,13374,13375,
13376,13377,13378,13379,13380,13381,13382,13383,13384,13385,13386,13387,
13388,13389,13390,13391,13392,13393,13394,13395,13396,13397,13398,13399,
13400,13401,13402,13403,13404,13405,13406,13407,13408,13409,13410,13411,
13412,13413,13414,13415,13416,13417,13418,13419,13420,13421,13422,13423,
13424,13425,13426,13427,13428,13429,13430,13431,13432,13433,13434,13435,
13436,13437,13438,13439,13440,13441,13442,13443,13444,13445,13446,13447,
13448,13449,13450,13451,13452,13453,13454,13455,13456,13457,13458,13459,
13460,13461,13462,13463,13464,13465,13466,13467,13468,13469,13470,13471,
13472,13473,13474,13475,13476,13477,13478,13479,13480,13481,13482,13483,
13484,13485,13486,13487,13488,13489,13490,13491,13492,13493,13494,13495,
13496,13497,13498,13499,13500,13501,13502,13503,13504,13505,13506,13507,
13508,13509,13510,13511,13512,13513,13514,13515,13516,13517,13518,13519,
13520,13521,13522,13523,13524,13525,13526,13527,13528,13529,13530,13531,
13532,13533,13534,13535,13536,13537,13538,13539,13540,13541,13542,13543,
13544,13545,13546,13547,13548,13549,13550,13551,13552,13553,13554,13555,
13556,13557,13558,13559,13560,13561,13562,13563,13564,13565,13566,13567,
13568,13569,13570,13571,13572,13573,13574,13575,13576,13577,13578,13579,
13580,13581,13582,13583,13584,13585,13586,13587,13588,13589,13590,13591,
13592,13593,13594,13595,13596,13597,13598,13599,13600,13601,13602,13603,
13604,13605,13606,13607,13608,13609,13610,13611,13612,13613,13614,13615,
13616,13617,13618,13619,13620,13621,13622,13623,13624,13625,13626,13627,
13628,13629,13630,13631,13632,13633,13634,13635,13636,13637,13638,13639,
13640,13641,13642,13643,13644,13645,13646,13647,13648,13649,13650,13651,
13652,13653,13654,13655,13656,13657,13658,13659,13660,13661,13662,13663,
13664,13665,13666,13667,13668,13669,13670,13671,13672,13673,13674,13675,
13676,13677,13678,13679,13680,13681,13682,13683,13684,13685,13686,13687,
13688,13689,13690,13691,13692,13693,13694,13695,13696,13697,13698,13699,
13700,13701,13702,13703,13704,13705,13706,13707,13708,13709,13710,13711,
13712,13713,13714,13715,13716,13717,13718,13719,13720,13721,13722,13723,
13724,13725,13726,13727,13728,13729,13730,13731,13732,13733,13734,13735,
13736,13737,13738,13739,13740,13741,13742,13743,13744,13745,13746,13747,
13748,13749,13750,13751,13752,13753,13754,13755,13756,13757,13758,13759,
13760,13761,13762,13763,13764,13765,13766,13767,13768,13769,13770,13771,
13772,13773,13774,13775,13776,13777,13778,13779,13780,13781,13782,13783,
13784,13785,13786,13787,13788,13789,13790,13791,13792,13793,13794,13795,
13796,13797,13798,13799,13800,13801,13802,13803,13804,13805,13806,13807,
13808,13809,13810,13811,13812,13813,13814,13815,13816,13817,13818,13819,
13820,13821,13822,13823,13824,13825,13826,13827,13828,13829,13830,13831,
13832,13833,13834,13835,13836,13837,13838,13839,13840,13841,13842,13843,
13844,13845,13846,13847,13848,13849,13850,13851,13852,13853,13854,13855,
13856,13857,13858,13859,13860,13861,13862,13863,13864,13865,13866,13867,
13868,13869,13870,13871,13872,13873,13874,13875,13876,13877,13878,13879,
13880,13881,13882,13883,13884,13885,13886,13887,13888,13889,13890,13891,
13892,13893,13894,13895,13896,13897,13898,13899,13900,13901,13902,13903,
13904,13905,13906,13907,13908,13909,13910,13911,13912,13913,13914,13915,
13916,13917,13918,13919,13920,13921,13922,13923,13924,13925,13926,13927,
13928,13929,13930,13931,13932,13933,13934,13935,13936,13937,13938,13939,
13940,13941,13942,13943,13944,13945,13946,13947,13948,13949,13950,13951,
13952,13953,13954,13955,13956,13957,13958,13959,13960,13961,13962,13963,
13964,13965,13966,13967,13968,13969,13970,13971,13972,13973,13974,13975,
13976,13977,13978,13979,13980,13981,13982,13983,13984,13985,13986,13987,
13988,13989,13990,13991,13992,13993,13994,13995,13996,13997,13998,13999,
14000,14001,14002,14003,14004,14005,14006,14007,14008,14009,14010,14011,
14012,14013,14014,14015,14016,14017,14018,14019,14020,14021,14022,14023,
14024,14025,14026,14027,14028,14029,14030,14031,14032,14033,14034,14035,
14036,14037,14038,14039,14040,14041,14042,14043,14044,14045,14046,14047,
14048,14049,14050,14051,14052,14053,14054,14055,14056,14057,14058,14059,
14060,14061,14062,14063,14064,14065,14066,14067,14068,14069,14070,14071,
14072,14073,14074,14075,14076,14077,14078,14079,14080,14081,14082,14083,
14084,14085,14086,14087,14088,14089,14090,14091,14092,14093,14094,14095,
14096,14097,14098,14099,14100,14101,14102,14103,14104,14105,14106,14107,
14108,14109,14110,14111,14112,14113,14114,14115,14116,14117,14118,14119,
14120,14121,14122,14123,14124,14125,14126,14127,14128,14129,14130,14131,
14132,14133,14134,14135,14136,14137,14138,14139,14140,14141,14142,14143,
14144,14145,14146,14147,14148,14149,14150,14151,14152,14153,14154,14155,
14156,14157,14158,14159,14160,14161,14162,14163,14164,14165,14166,14167,
14168,14169,14170,14171,14172,14173,14174,14175,14176,14177,14178,14179,
14180,14181,14182,14183,14184,14185,14186,14187,14188,14189,14190,14191,
14192,14193,14194,14195,14196,14197,14198,14199,14200,14201,14202,14203,
14204,14205,14206,14207,14208,14209,14210,14211,14212,14213,14214,14215,
14216,14217,14218,14219,14220,14221,14222,14223,14224,14225,14226,14227,
14228,14229,14230,14231,14232,14233,14234,14235,14236,14237,14238,14239,
14240,14241,14242,14243,14244,14245,14246,14247,14248,14249,14250,14251,
14252,14253,14254,14255,14256,14257,14258,14259,14260,14261,14262,14263,
14264,14265,14266,14267,14268,14269,14270,14271,14272,14273,14274,14275,
14276,14277,14278,14279,14280,14281,14282,14283,14284,14285,14286,14287,
14288,14289,14290,14291,14292,14293,14294,14295,14296,14297,14298,14299,
14300,14301,14302,14303,14304,14305,14306,14307,14308,14309,14310,14311,
14312,14313,14314,14315,14316,14317,14318,14319,14320,14321,14322,14323,
14324,14325,14326,14327,14328,14329,14330,14331,14332,14333,14334,14335,
14336,14337,14338,14339,14340,14341,14342,14343,14344,14345,14346,14347,
14348,14349,14350,14351,14352,14353,14354,14355,14356,14357,14358,14359,
14360,14361,14362,14363,14364,14365,14366,14367,14368,14369,14370,14371,
14372,14373,14374,14375,14376,14377,14378,14379,14380,14381,14382,14383,
14384,14385,14386,14387,14388,14389,14390,14391,14392,14393,14394,14395,
14396,14397,14398,14399,14400,14401,14402,14403,14404,14405,14406,14407,
14408,14409,14410,14411,14412,14413,14414,14415,14416,14417,14418,14419,
14420,14421,14422,14423,14424,14425,14426,14427,14428,14429,14430,14431,
14432,14433,14434,14435,14436,14437,14438,14439,14440,14441,14442,14443,
14444,14445,14446,14447,14448,14449,14450,14451,14452,14453,14454,14455,
14456,14457,14458,14459,14460,14461,14462,14463,14464,14465,14466,14467,
14468,14469,14470,14471,14472,14473,14474,14475,14476,14477,14478,14479,
14480,14481,14482,14483,14484,14485,14486,14487,14488,14489,14490,14491,
14492,14493,14494,14495,14496,14497,14498,14499,14500,14501,14502,14503,
14504,14505,14506,14507,14508,14509,14510,14511,14512,14513,14514,14515,
14516,14517,14518,14519,14520,14521,14522,14523,14524,14525,14526,14527,
14528,14529,14530,14531,14532,14533,14534,14535,14536,14537,14538,14539,
14540,14541,14542,14543,14544,14545,14546,14547,14548,14549,14550,14551,
14552,14553,14554,14555,14556,14557,14558,14559,14560,14561,14562,14563,
14564,14565,14566,14567,14568,14569,14570,14571,14572,14573,14574,14575,
14576,14577,14578,14579,14580,14581,14582,14583,14584,14585,14586,14587,
14588,14589,14590,14591,14592,14593,14594,14595,14596,14597,14598,14599,
14600,14601,14602,14603,14604,14605,14606,14607,14608,14609,14610,14611,
14612,14613,14614,14615,14616,14617,14618,14619,14620,14621,14622,14623,
14624,14625,14626,14627,14628,14629,14630,14631,14632,14633,14634,14635,
14636,14637,14638,14639,14640,14641,14642,14643,14644,14645,14646,14647,
14648,14649,14650,14651,14652,14653,14654,14655,14656,14657,14658,14659,
14660,14661,14662,14663,14664,14665,14666,14667,14668,14669,14670,14671,
14672,14673,14674,14675,14676,14677,14678,14679,14680,14681,14682,14683,
14684,14685,14686,14687,14688,14689,14690,14691,14692,14693,14694,14695,
14696,14697,14698,14699,14700,14701,14702,14703,14704,14705,14706,14707,
14708,14709,14710,14711,14712,14713,14714,14715,14716,14717,14718,14719,
14720,14721,14722,14723,14724,14725,14726,14727,14728,14729,14730,14731,
14732,14733,14734,14735,14736,14737,14738,14739,14740,14741,14742,14743,
14744,14745,14746,14747,14748,14749,14750,14751,14752,14753,14754,14755,
14756,14757,14758,14759,14760,14761,14762,14763,14764,14765,14766,14767,
14768,14769,14770,14771,14772,14773,14774,14775,14776,14777,14778,14779,
14780,14781,14782,14783,14784,14785,14786,14787,14788,14789,14790,14791,
14792,14793,14794,14795,14796,14797,14798,14799,14800,14801,14802,14803,
14804,14805,14806,14807,14808,14809,14810,14811,14812,14813,14814,14815,
14816,14817,14818,14819,14820,14821,14822,14823,14824,14825,14826,14827,
14828,14829,14830,14831,14832,14833,14834,14835,14836,14837,14838,14839,
14840,14841,14842,14843,14844,14845,14846,14847,14848,14849,14850,14851,
14852,14853,14854,14855,14856,14857,14858,14859,14860,14861,14862,14863,
14864,14865,14866,14867,14868,14869,14870,14871,14872,14873,14874,14875,
14876,14877,14878,14879,14880,14881,14882,14883,14884,14885,14886,14887,
14888,14889,14890,14891,14892,14893,14894,14895,14896,14897,14898,14899,
14900,14901,14902,14903,14904,14905,14906,14907,14908,14909,14910,14911,
14912,14913,14914,14915,14916,14917,14918,14919,14920,14921,14922,14923,
14924,14925,14926,14927,14928,14929,14930,14931,14932,14933,14934,14935,
14936,14937,14938,14939,14940,14941,14942,14943,14944,14945,14946,14947,
14948,14949,14950,14951,14952,14953,14954,14955,14956,14957,14958,14959,
14960,14961,14962,14963,14964,14965,14966,14967,14968,14969,14970,14971,
14972,14973,14974,14975,14976,14977,14978,14979,14980,14981,14982,14983,
14984,14985,14986,14987,14988,14989,14990,14991,14992,14993,14994,14995,
14996,14997,14998,14999,15000,15001,15002,15003,15004,15005,15006,15007,
15008,15009,15010,15011,15012,15013,15014,15015,15016,15017,15018,15019,
15020,15021,15022,15023,15024,15025,15026,15027,15028,15029,15030,15031,
15032,15033,15034,15035,15036,15037,15038,15039,15040,15041,15042,15043,
15044,15045,15046,15047,15048,15049,15050,15051,15052,15053,15054,15055,
15056,15057,15058,15059,15060,15061,15062,15063,15064,15065,15066,15067,
15068,15069,15070,15071,15072,15073,15074,15075,15076,15077,15078,15079,
15080,15081,15082,15083,15084,15085,15086,15087,15088,15089,15090,15091,
15092,15093,15094,15095,15096,15097,15098,15099,15100,15101,15102,15103,
15104,15105,15106,15107,15108,15109,15110,15111,15112,15113,15114,15115,
15116,15117,15118,15119,15120,15121,15122,15123,15124,15125,15126,15127,
15128,15129,15130,15131,15132,15133,15134,15135,15136,15137,15138,15139,
15140,15141,15142,15143,15144,15145,15146,15147,15148,15149,15150,15151,
15152,15153,15154,15155,15156,15157,15158,15159,15160,15161,15162,15163,
15164,15165,15166,15167,15168,15169,15170,15171,15172,15173,15174,15175,
15176,15177,15178,15179,15180,15181,15182,15183,15184,15185,15186,15187,
15188,15189,15190,15191,15192,15193,15194,15195,15196,15197,15198,15199,
15200,15201,15202,15203,15204,15205,15206,15207,15208,15209,15210,15211,
15212,15213,15214,15215,15216,15217,15218,15219,15220,15221,15222,15223,
15224,15225,15226,15227,15228,15229,15230,15231,15232,15233,15234,15235,
15236,15237,15238,15239,15240,15241,15242,15243,15244,15245,15246,15247,
15248,15249,15250,15251,15252,15253,15254,15255,15256,15257,15258,15259,
15260,15261,15262,15263,15264,15265,15266,15267,15268,15269,15270,15271,
15272,15273,15274,15275,15276,15277,15278,15279,15280,15281,15282,15283,
15284,15285,15286,15287,15288,15289,15290,15291,15292,15293,15294,15295,
15296,15297,15298,15299,15300,15301,15302,15303,15304,15305,15306,15307,
15308,15309,15310,15311,15312,15313,15314,15315,15316,15317,15318,15319,
15320,15321,15322,15323,15324,15325,15326,15327,15328,15329,15330,15331,
15332,15333,15334,15335,15336,15337,15338,15339,15340,15341,15342,15343,
15344,15345,15346,15347,15348,15349,15350,15351,15352,15353,15354,15355,
15356,15357,15358,15359,15360,15361,15362,15363,15364,15365,15366,15367,
15368,15369,15370,15371,15372,15373,15374,15375,15376,15377,15378,15379,
15380,15381,15382,15383,15384,15385,15386,15387,15388,15389,15390,15391,
15392,15393,15394,15395,15396,15397,15398,15399,15400,15401,15402,15403,
15404,15405,15406,15407,15408,15409,15410,15411,15412,15413,15414,15415,
15416,15417,15418,15419,15420,15421,15422,15423,15424,15425,15426,15427,
15428,15429,15430,15431,15432,15433,15434,15435,15436,15437,15438,15439,
15440,15441,15442,15443,15444,15445,15446,15447,15448,15449,15450,15451,
15452,15453,15454,15455,15456,15457,15458,15459,15460,15461,15462,15463,
15464,15465,15466,15467,15468,15469,15470,15471,15472,15473,15474,15475,
15476,15477,15478,15479,15480,15481,15482,15483,15484,15485,15486,15487,
15488,15489,15490,15491,15492,15493,15494,15495,15496,15497,15498,15499,
15500,15501,15502,15503,15504,15505,15506,15507,15508,15509,15510,15511,
15512,15513,15514,15515,15516,15517,15518,15519,15520,15521,15522,15523,
15524,15525,15526,15527,15528,15529,15530,15531,15532,15533,15534,15535,
15536,15537,15538,15539,15540,15541,15542,15543,15544,15545,15546,15547,
15548,15549,15550,15551,15552,15553,15554,15555,15556,15557,15558,15559,
15560,15561,15562,15563,15564,15565,15566,15567,15568,15569,15570,15571,
15572,15573,15574,15575,15576,15577,15578,15579,15580,15581,15582,15583,
15584,15585,15586,15587,15588,15589,15590,15591,15592,15593,15594,15595,
15596,15597,15598,15599,15600,15601,15602,15603,15604,15605,15606,15607,
15608,15609,15610,15611,15612,15613,15614,15615,15616,15617,15618,15619,
15620,15621,15622,15623,15624,15625,15626,15627,15628,15629,15630,15631,
15632,15633,15634,15635,15636,15637,15638,15639,15640,15641,15642,15643,
15644,15645,15646,15647,15648,15649,15650,15651,15652,15653,15654,15655,
15656,15657,15658,15659,15660,15661,15662,15663,15664,15665,15666,15667,
15668,15669,15670,15671,15672,15673,15674,15675,15676,15677,15678,15679,
15680,15681,15682,15683,15684,15685,15686,15687,15688,15689,15690,15691,
15692,15693,15694,15695,15696,15697,15698,15699,15700,15701,15702,15703,
15704,15705,15706,15707,15708,15709,15710,15711,15712,15713,15714,15715,
15716,15717,15718,15719,15720,15721,15722,15723,15724,15725,15726,15727,
15728,15729,15730,15731,15732,15733,15734,15735,15736,15737,15738,15739,
15740,15741,15742,15743,15744,15745,15746,15747,15748,15749,15750,15751,
15752,15753,15754,15755,15756,15757,15758,15759,15760,15761,15762,15763,
15764,15765,15766,15767,15768,15769,15770,15771,15772,15773,15774,15775,
15776,15777,15778,15779,15780,15781,15782,15783,15784,15785,15786,15787,
15788,15789,15790,15791,15792,15793,15794,15795,15796,15797,15798,15799,
15800,15801,15802,15803,15804,15805,15806,15807,15808,15809,15810,15811,
15812,15813,15814,15815,15816,15817,15818,15819,15820,15821,15822,15823,
15824,15825,15826,15827,15828,15829,15830,15831,15832,15833,15834,15835,
15836,15837,15838,15839,15840,15841,15842,15843,15844,15845,15846,15847,
15848,15849,15850,15851,15852,15853,15854,15855,15856,15857,15858,15859,
15860,15861,15862,15863,15864,15865,15866,15867,15868,15869,15870,15871,
15872,15873,15874,15875,15876,15877,15878,15879,15880,15881,15882,15883,
15884,15885,15886,15887,15888,15889,15890,15891,15892,15893,15894,15895,
15896,15897,15898,15899,15900,15901,15902,15903,15904,15905,15906,15907,
15908,15909,15910,15911,15912,15913,15914,15915,15916,15917,15918,15919,
15920,15921,15922,15923,15924,15925,15926,15927,15928,15929,15930,15931,
15932,15933,15934,15935,15936,15937,15938,15939,15940,15941,15942,15943,
15944,15945,15946,15947,15948,15949,15950,15951,15952,15953,15954,15955,
15956,15957,15958,15959,15960,15961,15962,15963,15964,15965,15966,15967,
15968,15969,15970,15971,15972,15973,15974,15975,15976,15977,15978,15979,
15980,15981,15982,15983,15984,15985,15986,15987,15988,15989,15990,15991,
15992,15993,15994,15995,15996,15997,15998,15999,16000,16001,16002,16003,
16004,16005,16006,16007,16008,16009,16010,16011,16012,16013,16014,16015,
16016,16017,16018,16019,16020,16021,16022,16023,16024,16025,16026,16027,
16028,16029,16030,16031,16032,16033,16034,16035,16036,16037,16038,16039,
16040,16041,16042,16043,16044,16045,16046,16047,16048,16049,16050,16051,
16052,16053,16054,16055,16056,16057,16058,16059,16060,16061,16062,16063,
16064,16065,16066,16067,16068,16069,16070,16071,16072,16073,16074,16075,
16076,16077,16078,16079,16080,16081,16082,16083,16084,16085,16086,16087,
16088,16089,16090,16091,16092,16093,16094,16095,16096,16097,16098,16099,
16100,16101,16102,16103,16104,16105,16106,16107,16108,16109,16110,16111,
16112,16113,16114,16115,16116,16117,16118,16119,16120,16121,16122,16123,
16124,16125,16126,16127,16128,16129,16130,16131,16132,16133,16134,16135,
16136,16137,16138,16139,16140,16141,16142,16143,16144,16145,16146,16147,
16148,16149,16150,16151,16152,16153,16154,16155,16156,16157,16158,16159,
16160,16161,16162,16163,16164,16165,16166,16167,16168,16169,16170,16171,
16172,16173,16174,16175,16176,16177,16178,16179,16180,16181,16182,16183,
16184,16185,16186,16187,16188,16189,16190,16191,16192,16193,16194,16195,
16196,16197,16198,16199,16200,16201,16202,16203,16204,16205,16206,16207,
16208,16209,16210,16211,16212,16213,16214,16215,16216,16217,16218,16219,
16220,16221,16222,16223,16224,16225,16226,16227,16228,16229,16230,16231,
16232,16233,16234,16235,16236,16237,16238,16239,16240,16241,16242,16243,
16244,16245,16246,16247,16248,16249,16250,16251,16252,16253,16254,16255,
16256,16257,16258,16259,16260,16261,16262,16263,16264,16265,16266,16267,
16268,16269,16270,16271,16272,16273,16274,16275,16276,16277,16278,16279,
16280,16281,16282,16283,16284,16285,16286,16287,16288,16289,16290,16291,
16292,16293,16294,16295,16296,16297,16298,16299,16300,16301,16302,16303,
16304,16305,16306,16307,16308,16309,16310,16311,16312,16313,16314,16315,
16316,16317,16318,16319,16320,16321,16322,16323,16324,16325,16326,16327,
16328,16329,16330,16331,16332,16333,16334,16335,16336,16337,16338,16339,
16340,16341,16342,16343,16344,16345,16346,16347,16348,16349,16350,16351,
16352,16353,16354,16355,16356,16357,16358,16359,16360,16361,16362,16363,
16364,16365,16366,16367,16368,16369,16370,16371,16372,16373,16374,16375,
16376,16377,16378,16379,16380,16381,16382,16383,16384,16385,16386,16387,
16388,16389,16390,16391,16392,16393,16394,16395,16396,16397,16398,16399,
16400,16401,16402,16403,16404,16405,16406,16407,16408,16409,16410,16411,
16412,16413,16414,16415,16416,16417,16418,16419,16420,16421,16422,16423,
16424,16425,16426,16427,16428,16429,16430,16431,16432,16433,16434,16435,
16436,16437,16438,16439,16440,16441,16442,16443,16444,16445,16446,16447,
16448,16449,16450,16451,16452,16453,16454,16455,16456,16457,16458,16459,
16460,16461,16462,16463,16464,16465,16466,16467,16468,16469,16470,16471,
16472,16473,16474,16475,16476,16477,16478,16479,16480,16481,16482,16483,
16484,16485,16486,16487,16488,16489,16490,16491,16492,16493,16494,16495,
16496,16497,16498,16499,16500,16501,16502,16503,16504,16505,16506,16507,
16508,16509,16510,16511,16512,16513,16514,16515,16516,16517,16518,16519,
16520,16521,16522,16523,16524,16525,16526,16527,16528,16529,16530,16531,
16532,16533,16534,16535,16536,16537,16538,16539,16540,16541,16542,16543,
16544,16545,16546,16547,16548,16549,16550,16551,16552,16553,16554,16555,
16556,16557,16558,16559,16560,16561,16562,16563,16564,16565,16566,16567,
16568,16569,16570,16571,16572,16573,16574,16575,16576,16577,16578,16579,
16580,16581,16582,16583,16584,16585,16586,16587,16588,16589,16590,16591,
16592,16593,16594,16595,16596,16597,16598,16599,16600,16601,16602,16603,
16604,16605,16606,16607,16608,16609,16610,16611,16612,16613,16614,16615,
16616,16617,16618,16619,16620,16621,16622,16623,16624,16625,16626,16627,
16628,16629,16630,16631,16632,16633,16634,16635,16636,16637,16638,16639,
16640,16641,16642,16643,16644,16645,16646,16647,16648,16649,16650,16651,
16652,16653,16654,16655,16656,16657,16658,16659,16660,16661,16662,16663,
16664,16665,16666,16667,16668,16669,16670,16671,16672,16673,16674,16675,
16676,16677,16678,16679,16680,16681,16682,16683,16684,16685,16686,16687,
16688,16689,16690,16691,16692,16693,16694,16695,16696,16697,16698,16699,
16700,16701,16702,16703,16704,16705,16706,16707,16708,16709,16710,16711,
16712,16713,16714,16715,16716,16717,16718,16719,16720,16721,16722,16723,
16724,16725,16726,16727,16728,16729,16730,16731,16732,16733,16734,16735,
16736,16737,16738,16739,16740,16741,16742,16743,16744,16745,16746,16747,
16748,16749,16750,16751,16752,16753,16754,16755,16756,16757,16758,16759,
16760,16761,16762,16763,16764,16765,16766,16767,16768,16769,16770,16771,
16772,16773,16774,16775,16776,16777,16778,16779,16780,16781,16782,16783,
16784,16785,16786,16787,16788,16789,16790,16791,16792,16793,16794,16795,
16796,16797,16798,16799,16800,16801,16802,16803,16804,16805,16806,16807,
16808,16809,16810,16811,16812,16813,16814,16815,16816,16817,16818,16819,
16820,16821,16822,16823,16824,16825,16826,16827,16828,16829,16830,16831,
16832,16833,16834,16835,16836,16837,16838,16839,16840,16841,16842,16843,
16844,16845,16846,16847,16848,16849,16850,16851,16852,16853,16854,16855,
16856,16857,16858,16859,16860,16861,16862,16863,16864,16865,16866,16867,
16868,16869,16870,16871,16872,16873,16874,16875,16876,16877,16878,16879,
16880,16881,16882,16883,16884,16885,16886,16887,16888,16889,16890,16891,
16892,16893,16894,16895,16896,16897,16898,16899,16900,16901,16902,16903,
16904,16905,16906,16907,16908,16909,16910,16911,16912,16913,16914,16915,
16916,16917,16918,16919,16920,16921,16922,16923,16924,16925,16926,16927,
16928,16929,16930,16931,16932,16933,16934,16935,16936,16937,16938,16939,
16940,16941,16942,16943,16944,16945,16946,16947,16948,16949,16950,16951,
16952,16953,16954,16955,16956,16957,16958,16959,16960,16961,16962,16963,
16964,16965,16966,16967,16968,16969,16970,16971,16972,16973,16974,16975,
16976,16977,16978,16979,16980,16981,16982,16983,16984,16985,16986,16987,
16988,16989,16990,16991,16992,16993,16994,16995,16996,16997,16998,16999,
17000,17001,17002,17003,17004,17005,17006,17007,17008,17009,17010,17011,
17012,17013,17014,17015,17016,17017,17018,17019,17020,17021,17022,17023,
17024,17025,17026,17027,17028,17029,17030,17031,17032,17033,17034,17035,
17036,17037,17038,17039,17040,17041,17042,17043,17044,17045,17046,17047,
17048,17049,17050,17051,17052,17053,17054,17055,17056,17057,17058,17059,
17060,17061,17062,17063,17064,17065,17066,17067,17068,17069,17070,17071,
17072,17073,17074,17075,17076,17077,17078,17079,17080,17081,17082,17083,
17084,17085,17086,17087,17088,17089,17090,17091,17092,17093,17094,17095,
17096,17097,17098,17099,17100,17101,17102,17103,17104,17105,17106,17107,
17108,17109,17110,17111,17112,17113,17114,17115,17116,17117,17118,17119,
17120,17121,17122,17123,17124,17125,17126,17127,17128,17129,17130,17131,
17132,17133,17134,17135,17136,17137,17138,17139,17140,17141,17142,17143,
17144,17145,17146,17147,17148,17149,17150,17151,17152,17153,17154,17155,
17156,17157,17158,17159,17160,17161,17162,17163,17164,17165,17166,17167,
17168,17169,17170,17171,17172,17173,17174,17175,17176,17177,17178,17179,
17180,17181,17182,17183,17184,17185,17186,17187,17188,17189,17190,17191,
17192,17193,17194,17195,17196,17197,17198,17199,17200,17201,17202,17203,
17204,17205,17206,17207,17208,17209,17210,17211,17212,17213,17214,17215,
17216,17217,17218,17219,17220,17221,17222,17223,17224,17225,17226,17227,
17228,17229,17230,17231,17232,17233,17234,17235,17236,17237,17238,17239,
17240,17241,17242,17243,17244,17245,17246,17247,17248,17249,17250,17251,
17252,17253,17254,17255,17256,17257,17258,17259,17260,17261,17262,17263,
17264,17265,17266,17267,17268,17269,17270,17271,17272,17273,17274,17275,
17276,17277,17278,17279,17280,17281,17282,17283,17284,17285,17286,17287,
17288,17289,17290,17291,17292,17293,17294,17295,17296,17297,17298,17299,
17300,17301,17302,17303,17304,17305,17306,17307,17308,17309,17310,17311,
17312,17313,17314,17315,17316,17317,17318,17319,17320,17321,17322,17323,
17324,17325,17326,17327,17328,17329,17330,17331,17332,17333,17334,17335,
17336,17337,17338,17339,17340,17341,17342,17343,17344,17345,17346,17347,
17348,17349,17350,17351,17352,17353,17354,17355,17356,17357,17358,17359,
17360,17361,17362,17363,17364,17365,17366,17367,17368,17369,17370,17371,
17372,17373,17374,17375,17376,17377,17378,17379,17380,17381,17382,17383,
17384,17385,17386,17387,17388,17389,17390,17391,17392,17393,17394,17395,
17396,17397,17398,17399,17400,17401,17402,17403,17404,17405,17406,17407,
17408,17409,17410,17411,17412,17413,17414,17415,17416,17417,17418,17419,
17420,17421,17422,17423,17424,17425,17426,17427,17428,17429,17430,17431,
17432,17433,17434,17435,17436,17437,17438,17439,17440,17441,17442,17443,
17444,17445,17446,17447,17448,17449,17450,17451,17452,17453,17454,17455,
17456,17457,17458,17459,17460,17461,17462,17463,17464,17465,17466,17467,
17468,17469,17470,17471,17472,17473,17474,17475,17476,17477,17478,17479,
17480,17481,17482,17483,17484,17485,17486,17487,17488,17489,17490,17491,
17492,17493,17494,17495,17496,17497,17498,17499,17500,17501,17502,17503,
17504,17505,17506,17507,17508,17509,17510,17511,17512,17513,17514,17515,
17516,17517,17518,17519,17520,17521,17522,17523,17524,17525,17526,17527,
17528,17529,17530,17531,17532,17533,17534,17535,17536,17537,17538,17539,
17540,17541,17542,17543,17544,17545,17546,17547,17548,17549,17550,17551,
17552,17553,17554,17555,17556,17557,17558,17559,17560,17561,17562,17563,
17564,17565,17566,17567,17568,17569,17570,17571,17572,17573,17574,17575,
17576,17577,17578,17579,17580,17581,17582,17583,17584,17585,17586,17587,
17588,17589,17590,17591,17592,17593,17594,17595,17596,17597,17598,17599,
17600,17601,17602,17603,17604,17605,17606,17607,17608,17609,17610,17611,
17612,17613,17614,17615,17616,17617,17618,17619,17620,17621,17622,17623,
17624,17625,17626,17627,17628,17629,17630,17631,17632,17633,17634,17635,
17636,17637,17638,17639,17640,17641,17642,17643,17644,17645,17646,17647,
17648,17649,17650,17651,17652,17653,17654,17655,17656,17657,17658,17659,
17660,17661,17662,17663,17664,17665,17666,17667,17668,17669,17670,17671,
17672,17673,17674,17675,17676,17677,17678,17679,17680,17681,17682,17683,
17684,17685,17686,17687,17688,17689,17690,17691,17692,17693,17694,17695,
17696,17697,17698,17699,17700,17701,17702,17703,17704,17705,17706,17707,
17708,17709,17710,17711,17712,17713,17714,17715,17716,17717,17718,17719,
17720,17721,17722,17723,17724,17725,17726,17727,17728,17729,17730,17731,
17732,17733,17734,17735,17736,17737,17738,17739,17740,17741,17742,17743,
17744,17745,17746,17747,17748,17749,17750,17751,17752,17753,17754,17755,
17756,17757,17758,17759,17760,17761,17762,17763,17764,17765,17766,17767,
17768,17769,17770,17771,17772,17773,17774,17775,17776,17777,17778,17779,
17780,17781,17782,17783,17784,17785,17786,17787,17788,17789,17790,17791,
17792,17793,17794,17795,17796,17797,17798,17799,17800,17801,17802,17803,
17804,17805,17806,17807,17808,17809,17810,17811,17812,17813,17814,17815,
17816,17817,17818,17819,17820,17821,17822,17823,17824,17825,17826,17827,
17828,17829,17830,17831,17832,17833,17834,17835,17836,17837,17838,17839,
17840,17841,17842,17843,17844,17845,17846,17847,17848,17849,17850,17851,
17852,17853,17854,17855,17856,17857,17858,17859,17860,17861,17862,17863,
17864,17865,17866,17867,17868,17869,17870,17871,17872,17873,17874,17875,
17876,17877,17878,17879,17880,17881,17882,17883,17884,17885,17886,17887,
17888,17889,17890,17891,17892,17893,17894,17895,17896,17897,17898,17899,
17900,17901,17902,17903,17904,17905,17906,17907,17908,17909,17910,17911,
17912,17913,17914,17915,17916,17917,17918,17919,17920,17921,17922,17923,
17924,17925,17926,17927,17928,17929,17930,17931,17932,17933,17934,17935,
17936,17937,17938,17939,17940,17941,17942,17943,17944,17945,17946,17947,
17948,17949,17950,17951,17952,17953,17954,17955,17956,17957,17958,17959,
17960,17961,17962,17963,17964,17965,17966,17967,17968,17969,17970,17971,
17972,17973,17974,17975,17976,17977,17978,17979,17980,17981,17982,17983,
17984,17985,17986,17987,17988,17989,17990,17991,17992,17993,17994,17995,
17996,17997,17998,17999,18000,18001,18002,18003,18004,18005,18006,18007,
18008,18009,18010,18011,18012,18013,18014,18015,18016,18017,18018,18019,
18020,18021,18022,18023,18024,18025,18026,18027,18028,18029,18030,18031,
18032,18033,18034,18035,18036,18037,18038,18039,18040,18041,18042,18043,
18044,18045,18046,18047,18048,18049,18050,18051,18052,18053,18054,18055,
18056,18057,18058,18059,18060,18061,18062,18063,18064,18065,18066,18067,
18068,18069,18070,18071,18072,18073,18074,18075,18076,18077,18078,18079,
18080,18081,18082,18083,18084,18085,18086,18087,18088,18089,18090,18091,
18092,18093,18094,18095,18096,18097,18098,18099,18100,18101,18102,18103,
18104,18105,18106,18107,18108,18109,18110,18111,18112,18113,18114,18115,
18116,18117,18118,18119,18120,18121,18122,18123,18124,18125,18126,18127,
18128,18129,18130,18131,18132,18133,18134,18135,18136,18137,18138,18139,
18140,18141,18142,18143,18144,18145,18146,18147,18148,18149,18150,18151,
18152,18153,18154,18155,18156,18157,18158,18159,18160,18161,18162,18163,
18164,18165,18166,18167,18168,18169,18170,18171,18172,18173,18174,18175,
18176,18177,18178,18179,18180,18181,18182,18183,18184,18185,18186,18187,
18188,18189,18190,18191,18192,18193,18194,18195,18196,18197,18198,18199,
18200,18201,18202,18203,18204,18205,18206,18207,18208,18209,18210,18211,
18212,18213,18214,18215,18216,18217,18218,18219,18220,18221,18222,18223,
18224,18225,18226,18227,18228,18229,18230,18231,18232,18233,18234,18235,
18236,18237,18238,18239,18240,18241,18242,18243,18244,18245,18246,18247,
18248,18249,18250,18251,18252,18253,18254,18255,18256,18257,18258,18259,
18260,18261,18262,18263,18264,18265,18266,18267,18268,18269,18270,18271,
18272,18273,18274,18275,18276,18277,18278,18279,18280,18281,18282,18283,
18284,18285,18286,18287,18288,18289,18290,18291,18292,18293,18294,18295,
18296,18297,18298,18299,18300,18301,18302,18303,18304,18305,18306,18307,
18308,18309,18310,18311,18312,18313,18314,18315,18316,18317,18318,18319,
18320,18321,18322,18323,18324,18325,18326,18327,18328,18329,18330,18331,
18332,18333,18334,18335,18336,18337,18338,18339,18340,18341,18342,18343,
18344,18345,18346,18347,18348,18349,18350,18351,18352,18353,18354,18355,
18356,18357,18358,18359,18360,18361,18362,18363,18364,18365,18366,18367,
18368,18369,18370,18371,18372,18373,18374,18375,18376,18377,18378,18379,
18380,18381,18382,18383,18384,18385,18386,18387,18388,18389,18390,18391,
18392,18393,18394,18395,18396,18397,18398,18399,18400,18401,18402,18403,
18404,18405,18406,18407,18408,18409,18410,18411,18412,18413,18414,18415,
18416,18417,18418,18419,18420,18421,18422,18423,18424,18425,18426,18427,
18428,18429,18430,18431,18432,18433,18434,18435,18436,18437,18438,18439,
18440,18441,18442,18443,18444,18445,18446,18447,18448,18449,18450,18451,
18452,18453,18454,18455,18456,18457,18458,18459,18460,18461,18462,18463,
18464,18465,18466,18467,18468,18469,18470,18471,18472,18473,18474,18475,
18476,18477,18478,18479,18480,18481,18482,18483,18484,18485,18486,18487,
18488,18489,18490,18491,18492,18493,18494,18495,18496,18497,18498,18499,
18500,18501,18502,18503,18504,18505,18506,18507,18508,18509,18510,18511,
18512,18513,18514,18515,18516,18517,18518,18519,18520,18521,18522,18523,
18524,18525,18526,18527,18528,18529,18530,18531,18532,18533,18534,18535,
18536,18537,18538,18539,18540,18541,18542,18543,18544,18545,18546,18547,
18548,18549,18550,18551,18552,18553,18554,18555,18556,18557,18558,18559,
18560,18561,18562,18563,18564,18565,18566,18567,18568,18569,18570,18571,
18572,18573,18574,18575,18576,18577,18578,18579,18580,18581,18582,18583,
18584,18585,18586,18587,18588,18589,18590,18591,18592,18593,18594,18595,
18596,18597,18598,18599,18600,18601,18602,18603,18604,18605,18606,18607,
18608,18609,18610,18611,18612,18613,18614,18615,18616,18617,18618,18619,
18620,18621,18622,18623,18624,18625,18626,18627,18628,18629,18630,18631,
18632,18633,18634,18635,18636,18637,18638,18639,18640,18641,18642,18643,
18644,18645,18646,18647,18648,18649,18650,18651,18652,18653,18654,18655,
18656,18657,18658,18659,18660,18661,18662,18663,18664,18665,18666,18667,
18668,18669,18670,18671,18672,18673,18674,18675,18676,18677,18678,18679,
18680,18681,18682,18683,18684,18685,18686,18687,18688,18689,18690,18691,
18692,18693,18694,18695,18696,18697,18698,18699,18700,18701,18702,18703,
18704,18705,18706,18707,18708,18709,18710,18711,18712,18713,18714,18715,
18716,18717,18718,18719,18720,18721,18722,18723,18724,18725,18726,18727,
18728,18729,18730,18731,18732,18733,18734,18735,18736,18737,18738,18739,
18740,18741,18742,18743,18744,18745,18746,18747,18748,18749,18750,18751,
18752,18753,18754,18755,18756,18757,18758,18759,18760,18761,18762,18763,
18764,18765,18766,18767,18768,18769,18770,18771,18772,18773,18774,18775,
18776,18777,18778,18779,18780,18781,18782,18783,18784,18785,18786,18787,
18788,18789,18790,18791,18792,18793,18794,18795,18796,18797,18798,18799,
18800,18801,18802,18803,18804,18805,18806,18807,18808,18809,18810,18811,
18812,18813,18814,18815,18816,18817,18818,18819,18820,18821,18822,18823,
18824,18825,18826,18827,18828,18829,18830,18831,18832,18833,18834,18835,
18836,18837,18838,18839,18840,18841,18842,18843,18844,18845,18846,18847,
18848,18849,18850,18851,18852,18853,18854,18855,18856,18857,18858,18859,
18860,18861,18862,18863,18864,18865,18866,18867,18868,18869,18870,18871,
18872,18873,18874,18875,18876,18877,18878,18879,18880,18881,18882,18883,
18884,18885,18886,18887,18888,18889,18890,18891,18892,18893,18894,18895,
18896,18897,18898,18899,18900,18901,18902,18903,18904,18905,18906,18907,
18908,18909,18910,18911,18912,18913,18914,18915,18916,18917,18918,18919,
18920,18921,18922,18923,18924,18925,18926,18927,18928,18929,18930,18931,
18932,18933,18934,18935,18936,18937,18938,18939,18940,18941,18942,18943,
18944,18945,18946,18947,18948,18949,18950,18951,18952,18953,18954,18955,
18956,18957,18958,18959,18960,18961,18962,18963,18964,18965,18966,18967,
18968,18969,18970,18971,18972,18973,18974,18975,18976,18977,18978,18979,
18980,18981,18982,18983,18984,18985,18986,18987,18988,18989,18990,18991,
18992,18993,18994,18995,18996,18997,18998,18999,19000,19001,19002,19003,
19004,19005,19006,19007,19008,19009,19010,19011,19012,19013,19014,19015,
19016,19017,19018,19019,19020,19021,19022,19023,19024,19025,19026,19027,
19028,19029,19030,19031,19032,19033,19034,19035,19036,19037,19038,19039,
19040,19041,19042,19043,19044,19045,19046,19047,19048,19049,19050,19051,
19052,19053,19054,19055,19056,19057,19058,19059,19060,19061,19062,19063,
19064,19065,19066,19067,19068,19069,19070,19071,19072,19073,19074,19075,
19076,19077,19078,19079,19080,19081,19082,19083,19084,19085,19086,19087,
19088,19089,19090,19091,19092,19093,19094,19095,19096,19097,19098,19099,
19100,19101,19102,19103,19104,19105,19106,19107,19108,19109,19110,19111,
19112,19113,19114,19115,19116,19117,19118,19119,19120,19121,19122,19123,
19124,19125,19126,19127,19128,19129,19130,19131,19132,19133,19134,19135,
19136,19137,19138,19139,19140,19141,19142,19143,19144,19145,19146,19147,
19148,19149,19150,19151,19152,19153,19154,19155,19156,19157,19158,19159,
19160,19161,19162,19163,19164,19165,19166,19167,19168,19169,19170,19171,
19172,19173,19174,19175,19176,19177,19178,19179,19180,19181,19182,19183,
19184,19185,19186,19187,19188,19189,19190,19191,19192,19193,19194,19195,
19196,19197,19198,19199,19200,19201,19202,19203,19204,19205,19206,19207,
19208,19209,19210,19211,19212,19213,19214,19215,19216,19217,19218,19219,
19220,19221,19222,19223,19224,19225,19226,19227,19228,19229,19230,19231,
19232,19233,19234,19235,19236,19237,19238,19239,19240,19241,19242,19243,
19244,19245,19246,19247,19248,19249,19250,19251,19252,19253,19254,19255,
19256,19257,19258,19259,19260,19261,19262,19263,19264,19265,19266,19267,
19268,19269,19270,19271,19272,19273,19274,19275,19276,19277,19278,19279,
19280,19281,19282,19283,19284,19285,19286,19287,19288,19289,19290,19291,
19292,19293,19294,19295,19296,19297,19298,19299,19300,19301,19302,19303,
19304,19305,19306,19307,19308,19309,19310,19311,19312,19313,19314,19315,
19316,19317,19318,19319,19320,19321,19322,19323,19324,19325,19326,19327,
19328,19329,19330,19331,19332,19333,19334,19335,19336,19337,19338,19339,
19340,19341,19342,19343,19344,19345,19346,19347,19348,19349,19350,19351,
19352,19353,19354,19355,19356,19357,19358,19359,19360,19361,19362,19363,
19364,19365,19366,19367,19368,19369,19370,19371,19372,19373,19374,19375,
19376,19377,19378,19379,19380,19381,19382,19383,19384,19385,19386,19387,
19388,19389,19390,19391,19392,19393,19394,19395,19396,19397,19398,19399,
19400,19401,19402,19403,19404,19405,19406,19407,19408,19409,19410,19411,
19412,19413,19414,19415,19416,19417,19418,19419,19420,19421,19422,19423,
19424,19425,19426,19427,19428,19429,19430,19431,19432,19433,19434,19435,
19436,19437,19438,19439,19440,19441,19442,19443,19444,19445,19446,19447,
19448,19449,19450,19451,19452,19453,19454,19455,19456,19457,19458,19459,
19460,19461,19462,19463,19464,19465,19466,19467,19468,19469,19470,19471,
19472,19473,19474,19475,19476,19477,19478,19479,19480,19481,19482,19483,
19484,19485,19486,19487,19488,19489,19490,19491,19492,19493,19494,19495,
19496,19497,19498,19499,19500,19501,19502,19503,19504,19505,19506,19507,
19508,19509,19510,19511,19512,19513,19514,19515,19516,19517,19518,19519,
19520,19521,19522,19523,19524,19525,19526,19527,19528,19529,19530,19531,
19532,19533,19534,19535,19536,19537,19538,19539,19540,19541,19542,19543,
19544,19545,19546,19547,19548,19549,19550,19551,19552,19553,19554,19555,
19556,19557,19558,19559,19560,19561,19562,19563,19564,19565,19566,19567,
19568,19569,19570,19571,19572,19573,19574,19575,19576,19577,19578,19579,
19580,19581,19582,19583,19584,19585,19586,19587,19588,19589,19590,19591,
19592,19593,19594,19595,19596,19597,19598,19599,19600,19601,19602,19603,
19604,19605,19606,19607,19608,19609,19610,19611,19612,19613,19614,19615,
19616,19617,19618,19619,19620,19621,19622,19623,19624,19625,19626,19627,
19628,19629,19630,19631,19632,19633,19634,19635,19636,19637,19638,19639,
19640,19641,19642,19643,19644,19645,19646,19647,19648,19649,19650,19651,
19652,19653,19654,19655,19656,19657,19658,19659,19660,19661,19662,19663,
19664,19665,19666,19667,19668,19669,19670,19671,19672,19673,19674,19675,
19676,19677,19678,19679,19680,19681,19682,19683,19684,19685,19686,19687,
19688,19689,19690,19691,19692,19693,19694,19695,19696,19697,19698,19699,
19700,19701,19702,19703,19704,19705,19706,19707,19708,19709,19710,19711,
19712,19713,19714,19715,19716,19717,19718,19719,19720,19721,19722,19723,
19724,19725,19726,19727,19728,19729,19730,19731,19732,19733,19734,19735,
19736,19737,19738,19739,19740,19741,19742,19743,19744,19745,19746,19747,
19748,19749,19750,19751,19752,19753,19754,19755,19756,19757,19758,19759,
19760,19761,19762,19763,19764,19765,19766,19767,19768,19769,19770,19771,
19772,19773,19774,19775,19776,19777,19778,19779,19780,19781,19782,19783,
19784,19785,19786,19787,19788,19789,19790,19791,19792,19793,19794,19795,
19796,19797,19798,19799,19800,19801,19802,19803,19804,19805,19806,19807,
19808,19809,19810,19811,19812,19813,19814,19815,19816,19817,19818,19819,
19820,19821,19822,19823,19824,19825,19826,19827,19828,19829,19830,19831,
19832,19833,19834,19835,19836,19837,19838,19839,19840,19841,19842,19843,
19844,19845,19846,19847,19848,19849,19850,19851,19852,19853,19854,19855,
19856,19857,19858,19859,19860,19861,19862,19863,19864,19865,19866,19867,
19868,19869,19870,19871,19872,19873,19874,19875,19876,19877,19878,19879,
19880,19881,19882,19883,19884,19885,19886,19887,19888,19889,19890,19891,
19892,19893,19894,19895,19896,19897,19898,19899,19900,19901,19902,19903,
19904,19905,19906,19907,19908,19909,19910,19911,19912,19913,19914,19915,
19916,19917,19918,19919,19920,19921,19922,19923,19924,19925,19926,19927,
19928,19929,19930,19931,19932,19933,19934,19935,19936,19937,19938,19939,
19940,19941,19942,19943,19944,19945,19946,19947,19948,19949,19950,19951,
19952,19953,19954,19955,19956,19957,19958,19959,19960,19961,19962,19963,
19964,19965,19966,19967,19968,19969,19970,19971,19972,19973,19974,19975,
19976,19977,19978,19979,19980,19981,19982,19983,19984,19985,19986,19987,
19988,19989,19990,19991,19992,19993,19994,19995,19996,19997,19998,19999,
20000,20001,20002,20003,20004,20005,20006,20007,20008,20009,20010,20011,
20012,20013,20014,20015,20016,20017,20018,20019,20020,20021,20022,20023,
20024,20025,20026,20027,20028,20029,20030,20031,20032,20033,20034,20035,
20036,20037,20038,20039,20040,20041,20042,20043,20044,20045,20046,20047,
20048,20049,20050,20051,20052,20053,20054,20055,20056,20057,20058,20059,
20060,20061,20062,20063,20064,20065,20066,20067,20068,20069,20070,20071,
20072,20073,20074,20075,20076,20077,20078,20079,20080,20081,20082,20083,
20084,20085,20086,20087,20088,20089,20090,20091,20092,20093,20094,20095,
20096,20097,20098,20099,20100,20101,20102,20103,20104,20105,20106,20107,
20108,20109,20110,20111,20112,20113,20114,20115,20116,20117,20118,20119,
20120,20121,20122,20123,20124,20125,20126,20127,20128,20129,20130,20131,
20132,20133,20134,20135,20136,20137,20138,20139,20140,20141,20142,20143,
20144,20145,20146,20147,20148,20149,20150,20151,20152,20153,20154,20155,
20156,20157,20158,20159,20160,20161,20162,20163,20164,20165,20166,20167,
20168,20169,20170,20171,20172,20173,20174,20175,20176,20177,20178,20179,
20180,20181,20182,20183,20184,20185,20186,20187,20188,20189,20190,20191,
20192,20193,20194,20195,20196,20197,20198,20199,20200,20201,20202,20203,
20204,20205,20206,20207,20208,20209,20210,20211,20212,20213,20214,20215,
20216,20217,20218,20219,20220,20221,20222,20223,20224,20225,20226,20227,
20228,20229,20230,20231,20232,20233,20234,20235,20236,20237,20238,20239,
20240,20241,20242,20243,20244,20245,20246,20247,20248,20249,20250,20251,
20252,20253,20254,20255,20256,20257,20258,20259,20260,20261,20262,20263,
20264,20265,20266,20267,20268,20269,20270,20271,20272,20273,20274,20275,
20276,20277,20278,20279,20280,20281,20282,20283,20284,20285,20286,20287,
20288,20289,20290,20291,20292,20293,20294,20295,20296,20297,20298,20299,
20300,20301,20302,20303,20304,20305,20306,20307,20308,20309,20310,20311,
20312,20313,20314,20315,20316,20317,20318,20319,20320,20321,20322,20323,
20324,20325,20326,20327,20328,20329,20330,20331,20332,20333,20334,20335,
20336,20337,20338,20339,20340,20341,20342,20343,20344,20345,20346,20347,
20348,20349,20350,20351,20352,20353,20354,20355,20356,20357,20358,20359,
20360,20361,20362,20363,20364,20365,20366,20367,20368,20369,20370,20371,
20372,20373,20374,20375,20376,20377,20378,20379,20380,20381,20382,20383,
20384,20385,20386,20387,20388,20389,20390,20391,20392,20393,20394,20395,
20396,20397,20398,20399,20400,20401,20402,20403,20404,20405,20406,20407,
20408,20409,20410,20411,20412,20413,20414,20415,20416,20417,20418,20419,
20420,20421,20422,20423,20424,20425,20426,20427,20428,20429,20430,20431,
20432,20433,20434,20435,20436,20437,20438,20439,20440,20441,20442,20443,
20444,20445,20446,20447,20448,20449,20450,20451,20452,20453,20454,20455,
20456,20457,20458,20459,20460,20461,20462,20463,20464,20465,20466,20467,
20468,20469,20470,20471,20472,20473,20474,20475,20476,20477,20478,20479,
20480,20481,20482,20483,20484,20485,20486,20487,20488,20489,20490,20491,
20492,20493,20494,20495,20496,20497,20498,20499,20500,20501,20502,20503,
20504,20505,20506,20507,20508,20509,20510,20511,20512,20513,20514,20515,
20516,20517,20518,20519,20520,20521,20522,20523,20524,20525,20526,20527,
20528,20529,20530,20531,20532,20533,20534,20535,20536,20537,20538,20539,
20540,20541,20542,20543,20544,20545,20546,20547,20548,20549,20550,20551,
20552,20553,20554,20555,20556,20557,20558,20559,20560,20561,20562,20563,
20564,20565,20566,20567,20568,20569,20570,20571,20572,20573,20574,20575,
20576,20577,20578,20579,20580,20581,20582,20583,20584,20585,20586,20587,
20588,20589,20590,20591,20592,20593,20594,20595,20596,20597,20598,20599,
20600,20601,20602,20603,20604,20605,20606,20607,20608,20609,20610,20611,
20612,20613,20614,20615,20616,20617,20618,20619,20620,20621,20622,20623,
20624,20625,20626,20627,20628,20629,20630,20631,20632,20633,20634,20635,
20636,20637,20638,20639,20640,20641,20642,20643,20644,20645,20646,20647,
20648,20649,20650,20651,20652,20653,20654,20655,20656,20657,20658,20659,
20660,20661,20662,20663,20664,20665,20666,20667,20668,20669,20670,20671,
20672,20673,20674,20675,20676,20677,20678,20679,20680,20681,20682,20683,
20684,20685,20686,20687,20688,20689,20690,20691,20692,20693,20694,20695,
20696,20697,20698,20699,20700,20701,20702,20703,20704,20705,20706,20707,
20708,20709,20710,20711,20712,20713,20714,20715,20716,20717,20718,20719,
20720,20721,20722,20723,20724,20725,20726,20727,20728,20729,20730,20731,
20732,20733,20734,20735,20736,20737,20738,20739,20740,20741,20742,20743,
20744,20745,20746,20747,20748,20749,20750,20751,20752,20753,20754,20755,
20756,20757,20758,20759,20760,20761,20762,20763,20764,20765,20766,20767,
20768,20769,20770,20771,20772,20773,20774,20775,20776,20777,20778,20779,
20780,20781,20782,20783,20784,20785,20786,20787,20788,20789,20790,20791,
20792,20793,20794,20795,20796,20797,20798,20799,20800,20801,20802,20803,
20804,20805,20806,20807,20808,20809,20810,20811,20812,20813,20814,20815,
20816,20817,20818,20819,20820,20821,20822,20823,20824,20825,20826,20827,
20828,20829,20830,20831,20832,20833,20834,20835,20836,20837,20838,20839,
20840,20841,20842,20843,20844,20845,20846,20847,20848,20849,20850,20851,
20852,20853,20854,20855,20856,20857,20858,20859,20860,20861,20862,20863,
20864,20865,20866,20867,20868,20869,20870,20871,20872,20873,20874,20875,
20876,20877,20878,20879,20880,20881,20882,20883,20884,20885,20886,20887,
20888,20889,20890,20891,20892,20893,20894,20895,20896,20897,20898,20899,
20900,20901,20902,20903,20904,20905,20906,20907,20908,20909,20910,20911,
20912,20913,20914,20915,20916,20917,20918,20919,20920,20921,20922,20923,
20924,20925,20926,20927,20928,20929,20930,20931,20932,20933,20934,20935,
20936,20937,20938,20939,20940,20941,20942,20943,20944,20945,20946,20947,
20948,20949,20950,20951,20952,20953,20954,20955,20956,20957,20958,20959,
20960,20961,20962,20963,20964,20965,20966,20967,20968,20969,20970,20971,
20972,20973,20974,20975,20976,20977,20978,20979,20980,20981,20982,20983,
20984,20985,20986,20987,20988,20989,20990,20991,20992,20993,20994,20995,
20996,20997,20998,20999,21000,21001,21002,21003,21004,21005,21006,21007,
21008,21009,21010,21011,21012,21013,21014,21015,21016,21017,21018,21019,
21020,21021,21022,21023,21024,21025,21026,21027,21028,21029,21030,21031,
21032,21033,21034,21035,21036,21037,21038,21039,21040,21041,21042,21043,
21044,21045,21046,21047,21048,21049,21050,21051,21052,21053,21054,21055,
21056,21057,21058,21059,21060,21061,21062,21063,21064,21065,21066,21067,
21068,21069,21070,21071,21072,21073,21074,21075,21076,21077,21078,21079,
21080,21081,21082,21083,21084,21085,21086,21087,21088,21089,21090,21091,
21092,21093,21094,21095,21096,21097,21098,21099,21100,21101,21102,21103,
21104,21105,21106,21107,21108,21109,21110,21111,21112,21113,21114,21115,
21116,21117,21118,21119,21120,21121,21122,21123,21124,21125,21126,21127,
21128,21129,21130,21131,21132,21133,21134,21135,21136,21137,21138,21139,
21140,21141,21142,21143,21144,21145,21146,21147,21148,21149,21150,21151,
21152,21153,21154,21155,21156,21157,21158,21159,21160,21161,21162,21163,
21164,21165,21166,21167,21168,21169,21170,21171,21172,21173,21174,21175,
21176,21177,21178,21179,21180,21181,21182,21183,21184,21185,21186,21187,
21188,21189,21190,21191,21192,21193,21194,21195,21196,21197,21198,21199,
21200,21201,21202,21203,21204,21205,21206,21207,21208,21209,21210,21211,
21212,21213,21214,21215,21216,21217,21218,21219,21220,21221,21222,21223,
21224,21225,21226,21227,21228,21229,21230,21231,21232,21233,21234,21235,
21236,21237,21238,21239,21240,21241,21242,21243,21244,21245,21246,21247,
21248,21249,21250,21251,21252,21253,21254,21255,21256,21257,21258,21259,
21260,21261,21262,21263,21264,21265,21266,21267,21268,21269,21270,21271,
21272,21273,21274,21275,21276,21277,21278,21279,21280,21281,21282,21283,
21284,21285,21286,21287,21288,21289,21290,21291,21292,21293,21294,21295,
21296,21297,21298,21299,21300,21301,21302,21303,21304,21305,21306,21307,
21308,21309,21310,21311,21312,21313,21314,21315,21316,21317,21318,21319,
21320,21321,21322,21323,21324,21325,21326,21327,21328,21329,21330,21331,
21332,21333,21334,21335,21336,21337,21338,21339,21340,21341,21342,21343,
21344,21345,21346,21347,21348,21349,21350,21351,21352,21353,21354,21355,
21356,21357,21358,21359,21360,21361,21362,21363,21364,21365,21366,21367,
21368,21369,21370,21371,21372,21373,21374,21375,21376,21377,21378,21379,
21380,21381,21382,21383,21384,21385,21386,21387,21388,21389,21390,21391,
21392,21393,21394,21395,21396,21397,21398,21399,21400,21401,21402,21403,
21404,21405,21406,21407,21408,21409,21410,21411,21412,21413,21414,21415,
21416,21417,21418,21419,21420,21421,21422,21423,21424,21425,21426,21427,
21428,21429,21430,21431,21432,21433,21434,21435,21436,21437,21438,21439,
21440,21441,21442,21443,21444,21445,21446,21447,21448,21449,21450,21451,
21452,21453,21454,21455,21456,21457,21458,21459,21460,21461,21462,21463,
21464,21465,21466,21467,21468,21469,21470,21471,21472,21473,21474,21475,
21476,21477,21478,21479,21480,21481,21482,21483,21484,21485,21486,21487,
21488,21489,21490,21491,21492,21493,21494,21495,21496,21497,21498,21499,
21500,21501,21502,21503,21504,21505,21506,21507,21508,21509,21510,21511,
21512,21513,21514,21515,21516,21517,21518,21519,21520,21521,21522,21523,
21524,21525,21526,21527,21528,21529,21530,21531,21532,21533,21534,21535,
21536,21537,21538,21539,21540,21541,21542,21543,21544,21545,21546,21547,
21548,21549,21550,21551,21552,21553,21554,21555,21556,21557,21558,21559,
21560,21561,21562,21563,21564,21565,21566,21567,21568,21569,21570,21571,
21572,21573,21574,21575,21576,21577,21578,21579,21580,21581,21582,21583,
21584,21585,21586,21587,21588,21589,21590,21591,21592,21593,21594,21595,
21596,21597,21598,21599,21600,21601,21602,21603,21604,21605,21606,21607,
21608,21609,21610,21611,21612,21613,21614,21615,21616,21617,21618,21619,
21620,21621,21622,21623,21624,21625,21626,21627,21628,21629,21630,21631,
21632,21633,21634,21635,21636,21637,21638,21639,21640,21641,21642,21643,
21644,21645,21646,21647,21648,21649,21650,21651,21652,21653,21654,21655,
21656,21657,21658,21659,21660,21661,21662,21663,21664,21665,21666,21667,
21668,21669,21670,21671,21672,21673,21674,21675,21676,21677,21678,21679,
21680,21681,21682,21683,21684,21685,21686,21687,21688,21689,21690,21691,
21692,21693,21694,21695,21696,21697,21698,21699,21700,21701,21702,21703,
21704,21705,21706,21707,21708,21709,21710,21711,21712,21713,21714,21715,
21716,21717,21718,21719,21720,21721,21722,21723,21724,21725,21726,21727,
21728,21729,21730,21731,21732,21733,21734,21735,21736,21737,21738,21739,
21740,21741,21742,21743,21744,21745,21746,21747,21748,21749,21750,21751,
21752,21753,21754,21755,21756,21757,21758,21759,21760,21761,21762,21763,
21764,21765,21766,21767,21768,21769,21770,21771,21772,21773,21774,21775,
21776,21777,21778,21779,21780,21781,21782,21783,21784,21785,21786,21787,
21788,21789,21790,21791,21792,21793,21794,21795,21796,21797,21798,21799,
21800,21801,21802,21803,21804,21805,21806,21807,21808,21809,21810,21811,
21812,21813,21814,21815,21816,21817,21818,21819,21820,21821,21822,21823,
21824,21825,21826,21827,21828,21829,21830,21831,21832,21833,21834,21835,
21836,21837,21838,21839,21840,21841,21842,21843,21844,21845,21846,21847,
21848,21849,21850,21851,21852,21853,21854,21855,21856,21857,21858,21859,
21860,21861,21862,21863,21864,21865,21866,21867,21868,21869,21870,21871,
21872,21873,21874,21875,21876,21877,21878,21879,21880,21881,21882,21883,
21884,21885,21886,21887,21888,21889,21890,21891,21892,21893,21894,21895,
21896,21897,21898,21899,21900,21901,21902,21903,21904,21905,21906,21907,
21908,21909,21910,21911,21912,21913,21914,21915,21916,21917,21918,21919,
21920,21921,21922,21923,21924,21925,21926,21927,21928,21929,21930,21931,
21932,21933,21934,21935,21936,21937,21938,21939,21940,21941,21942,21943,
21944,21945,21946,21947,21948,21949,21950,21951,21952,21953,21954,21955,
21956,21957,21958,21959,21960,21961,21962,21963,21964,21965,21966,21967,
21968,21969,21970,21971,21972,21973,21974,21975,21976,21977,21978,21979,
21980,21981,21982,21983,21984,21985,21986,21987,21988,21989,21990,21991,
21992,21993,21994,21995,21996,21997,21998,21999,22000,22001,22002,22003,
22004,22005,22006,22007,22008,22009,22010,22011,22012,22013,22014,22015,
22016,22017,22018,22019,22020,22021,22022,22023,22024,22025,22026,22027,
22028,22029,22030,22031,22032,22033,22034,22035,22036,22037,22038,22039,
22040,22041,22042,22043,22044,22045,22046,22047,22048,22049,22050,22051,
22052,22053,22054,22055,22056,22057,22058,22059,22060,22061,22062,22063,
22064,22065,22066,22067,22068,22069,22070,22071,22072,22073,22074,22075,
22076,22077,22078,22079,22080,22081,22082,22083,22084,22085,22086,22087,
22088,22089,22090,22091,22092,22093,22094,22095,22096,22097,22098,22099,
22100,22101,22102,22103,22104,22105,22106,22107,22108,22109,22110,22111,
22112,22113,22114,22115,22116,22117,22118,22119,22120,22121,22122,22123,
22124,22125,22126,22127,22128,22129,22130,22131,22132,22133,22134,22135,
22136,22137,22138,22139,22140,22141,22142,22143,22144,22145,22146,22147,
22148,22149,22150,22151,22152,22153,22154,22155,22156,22157,22158,22159,
22160,22161,22162,22163,22164,22165,22166,22167,22168,22169,22170,22171,
22172,22173,22174,22175,22176,22177,22178,22179,22180,22181,22182,22183,
22184,22185,22186,22187,22188,22189,22190,22191,22192,22193,22194,22195,
22196,22197,22198,22199,22200,22201,22202,22203,22204,22205,22206,22207,
22208,22209,22210,22211,22212,22213,22214,22215,22216,22217,22218,22219,
22220,22221,22222,22223,22224,22225,22226,22227,22228,22229,22230,22231,
22232,22233,22234,22235,22236,22237,22238,22239,22240,22241,22242,22243,
22244,22245,22246,22247,22248,22249,22250,22251,22252,22253,22254,22255,
22256,22257,22258,22259,22260,22261,22262,22263,22264,22265,22266,22267,
22268,22269,22270,22271,22272,22273,22274,22275,22276,22277,22278,22279,
22280,22281,22282,22283,22284,22285,22286,22287,22288,22289,22290,22291,
22292,22293,22294,22295,22296,22297,22298,22299,22300,22301,22302,22303,
22304,22305,22306,22307,22308,22309,22310,22311,22312,22313,22314,22315,
22316,22317,22318,22319,22320,22321,22322,22323,22324,22325,22326,22327,
22328,22329,22330,22331,22332,22333,22334,22335,22336,22337,22338,22339,
22340,22341,22342,22343,22344,22345,22346,22347,22348,22349,22350,22351,
22352,22353,22354,22355,22356,22357,22358,22359,22360,22361,22362,22363,
22364,22365,22366,22367,22368,22369,22370,22371,22372,22373,22374,22375,
22376,22377,22378,22379,22380,22381,22382,22383,22384,22385,22386,22387,
22388,22389,22390,22391,22392,22393,22394,22395,22396,22397,22398,22399,
22400,22401,22402,22403,22404,22405,22406,22407,22408,22409,22410,22411,
22412,22413,22414,22415,22416,22417,22418,22419,22420,22421,22422,22423,
22424,22425,22426,22427,22428,22429,22430,22431,22432,22433,22434,22435,
22436,22437,22438,22439,22440,22441,22442,22443,22444,22445,22446,22447,
22448,22449,22450,22451,22452,22453,22454,22455,22456,22457,22458,22459,
22460,22461,22462,22463,22464,22465,22466,22467,22468,22469,22470,22471,
22472,22473,22474,22475,22476,22477,22478,22479,22480,22481,22482,22483,
22484,22485,22486,22487,22488,22489,22490,22491,22492,22493,22494,22495,
22496,22497,22498,22499,22500,22501,22502,22503,22504,22505,22506,22507,
22508,22509,22510,22511,22512,22513,22514,22515,22516,22517,22518,22519,
22520,22521,22522,22523,22524,22525,22526,22527,22528,22529,22530,22531,
22532,22533,22534,22535,22536,22537,22538,22539,22540,22541,22542,22543,
22544,22545,22546,22547,22548,22549,22550,22551,22552,22553,22554,22555,
22556,22557,22558,22559,22560,22561,22562,22563,22564,22565,22566,22567,
22568,22569,22570,22571,22572,22573,22574,22575,22576,22577,22578,22579,
22580,22581,22582,22583,22584,22585,22586,22587,22588,22589,22590,22591,
22592,22593,22594,22595,22596,22597,22598,22599,22600,22601,22602,22603,
22604,22605,22606,22607,22608,22609,22610,22611,22612,22613,22614,22615,
22616,22617,22618,22619,22620,22621,22622,22623,22624,22625,22626,22627,
22628,22629,22630,22631,22632,22633,22634,22635,22636,22637,22638,22639,
22640,22641,22642,22643,22644,22645,22646,22647,22648,22649,22650,22651,
22652,22653,22654,22655,22656,22657,22658,22659,22660,22661,22662,22663,
22664,22665,22666,22667,22668,22669,22670,22671,22672,22673,22674,22675,
22676,22677,22678,22679,22680,22681,22682,22683,22684,22685,22686,22687,
22688,22689,22690,22691,22692,22693,22694,22695,22696,22697,22698,22699,
22700,22701,22702,22703,22704,22705,22706,22707,22708,22709,22710,22711,
22712,22713,22714,22715,22716,22717,22718,22719,22720,22721,22722,22723,
22724,22725,22726,22727,22728,22729,22730,22731,22732,22733,22734,22735,
22736,22737,22738,22739,22740,22741,22742,22743,22744,22745,22746,22747,
22748,22749,22750,22751,22752,22753,22754,22755,22756,22757,22758,22759,
22760,22761,22762,22763,22764,22765,22766,22767,22768,22769,22770,22771,
22772,22773,22774,22775,22776,22777,22778,22779,22780,22781,22782,22783,
22784,22785,22786,22787,22788,22789,22790,22791,22792,22793,22794,22795,
22796,22797,22798,22799,22800,22801,22802,22803,22804,22805,22806,22807,
22808,22809,22810,22811,22812,22813,22814,22815,22816,22817,22818,22819,
22820,22821,22822,22823,22824,22825,22826,22827,22828,22829,22830,22831,
22832,22833,22834,22835,22836,22837,22838,22839,22840,22841,22842,22843,
22844,22845,22846,22847,22848,22849,22850,22851,22852,22853,22854,22855,
22856,22857,22858,22859,22860,22861,22862,22863,22864,22865,22866,22867,
22868,22869,22870,22871,22872,22873,22874,22875,22876,22877,22878,22879,
22880,22881,22882,22883,22884,22885,22886,22887,22888,22889,22890,22891,
22892,22893,22894,22895,22896,22897,22898,22899,22900,22901,22902,22903,
22904,22905,22906,22907,22908,22909,22910,22911,22912,22913,22914,22915,
22916,22917,22918,22919,22920,22921,22922,22923,22924,22925,22926,22927,
22928,22929,22930,22931,22932,22933,22934,22935,22936,22937,22938,22939,
22940,22941,22942,22943,22944,22945,22946,22947,22948,22949,22950,22951,
22952,22953,22954,22955,22956,22957,22958,22959,22960,22961,22962,22963,
22964,22965,22966,22967,22968,22969,22970,22971,22972,22973,22974,22975,
22976,22977,22978,22979,22980,22981,22982,22983,22984,22985,22986,22987,
22988,22989,22990,22991,22992,22993,22994,22995,22996,22997,22998,22999,
23000,23001,23002,23003,23004,23005,23006,23007,23008,23009,23010,23011,
23012,23013,23014,23015,23016,23017,23018,23019,23020,23021,23022,23023,
23024,23025,23026,23027,23028,23029,23030,23031,23032,23033,23034,23035,
23036,23037,23038,23039,23040,23041,23042,23043,23044,23045,23046,23047,
23048,23049,23050,23051,23052,23053,23054,23055,23056,23057,23058,23059,
23060,23061,23062,23063,23064,23065,23066,23067,23068,23069,23070,23071,
23072,23073,23074,23075,23076,23077,23078,23079,23080,23081,23082,23083,
23084,23085,23086,23087,23088,23089,23090,23091,23092,23093,23094,23095,
23096,23097,23098,23099,23100,23101,23102,23103,23104,23105,23106,23107,
23108,23109,23110,23111,23112,23113,23114,23115,23116,23117,23118,23119,
23120,23121,23122,23123,23124,23125,23126,23127,23128,23129,23130,23131,
23132,23133,23134,23135,23136,23137,23138,23139,23140,23141,23142,23143,
23144,23145,23146,23147,23148,23149,23150,23151,23152,23153,23154,23155,
23156,23157,23158,23159,23160,23161,23162,23163,23164,23165,23166,23167,
23168,23169,23170,23171,23172,23173,23174,23175,23176,23177,23178,23179,
23180,23181,23182,23183,23184,23185,23186,23187,23188,23189,23190,23191,
23192,23193,23194,23195,23196,23197,23198,23199,23200,23201,23202,23203,
23204,23205,23206,23207,23208,23209,23210,23211,23212,23213,23214,23215,
23216,23217,23218,23219,23220,23221,23222,23223,23224,23225,23226,23227,
23228,23229,23230,23231,23232,23233,23234,23235,23236,23237,23238,23239,
23240,23241,23242,23243,23244,23245,23246,23247,23248,23249,23250,23251,
23252,23253,23254,23255,23256,23257,23258,23259,23260,23261,23262,23263,
23264,23265,23266,23267,23268,23269,23270,23271,23272,23273,23274,23275,
23276,23277,23278,23279,23280,23281,23282,23283,23284,23285,23286,23287,
23288,23289,23290,23291,23292,23293,23294,23295,23296,23297,23298,23299,
23300,23301,23302,23303,23304,23305,23306,23307,23308,23309,23310,23311,
23312,23313,23314,23315,23316,23317,23318,23319,23320,23321,23322,23323,
23324,23325,23326,23327,23328,23329,23330,23331,23332,23333,23334,23335,
23336,23337,23338,23339,23340,23341,23342,23343,23344,23345,23346,23347,
23348,23349,23350,23351,23352,23353,23354,23355,23356,23357,23358,23359,
23360,23361,23362,23363,23364,23365,23366,23367,23368,23369,23370,23371,
23372,23373,23374,23375,23376,23377,23378,23379,23380,23381,23382,23383,
23384,23385,23386,23387,23388,23389,23390,23391,23392,23393,23394,23395,
23396,23397,23398,23399,23400,23401,23402,23403,23404,23405,23406,23407,
23408,23409,23410,23411,23412,23413,23414,23415,23416,23417,23418,23419,
23420,23421,23422,23423,23424,23425,23426,23427,23428,23429,23430,23431,
23432,23433,23434,23435,23436,23437,23438,23439,23440,23441,23442,23443,
23444,23445,23446,23447,23448,23449,23450,23451,23452,23453,23454,23455,
23456,23457,23458,23459,23460,23461,23462,23463,23464,23465,23466,23467,
23468,23469,23470,23471,23472,23473,23474,23475,23476,23477,23478,23479,
23480,23481,23482,23483,23484,23485,23486,23487,23488,23489,23490,23491,
23492,23493,23494,23495,23496,23497,23498,23499,23500,23501,23502,23503,
23504,23505,23506,23507,23508,23509,23510,23511,23512,23513,23514,23515,
23516,23517,23518,23519,23520,23521,23522,23523,23524,23525,23526,23527,
23528,23529,23530,23531,23532,23533,23534,23535,23536,23537,23538,23539,
23540,23541,23542,23543,23544,23545,23546,23547,23548,23549,23550,23551,
23552,23553,23554,23555,23556,23557,23558,23559,23560,23561,23562,23563,
23564,23565,23566,23567,23568,23569,23570,23571,23572,23573,23574,23575,
23576,23577,23578,23579,23580,23581,23582,23583,23584,23585,23586,23587,
23588,23589,23590,23591,23592,23593,23594,23595,23596,23597,23598,23599,
23600,23601,23602,23603,23604,23605,23606,23607,23608,23609,23610,23611,
23612,23613,23614,23615,23616,23617,23618,23619,23620,23621,23622,23623,
23624,23625,23626,23627,23628,23629,23630,23631,23632,23633,23634,23635,
23636,23637,23638,23639,23640,23641,23642,23643,23644,23645,23646,23647,
23648,23649,23650,23651,23652,23653,23654,23655,23656,23657,23658,23659,
23660,23661,23662,23663,23664,23665,23666,23667,23668,23669,23670,23671,
23672,23673,23674,23675,23676,23677,23678,23679,23680,23681,23682,23683,
23684,23685,23686,23687,23688,23689,23690,23691,23692,23693,23694,23695,
23696,23697,23698,23699,23700,23701,23702,23703,23704,23705,23706,23707,
23708,23709,23710,23711,23712,23713,23714,23715,23716,23717,23718,23719,
23720,23721,23722,23723,23724,23725,23726,23727,23728,23729,23730,23731,
23732,23733,23734,23735,23736,23737,23738,23739,23740,23741,23742,23743,
23744,23745,23746,23747,23748,23749,23750,23751,23752,23753,23754,23755,
23756,23757,23758,23759,23760,23761,23762,23763,23764,23765,23766,23767,
23768,23769,23770,23771,23772,23773,23774,23775,23776,23777,23778,23779,
23780,23781,23782,23783,23784,23785,23786,23787,23788,23789,23790,23791,
23792,23793,23794,23795,23796,23797,23798,23799,23800,23801,23802,23803,
23804,23805,23806,23807,23808,23809,23810,23811,23812,23813,23814,23815,
23816,23817,23818,23819,23820,23821,23822,23823,23824,23825,23826,23827,
23828,23829,23830,23831,23832,23833,23834,23835,23836,23837,23838,23839,
23840,23841,23842,23843,23844,23845,23846,23847,23848,23849,23850,23851,
23852,23853,23854,23855,23856,23857,23858,23859,23860,23861,23862,23863,
23864,23865,23866,23867,23868,23869,23870,23871,23872,23873,23874,23875,
23876,23877,23878,23879,23880,23881,23882,23883,23884,23885,23886,23887,
23888,23889,23890,23891,23892,23893,23894,23895,23896,23897,23898,23899,
23900,23901,23902,23903,23904,23905,23906,23907,23908,23909,23910,23911,
23912,23913,23914,23915,23916,23917,23918,23919,23920,23921,23922,23923,
23924,23925,23926,23927,23928,23929,23930,23931,23932,23933,23934,23935,
23936,23937,23938,23939,23940,23941,23942,23943,23944,23945,23946,23947,
23948,23949,23950,23951,23952,23953,23954,23955,23956,23957,23958,23959,
23960,23961,23962,23963,23964,23965,23966,23967,23968,23969,23970,23971,
23972,23973,23974,23975,23976,23977,23978,23979,23980,23981,23982,23983,
23984,23985,23986,23987,23988,23989,23990,23991,23992,23993,23994,23995,
23996,23997,23998,23999,24000,24001,24002,24003,24004,24005,24006,24007,
24008,24009,24010,24011,24012,24013,24014,24015,24016,24017,24018,24019,
24020,24021,24022,24023,24024,24025,24026,24027,24028,24029,24030,24031,
24032,24033,24034,24035,24036,24037,24038,24039,24040,24041,24042,24043,
24044,24045,24046,24047,24048,24049,24050,24051,24052,24053,24054,24055,
24056,24057,24058,24059,24060,24061,24062,24063,24064,24065,24066,24067,
24068,24069,24070,24071,24072,24073,24074,24075,24076,24077,24078,24079,
24080,24081,24082,24083,24084,24085,24086,24087,24088,24089,24090,24091,
24092,24093,24094,24095,24096,24097,24098,24099,24100,24101,24102,24103,
24104,24105,24106,24107,24108,24109,24110,24111,24112,24113,24114,24115,
24116,24117,24118,24119,24120,24121,24122,24123,24124,24125,24126,24127,
24128,24129,24130,24131,24132,24133,24134,24135,24136,24137,24138,24139,
24140,24141,24142,24143,24144,24145,24146,24147,24148,24149,24150,24151,
24152,24153,24154,24155,24156,24157,24158,24159,24160,24161,24162,24163,
24164,24165,24166,24167,24168,24169,24170,24171,24172,24173,24174,24175,
24176,24177,24178,24179,24180,24181,24182,24183,24184,24185,24186,24187,
24188,24189,24190,24191,24192,24193,24194,24195,24196,24197,24198,24199,
24200,24201,24202,24203,24204,24205,24206,24207,24208,24209,24210,24211,
24212,24213,24214,24215,24216,24217,24218,24219,24220,24221,24222,24223,
24224,24225,24226,24227,24228,24229,24230,24231,24232,24233,24234,24235,
24236,24237,24238,24239,24240,24241,24242,24243,24244,24245,24246,24247,
24248,24249,24250,24251,24252,24253,24254,24255,24256,24257,24258,24259,
24260,24261,24262,24263,24264,24265,24266,24267,24268,24269,24270,24271,
24272,24273,24274,24275,24276,24277,24278,24279,24280,24281,24282,24283,
24284,24285,24286,24287,24288,24289,24290,24291,24292,24293,24294,24295,
24296,24297,24298,24299,24300,24301,24302,24303,24304,24305,24306,24307,
24308,24309,24310,24311,24312,24313,24314,24315,24316,24317,24318,24319,
24320,24321,24322,24323,24324,24325,24326,24327,24328,24329,24330,24331,
24332,24333,24334,24335,24336,24337,24338,24339,24340,24341,24342,24343,
24344,24345,24346,24347,24348,24349,24350,24351,24352,24353,24354,24355,
24356,24357,24358,24359,24360,24361,24362,24363,24364,24365,24366,24367,
24368,24369,24370,24371,24372,24373,24374,24375,24376,24377,24378,24379,
24380,24381,24382,24383,24384,24385,24386,24387,24388,24389,24390,24391,
24392,24393,24394,24395,24396,24397,24398,24399,24400,24401,24402,24403,
24404,24405,24406,24407,24408,24409,24410,24411,24412,24413,24414,24415,
24416,24417,24418,24419,24420,24421,24422,24423,24424,24425,24426,24427,
24428,24429,24430,24431,24432,24433,24434,24435,24436,24437,24438,24439,
24440,24441,24442,24443,24444,24445,24446,24447,24448,24449,24450,24451,
24452,24453,24454,24455,24456,24457,24458,24459,24460,24461,24462,24463,
24464,24465,24466,24467,24468,24469,24470,24471,24472,24473,24474,24475,
24476,24477,24478,24479,24480,24481,24482,24483,24484,24485,24486,24487,
24488,24489,24490,24491,24492,24493,24494,24495,24496,24497,24498,24499,
24500,24501,24502,24503,24504,24505,24506,24507,24508,24509,24510,24511,
24512,24513,24514,24515,24516,24517,24518,24519,24520,24521,24522,24523,
24524,24525,24526,24527,24528,24529,24530,24531,24532,24533,24534,24535,
24536,24537,24538,24539,24540,24541,24542,24543,24544,24545,24546,24547,
24548,24549,24550,24551,24552,24553,24554,24555,24556,24557,24558,24559,
24560,24561,24562,24563,24564,24565,24566,24567,24568,24569,24570,24571,
24572,24573,24574,24575,24576,24577,24578,24579,24580,24581,24582,24583,
24584,24585,24586,24587,24588,24589,24590,24591,24592,24593,24594,24595,
24596,24597,24598,24599,24600,24601,24602,24603,24604,24605,24606,24607,
24608,24609,24610,24611,24612,24613,24614,24615,24616,24617,24618,24619,
24620,24621,24622,24623,24624,24625,24626,24627,24628,24629,24630,24631,
24632,24633,24634,24635,24636,24637,24638,24639,24640,24641,24642,24643,
24644,24645,24646,24647,24648,24649,24650,24651,24652,24653,24654,24655,
24656,24657,24658,24659,24660,24661,24662,24663,24664,24665,24666,24667,
24668,24669,24670,24671,24672,24673,24674,24675,24676,24677,24678,24679,
24680,24681,24682,24683,24684,24685,24686,24687,24688,24689,24690,24691,
24692,24693,24694,24695,24696,24697,24698,24699,24700,24701,24702,24703,
24704,24705,24706,24707,24708,24709,24710,24711,24712,24713,24714,24715,
24716,24717,24718,24719,24720,24721,24722,24723,24724,24725,24726,24727,
24728,24729,24730,24731,24732,24733,24734,24735,24736,24737,24738,24739,
24740,24741,24742,24743,24744,24745,24746,24747,24748,24749,24750,24751,
24752,24753,24754,24755,24756,24757,24758,24759,24760,24761,24762,24763,
24764,24765,24766,24767,24768,24769,24770,24771,24772,24773,24774,24775,
24776,24777,24778,24779,24780,24781,24782,24783,24784,24785,24786,24787,
24788,24789,24790,24791,24792,24793,24794,24795,24796,24797,24798,24799,
24800,24801,24802,24803,24804,24805,24806,24807,24808,24809,24810,24811,
24812,24813,24814,24815,24816,24817,24818,24819,24820,24821,24822,24823,
24824,24825,24826,24827,24828,24829,24830,24831,24832,24833,24834,24835,
24836,24837,24838,24839,24840,24841,24842,24843,24844,24845,24846,24847,
24848,24849,24850,24851,24852,24853,24854,24855,24856,24857,24858,24859,
24860,24861,24862,24863,24864,24865,24866,24867,24868,24869,24870,24871,
24872,24873,24874,24875,24876,24877,24878,24879,24880,24881,24882,24883,
24884,24885,24886,24887,24888,24889,24890,24891,24892,24893,24894,24895,
24896,24897,24898,24899,24900,24901,24902,24903,24904,24905,24906,24907,
24908,24909,24910,24911,24912,24913,24914,24915,24916,24917,24918,24919,
24920,24921,24922,24923,24924,24925,24926,24927,24928,24929,24930,24931,
24932,24933,24934,24935,24936,24937,24938,24939,24940,24941,24942,24943,
24944,24945,24946,24947,24948,24949,24950,24951,24952,24953,24954,24955,
24956,24957,24958,24959,24960,24961,24962,24963,24964,24965,24966,24967,
24968,24969,24970,24971,24972,24973,24974,24975,24976,24977,24978,24979,
24980,24981,24982,24983,24984,24985,24986,24987,24988,24989,24990,24991,
24992,24993,24994,24995,24996,24997,24998,24999,25000,25001,25002,25003,
25004,25005,25006,25007,25008,25009,25010,25011,25012,25013,25014,25015,
25016,25017,25018,25019,25020,25021,25022,25023,25024,25025,25026,25027,
25028,25029,25030,25031,25032,25033,25034,25035,25036,25037,25038,25039,
25040,25041,25042,25043,25044,25045,25046,25047,25048,25049,25050,25051,
25052,25053,25054,25055,25056,25057,25058,25059,25060,25061,25062,25063,
25064,25065,25066,25067,25068,25069,25070,25071,25072,25073,25074,25075,
25076,25077,25078,25079,25080,25081,25082,25083,25084,25085,25086,25087,
25088,25089,25090,25091,25092,25093,25094,25095,25096,25097,25098,25099,
25100,25101,25102,25103,25104,25105,25106,25107,25108,25109,25110,25111,
25112,25113,25114,25115,25116,25117,25118,25119,25120,25121,25122,25123,
25124,25125,25126,25127,25128,25129,25130,25131,25132,25133,25134,25135,
25136,25137,25138,25139,25140,25141,25142,25143,25144,25145,25146,25147,
25148,25149,25150,25151,25152,25153,25154,25155,25156,25157,25158,25159,
25160,25161,25162,25163,25164,25165,25166,25167,25168,25169,25170,25171,
25172,25173,25174,25175,25176,25177,25178,25179,25180,25181,25182,25183,
25184,25185,25186,25187,25188,25189,25190,25191,25192,25193,25194,25195,
25196,25197,25198,25199,25200,25201,25202,25203,25204,25205,25206,25207,
25208,25209,25210,25211,25212,25213,25214,25215,25216,25217,25218,25219,
25220,25221,25222,25223,25224,25225,25226,25227,25228,25229,25230,25231,
25232,25233,25234,25235,25236,25237,25238,25239,25240,25241,25242,25243,
25244,25245,25246,25247,25248,25249,25250,25251,25252,25253,25254,25255,
25256,25257,25258,25259,25260,25261,25262,25263,25264,25265,25266,25267,
25268,25269,25270,25271,25272,25273,25274,25275,25276,25277,25278,25279,
25280,25281,25282,25283,25284,25285,25286,25287,25288,25289,25290,25291,
25292,25293,25294,25295,25296,25297,25298,25299,25300,25301,25302,25303,
25304,25305,25306,25307,25308,25309,25310,25311,25312,25313,25314,25315,
25316,25317,25318,25319,25320,25321,25322,25323,25324,25325,25326,25327,
25328,25329,25330,25331,25332,25333,25334,25335,25336,25337,25338,25339,
25340,25341,25342,25343,25344,25345,25346,25347,25348,25349,25350,25351,
25352,25353,25354,25355,25356,25357,25358,25359,25360,25361,25362,25363,
25364,25365,25366,25367,25368,25369,25370,25371,25372,25373,25374,25375,
25376,25377,25378,25379,25380,25381,25382,25383,25384,25385,25386,25387,
25388,25389,25390,25391,25392,25393,25394,25395,25396,25397,25398,25399,
25400,25401,25402,25403,25404,25405,25406,25407,25408,25409,25410,25411,
25412,25413,25414,25415,25416,25417,25418,25419,25420,25421,25422,25423,
25424,25425,25426,25427,25428,25429,25430,25431,25432,25433,25434,25435,
25436,25437,25438,25439,25440,25441,25442,25443,25444,25445,25446,25447,
25448,25449,25450,25451,25452,25453,25454,25455,25456,25457,25458,25459,
25460,25461,25462,25463,25464,25465,25466,25467,25468,25469,25470,25471,
25472,25473,25474,25475,25476,25477,25478,25479,25480,25481,25482,25483,
25484,25485,25486,25487,25488,25489,25490,25491,25492,25493,25494,25495,
25496,25497,25498,25499,25500,25501,25502,25503,25504,25505,25506,25507,
25508,25509,25510,25511,25512,25513,25514,25515,25516,25517,25518,25519,
25520,25521,25522,25523,25524,25525,25526,25527,25528,25529,25530,25531,
25532,25533,25534,25535,25536,25537,25538,25539,25540,25541,25542,25543,
25544,25545,25546,25547,25548,25549,25550,25551,25552,25553,25554,25555,
25556,25557,25558,25559,25560,25561,25562,25563,25564,25565,25566,25567,
25568,25569,25570,25571,25572,25573,25574,25575,25576,25577,25578,25579,
25580,25581,25582,25583,25584,25585,25586,25587,25588,25589,25590,25591,
25592,25593,25594,25595,25596,25597,25598,25599,25600,25601,25602,25603,
25604,25605,25606,25607,25608,25609,25610,25611,25612,25613,25614,25615,
25616,25617,25618,25619,25620,25621,25622,25623,25624,25625,25626,25627,
25628,25629,25630,25631,25632,25633,25634,25635,25636,25637,25638,25639,
25640,25641,25642,25643,25644,25645,25646,25647,25648,25649,25650,25651,
25652,25653,25654,25655,25656,25657,25658,25659,25660,25661,25662,25663,
25664,25665,25666,25667,25668,25669,25670,25671,25672,25673,25674,25675,
25676,25677,25678,25679,25680,25681,25682,25683,25684,25685,25686,25687,
25688,25689,25690,25691,25692,25693,25694,25695,25696,25697,25698,25699,
25700,25701,25702,25703,25704,25705,25706,25707,25708,25709,25710,25711,
25712,25713,25714,25715,25716,25717,25718,25719,25720,25721,25722,25723,
25724,25725,25726,25727,25728,25729,25730,25731,25732,25733,25734,25735,
25736,25737,25738,25739,25740,25741,25742,25743,25744,25745,25746,25747,
25748,25749,25750,25751,25752,25753,25754,25755,25756,25757,25758,25759,
25760,25761,25762,25763,25764,25765,25766,25767,25768,25769,25770,25771,
25772,25773,25774,25775,25776,25777,25778,25779,25780,25781,25782,25783,
25784,25785,25786,25787,25788,25789,25790,25791,25792,25793,25794,25795,
25796,25797,25798,25799,25800,25801,25802,25803,25804,25805,25806,25807,
25808,25809,25810,25811,25812,25813,25814,25815,25816,25817,25818,25819,
25820,25821,25822,25823,25824,25825,25826,25827,25828,25829,25830,25831,
25832,25833,25834,25835,25836,25837,25838,25839,25840,25841,25842,25843,
25844,25845,25846,25847,25848,25849,25850,25851,25852,25853,25854,25855,
25856,25857,25858,25859,25860,25861,25862,25863,25864,25865,25866,25867,
25868,25869,25870,25871,25872,25873,25874,25875,25876,25877,25878,25879,
25880,25881,25882,25883,25884,25885,25886,25887,25888,25889,25890,25891,
25892,25893,25894,25895,25896,25897,25898,25899,25900,25901,25902,25903,
25904,25905,25906,25907,25908,25909,25910,25911,25912,25913,25914,25915,
25916,25917,25918,25919,25920,25921,25922,25923,25924,25925,25926,25927,
25928,25929,25930,25931,25932,25933,25934,25935,25936,25937,25938,25939,
25940,25941,25942,25943,25944,25945,25946,25947,25948,25949,25950,25951,
25952,25953,25954,25955,25956,25957,25958,25959,25960,25961,25962,25963,
25964,25965,25966,25967,25968,25969,25970,25971,25972,25973,25974,25975,
25976,25977,25978,25979,25980,25981,25982,25983,25984,25985,25986,25987,
25988,25989,25990,25991,25992,25993,25994,25995,25996,25997,25998,25999,
26000,26001,26002,26003,26004,26005,26006,26007,26008,26009,26010,26011,
26012,26013,26014,26015,26016,26017,26018,26019,26020,26021,26022,26023,
26024,26025,26026,26027,26028,26029,26030,26031,26032,26033,26034,26035,
26036,26037,26038,26039,26040,26041,26042,26043,26044,26045,26046,26047,
26048,26049,26050,26051,26052,26053,26054,26055,26056,26057,26058,26059,
26060,26061,26062,26063,26064,26065,26066,26067,26068,26069,26070,26071,
26072,26073,26074,26075,26076,26077,26078,26079,26080,26081,26082,26083,
26084,26085,26086,26087,26088,26089,26090,26091,26092,26093,26094,26095,
26096,26097,26098,26099,26100,26101,26102,26103,26104,26105,26106,26107,
26108,26109,26110,26111,26112,26113,26114,26115,26116,26117,26118,26119,
26120,26121,26122,26123,26124,26125,26126,26127,26128,26129,26130,26131,
26132,26133,26134,26135,26136,26137,26138,26139,26140,26141,26142,26143,
26144,26145,26146,26147,26148,26149,26150,26151,26152,26153,26154,26155,
26156,26157,26158,26159,26160,26161,26162,26163,26164,26165,26166,26167,
26168,26169,26170,26171,26172,26173,26174,26175,26176,26177,26178,26179,
26180,26181,26182,26183,26184,26185,26186,26187,26188,26189,26190,26191,
26192,26193,26194,26195,26196,26197,26198,26199,26200,26201,26202,26203,
26204,26205,26206,26207,26208,26209,26210,26211,26212,26213,26214,26215,
26216,26217,26218,26219,26220,26221,26222,26223,26224,26225,26226,26227,
26228,26229,26230,26231,26232,26233,26234,26235,26236,26237,26238,26239,
26240,26241,26242,26243,26244,26245,26246,26247,26248,26249,26250,26251,
26252,26253,26254,26255,26256,26257,26258,26259,26260,26261,26262,26263,
26264,26265,26266,26267,26268,26269,26270,26271,26272,26273,26274,26275,
26276,26277,26278,26279,26280,26281,26282,26283,26284,26285,26286,26287,
26288,26289,26290,26291,26292,26293,26294,26295,26296,26297,26298,26299,
26300,26301,26302,26303,26304,26305,26306,26307,26308,26309,26310,26311,
26312,26313,26314,26315,26316,26317,26318,26319,26320,26321,26322,26323,
26324,26325,26326,26327,26328,26329,26330,26331,26332,26333,26334,26335,
26336,26337,26338,26339,26340,26341,26342,26343,26344,26345,26346,26347,
26348,26349,26350,26351,26352,26353,26354,26355,26356,26357,26358,26359,
26360,26361,26362,26363,26364,26365,26366,26367,26368,26369,26370,26371,
26372,26373,26374,26375,26376,26377,26378,26379,26380,26381,26382,26383,
26384,26385,26386,26387,26388,26389,26390,26391,26392,26393,26394,26395,
26396,26397,26398,26399,26400,26401,26402,26403,26404,26405,26406,26407,
26408,26409,26410,26411,26412,26413,26414,26415,26416,26417,26418,26419,
26420,26421,26422,26423,26424,26425,26426,26427,26428,26429,26430,26431,
26432,26433,26434,26435,26436,26437,26438,26439,26440,26441,26442,26443,
26444,26445,26446,26447,26448,26449,26450,26451,26452,26453,26454,26455,
26456,26457,26458,26459,26460,26461,26462,26463,26464,26465,26466,26467,
26468,26469,26470,26471,26472,26473,26474,26475,26476,26477,26478,26479,
26480,26481,26482,26483,26484,26485,26486,26487,26488,26489,26490,26491,
26492,26493,26494,26495,26496,26497,26498,26499,26500,26501,26502,26503,
26504,26505,26506,26507,26508,26509,26510,26511,26512,26513,26514,26515,
26516,26517,26518,26519,26520,26521,26522,26523,26524,26525,26526,26527,
26528,26529,26530,26531,26532,26533,26534,26535,26536,26537,26538,26539,
26540,26541,26542,26543,26544,26545,26546,26547,26548,26549,26550,26551,
26552,26553,26554,26555,26556,26557,26558,26559,26560,26561,26562,26563,
26564,26565,26566,26567,26568,26569,26570,26571,26572,26573,26574,26575,
26576,26577,26578,26579,26580,26581,26582,26583,26584,26585,26586,26587,
26588,26589,26590,26591,26592,26593,26594,26595,26596,26597,26598,26599,
26600,26601,26602,26603,26604,26605,26606,26607,26608,26609,26610,26611,
26612,26613,26614,26615,26616,26617,26618,26619,26620,26621,26622,26623,
26624,26625,26626,26627,26628,26629,26630,26631,26632,26633,26634,26635,
26636,26637,26638,26639,26640,26641,26642,26643,26644,26645,26646,26647,
26648,26649,26650,26651,26652,26653,26654,26655,26656,26657,26658,26659,
26660,26661,26662,26663,26664,26665,26666,26667,26668,26669,26670,26671,
26672,26673,26674,26675,26676,26677,26678,26679,26680,26681,26682,26683,
26684,26685,26686,26687,26688,26689,26690,26691,26692,26693,26694,26695,
26696,26697,26698,26699,26700,26701,26702,26703,26704,26705,26706,26707,
26708,26709,26710,26711,26712,26713,26714,26715,26716,26717,26718,26719,
26720,26721,26722,26723,26724,26725,26726,26727,26728,26729,26730,26731,
26732,26733,26734,26735,26736,26737,26738,26739,26740,26741,26742,26743,
26744,26745,26746,26747,26748,26749,26750,26751,26752,26753,26754,26755,
26756,26757,26758,26759,26760,26761,26762,26763,26764,26765,26766,26767,
26768,26769,26770,26771,26772,26773,26774,26775,26776,26777,26778,26779,
26780,26781,26782,26783,26784,26785,26786,26787,26788,26789,26790,26791,
26792,26793,26794,26795,26796,26797,26798,26799,26800,26801,26802,26803,
26804,26805,26806,26807,26808,26809,26810,26811,26812,26813,26814,26815,
26816,26817,26818,26819,26820,26821,26822,26823,26824,26825,26826,26827,
26828,26829,26830,26831,26832,26833,26834,26835,26836,26837,26838,26839,
26840,26841,26842,26843,26844,26845,26846,26847,26848,26849,26850,26851,
26852,26853,26854,26855,26856,26857,26858,26859,26860,26861,26862,26863,
26864,26865,26866,26867,26868,26869,26870,26871,26872,26873,26874,26875,
26876,26877,26878,26879,26880,26881,26882,26883,26884,26885,26886,26887,
26888,26889,26890,26891,26892,26893,26894,26895,26896,26897,26898,26899,
26900,26901,26902,26903,26904,26905,26906,26907,26908,26909,26910,26911,
26912,26913,26914,26915,26916,26917,26918,26919,26920,26921,26922,26923,
26924,26925,26926,26927,26928,26929,26930,26931,26932,26933,26934,26935,
26936,26937,26938,26939,26940,26941,26942,26943,26944,26945,26946,26947,
26948,26949,26950,26951,26952,26953,26954,26955,26956,26957,26958,26959,
26960,26961,26962,26963,26964,26965,26966,26967,26968,26969,26970,26971,
26972,26973,26974,26975,26976,26977,26978,26979,26980,26981,26982,26983,
26984,26985,26986,26987,26988,26989,26990,26991,26992,26993,26994,26995,
26996,26997,26998,26999,27000,27001,27002,27003,27004,27005,27006,27007,
27008,27009,27010,27011,27012,27013,27014,27015,27016,27017,27018,27019,
27020,27021,27022,27023,27024,27025,27026,27027,27028,27029,27030,27031,
27032,27033,27034,27035,27036,27037,27038,27039,27040,27041,27042,27043,
27044,27045,27046,27047,27048,27049,27050,27051,27052,27053,27054,27055,
27056,27057,27058,27059,27060,27061,27062,27063,27064,27065,27066,27067,
27068,27069,27070,27071,27072,27073,27074,27075,27076,27077,27078,27079,
27080,27081,27082,27083,27084,27085,27086,27087,27088,27089,27090,27091,
27092,27093,27094,27095,27096,27097,27098,27099,27100,27101,27102,27103,
27104,27105,27106,27107,27108,27109,27110,27111,27112,27113,27114,27115,
27116,27117,27118,27119,27120,27121,27122,27123,27124,27125,27126,27127,
27128,27129,27130,27131,27132,27133,27134,27135,27136,27137,27138,27139,
27140,27141,27142,27143,27144,27145,27146,27147,27148,27149,27150,27151,
27152,27153,27154,27155,27156,27157,27158,27159,27160,27161,27162,27163,
27164,27165,27166,27167,27168,27169,27170,27171,27172,27173,27174,27175,
27176,27177,27178,27179,27180,27181,27182,27183,27184,27185,27186,27187,
27188,27189,27190,27191,27192,27193,27194,27195,27196,27197,27198,27199,
27200,27201,27202,27203,27204,27205,27206,27207,27208,27209,27210,27211,
27212,27213,27214,27215,27216,27217,27218,27219,27220,27221,27222,27223,
27224,27225,27226,27227,27228,27229,27230,27231,27232,27233,27234,27235,
27236,27237,27238,27239,27240,27241,27242,27243,27244,27245,27246,27247,
27248,27249,27250,27251,27252,27253,27254,27255,27256,27257,27258,27259,
27260,27261,27262,27263,27264,27265,27266,27267,27268,27269,27270,27271,
27272,27273,27274,27275,27276,27277,27278,27279,27280,27281,27282,27283,
27284,27285,27286,27287,27288,27289,27290,27291,27292,27293,27294,27295,
27296,27297,27298,27299,27300,27301,27302,27303,27304,27305,27306,27307,
27308,27309,27310,27311,27312,27313,27314,27315,27316,27317,27318,27319,
27320,27321,27322,27323,27324,27325,27326,27327,27328,27329,27330,27331,
27332,27333,27334,27335,27336,27337,27338,27339,27340,27341,27342,27343,
27344,27345,27346,27347,27348,27349,27350,27351,27352,27353,27354,27355,
27356,27357,27358,27359,27360,27361,27362,27363,27364,27365,27366,27367,
27368,27369,27370,27371,27372,27373,27374,27375,27376,27377,27378,27379,
27380,27381,27382,27383,27384,27385,27386,27387,27388,27389,27390,27391,
27392,27393,27394,27395,27396,27397,27398,27399,27400,27401,27402,27403,
27404,27405,27406,27407,27408,27409,27410,27411,27412,27413,27414,27415,
27416,27417,27418,27419,27420,27421,27422,27423,27424,27425,27426,27427,
27428,27429,27430,27431,27432,27433,27434,27435,27436,27437,27438,27439,
27440,27441,27442,27443,27444,27445,27446,27447,27448,27449,27450,27451,
27452,27453,27454,27455,27456,27457,27458,27459,27460,27461,27462,27463,
27464,27465,27466,27467,27468,27469,27470,27471,27472,27473,27474,27475,
27476,27477,27478,27479,27480,27481,27482,27483,27484,27485,27486,27487,
27488,27489,27490,27491,27492,27493,27494,27495,27496,27497,27498,27499,
27500,27501,27502,27503,27504,27505,27506,27507,27508,27509,27510,27511,
27512,27513,27514,27515,27516,27517,27518,27519,27520,27521,27522,27523,
27524,27525,27526,27527,27528,27529,27530,27531,27532,27533,27534,27535,
27536,27537,27538,27539,27540,27541,27542,27543,27544,27545,27546,27547,
27548,27549,27550,27551,27552,27553,27554,27555,27556,27557,27558,27559,
27560,27561,27562,27563,27564,27565,27566,27567,27568,27569,27570,27571,
27572,27573,27574,27575,27576,27577,27578,27579,27580,27581,27582,27583,
27584,27585,27586,27587,27588,27589,27590,27591,27592,27593,27594,27595,
27596,27597,27598,27599,27600,27601,27602,27603,27604,27605,27606,27607,
27608,27609,27610,27611,27612,27613,27614,27615,27616,27617,27618,27619,
27620,27621,27622,27623,27624,27625,27626,27627,27628,27629,27630,27631,
27632,27633,27634,27635,27636,27637,27638,27639,27640,27641,27642,27643,
27644,27645,27646,27647,27648,27649,27650,27651,27652,27653,27654,27655,
27656,27657,27658,27659,27660,27661,27662,27663,27664,27665,27666,27667,
27668,27669,27670,27671,27672,27673,27674,27675,27676,27677,27678,27679,
27680,27681,27682,27683,27684,27685,27686,27687,27688,27689,27690,27691,
27692,27693,27694,27695,27696,27697,27698,27699,27700,27701,27702,27703,
27704,27705,27706,27707,27708,27709,27710,27711,27712,27713,27714,27715,
27716,27717,27718,27719,27720,27721,27722,27723,27724,27725,27726,27727,
27728,27729,27730,27731,27732,27733,27734,27735,27736,27737,27738,27739,
27740,27741,27742,27743,27744,27745,27746,27747,27748,27749,27750,27751,
27752,27753,27754,27755,27756,27757,27758,27759,27760,27761,27762,27763,
27764,27765,27766,27767,27768,27769,27770,27771,27772,27773,27774,27775,
27776,27777,27778,27779,27780,27781,27782,27783,27784,27785,27786,27787,
27788,27789,27790,27791,27792,27793,27794,27795,27796,27797,27798,27799,
27800,27801,27802,27803,27804,27805,27806,27807,27808,27809,27810,27811,
27812,27813,27814,27815,27816,27817,27818,27819,27820,27821,27822,27823,
27824,27825,27826,27827,27828,27829,27830,27831,27832,27833,27834,27835,
27836,27837,27838,27839,27840,27841,27842,27843,27844,27845,27846,27847,
27848,27849,27850,27851,27852,27853,27854,27855,27856,27857,27858,27859,
27860,27861,27862,27863,27864,27865,27866,27867,27868,27869,27870,27871,
27872,27873,27874,27875,27876,27877,27878,27879,27880,27881,27882,27883,
27884,27885,27886,27887,27888,27889,27890,27891,27892,27893,27894,27895,
27896,27897,27898,27899,27900,27901,27902,27903,27904,27905,27906,27907,
27908,27909,27910,27911,27912,27913,27914,27915,27916,27917,27918,27919,
27920,27921,27922,27923,27924,27925,27926,27927,27928,27929,27930,27931,
27932,27933,27934,27935,27936,27937,27938,27939,27940,27941,27942,27943,
27944,27945,27946,27947,27948,27949,27950,27951,27952,27953,27954,27955,
27956,27957,27958,27959,27960,27961,27962,27963,27964,27965,27966,27967,
27968,27969,27970,27971,27972,27973,27974,27975,27976,27977,27978,27979,
27980,27981,27982,27983,27984,27985,27986,27987,27988,27989,27990,27991,
27992,27993,27994,27995,27996,27997,27998,27999,28000,28001,28002,28003,
28004,28005,28006,28007,28008,28009,28010,28011,28012,28013,28014,28015,
28016,28017,28018,28019,28020,28021,28022,28023,28024,28025,28026,28027,
28028,28029,28030,28031,28032,28033,28034,28035,28036,28037,28038,28039,
28040,28041,28042,28043,28044,28045,28046,28047,28048,28049,28050,28051,
28052,28053,28054,28055,28056,28057,28058,28059,28060,28061,28062,28063,
28064,28065,28066,28067,28068,28069,28070,28071,28072,28073,28074,28075,
28076,28077,28078,28079,28080,28081,28082,28083,28084,28085,28086,28087,
28088,28089,28090,28091,28092,28093,28094,28095,28096,28097,28098,28099,
28100,28101,28102,28103,28104,28105,28106,28107,28108,28109,28110,28111,
28112,28113,28114,28115,28116,28117,28118,28119,28120,28121,28122,28123,
28124,28125,28126,28127,28128,28129,28130,28131,28132,28133,28134,28135,
28136,28137,28138,28139,28140,28141,28142,28143,28144,28145,28146,28147,
28148,28149,28150,28151,28152,28153,28154,28155,28156,28157,28158,28159,
28160,28161,28162,28163,28164,28165,28166,28167,28168,28169,28170,28171,
28172,28173,28174,28175,28176,28177,28178,28179,28180,28181,28182,28183,
28184,28185,28186,28187,28188,28189,28190,28191,28192,28193,28194,28195,
28196,28197,28198,28199,28200,28201,28202,28203,28204,28205,28206,28207,
28208,28209,28210,28211,28212,28213,28214,28215,28216,28217,28218,28219,
28220,28221,28222,28223,28224,28225,28226,28227,28228,28229,28230,28231,
28232,28233,28234,28235,28236,28237,28238,28239,28240,28241,28242,28243,
28244,28245,28246,28247,28248,28249,28250,28251,28252,28253,28254,28255,
28256,28257,28258,28259,28260,28261,28262,28263,28264,28265,28266,28267,
28268,28269,28270,28271,28272,28273,28274,28275,28276,28277,28278,28279,
28280,28281,28282,28283,28284,28285,28286,28287,28288,28289,28290,28291,
28292,28293,28294,28295,28296,28297,28298,28299,28300,28301,28302,28303,
28304,28305,28306,28307,28308,28309,28310,28311,28312,28313,28314,28315,
28316,28317,28318,28319,28320,28321,28322,28323,28324,28325,28326,28327,
28328,28329,28330,28331,28332,28333,28334,28335,28336,28337,28338,28339,
28340,28341,28342,28343,28344,28345,28346,28347,28348,28349,28350,28351,
28352,28353,28354,28355,28356,28357,28358,28359,28360,28361,28362,28363,
28364,28365,28366,28367,28368,28369,28370,28371,28372,28373,28374,28375,
28376,28377,28378,28379,28380,28381,28382,28383,28384,28385,28386,28387,
28388,28389,28390,28391,28392,28393,28394,28395,28396,28397,28398,28399,
28400,28401,28402,28403,28404,28405,28406,28407,28408,28409,28410,28411,
28412,28413,28414,28415,28416,28417,28418,28419,28420,28421,28422,28423,
28424,28425,28426,28427,28428,28429,28430,28431,28432,28433,28434,28435,
28436,28437,28438,28439,28440,28441,28442,28443,28444,28445,28446,28447,
28448,28449,28450,28451,28452,28453,28454,28455,28456,28457,28458,28459,
28460,28461,28462,28463,28464,28465,28466,28467,28468,28469,28470,28471,
28472,28473,28474,28475,28476,28477,28478,28479,28480,28481,28482,28483,
28484,28485,28486,28487,28488,28489,28490,28491,28492,28493,28494,28495,
28496,28497,28498,28499,28500,28501,28502,28503,28504,28505,28506,28507,
28508,28509,28510,28511,28512,28513,28514,28515,28516,28517,28518,28519,
28520,28521,28522,28523,28524,28525,28526,28527,28528,28529,28530,28531,
28532,28533,28534,28535,28536,28537,28538,28539,28540,28541,28542,28543,
28544,28545,28546,28547,28548,28549,28550,28551,28552,28553,28554,28555,
28556,28557,28558,28559,28560,28561,28562,28563,28564,28565,28566,28567,
28568,28569,28570,28571,28572,28573,28574,28575,28576,28577,28578,28579,
28580,28581,28582,28583,28584,28585,28586,28587,28588,28589,28590,28591,
28592,28593,28594,28595,28596,28597,28598,28599,28600,28601,28602,28603,
28604,28605,28606,28607,28608,28609,28610,28611,28612,28613,28614,28615,
28616,28617,28618,28619,28620,28621,28622,28623,28624,28625,28626,28627,
28628,28629,28630,28631,28632,28633,28634,28635,28636,28637,28638,28639,
28640,28641,28642,28643,28644,28645,28646,28647,28648,28649,28650,28651,
28652,28653,28654,28655,28656,28657,28658,28659,28660,28661,28662,28663,
28664,28665,28666,28667,28668,28669,28670,28671,28672,28673,28674,28675,
28676,28677,28678,28679,28680,28681,28682,28683,28684,28685,28686,28687,
28688,28689,28690,28691,28692,28693,28694,28695,28696,28697,28698,28699,
28700,28701,28702,28703,28704,28705,28706,28707,28708,28709,28710,28711,
28712,28713,28714,28715,28716,28717,28718,28719,28720,28721,28722,28723,
28724,28725,28726,28727,28728,28729,28730,28731,28732,28733,28734,28735,
28736,28737,28738,28739,28740,28741,28742,28743,28744,28745,28746,28747,
28748,28749,28750,28751,28752,28753,28754,28755,28756,28757,28758,28759,
28760,28761,28762,28763,28764,28765,28766,28767,28768,28769,28770,28771,
28772,28773,28774,28775,28776,28777,28778,28779,28780,28781,28782,28783,
28784,28785,28786,28787,28788,28789,28790,28791,28792,28793,28794,28795,
28796,28797,28798,28799,28800,28801,28802,28803,28804,28805,28806,28807,
28808,28809,28810,28811,28812,28813,28814,28815,28816,28817,28818,28819,
28820,28821,28822,28823,28824,28825,28826,28827,28828,28829,28830,28831,
28832,28833,28834,28835,28836,28837,28838,28839,28840,28841,28842,28843,
28844,28845,28846,28847,28848,28849,28850,28851,28852,28853,28854,28855,
28856,28857,28858,28859,28860,28861,28862,28863,28864,28865,28866,28867,
28868,28869,28870,28871,28872,28873,28874,28875,28876,28877,28878,28879,
28880,28881,28882,28883,28884,28885,28886,28887,28888,28889,28890,28891,
28892,28893,28894,28895,28896,28897,28898,28899,28900,28901,28902,28903,
28904,28905,28906,28907,28908,28909,28910,28911,28912,28913,28914,28915,
28916,28917,28918,28919,28920,28921,28922,28923,28924,28925,28926,28927,
28928,28929,28930,28931,28932,28933,28934,28935,28936,28937,28938,28939,
28940,28941,28942,28943,28944,28945,28946,28947,28948,28949,28950,28951,
28952,28953,28954,28955,28956,28957,28958,28959,28960,28961,28962,28963,
28964,28965,28966,28967,28968,28969,28970,28971,28972,28973,28974,28975,
28976,28977,28978,28979,28980,28981,28982,28983,28984,28985,28986,28987,
28988,28989,28990,28991,28992,28993,28994,28995,28996,28997,28998,28999,
29000,29001,29002,29003,29004,29005,29006,29007,29008,29009,29010,29011,
29012,29013,29014,29015,29016,29017,29018,29019,29020,29021,29022,29023,
29024,29025,29026,29027,29028,29029,29030,29031,29032,29033,29034,29035,
29036,29037,29038,29039,29040,29041,29042,29043,29044,29045,29046,29047,
29048,29049,29050,29051,29052,29053,29054,29055,29056,29057,29058,29059,
29060,29061,29062,29063,29064,29065,29066,29067,29068,29069,29070,29071,
29072,29073,29074,29075,29076,29077,29078,29079,29080,29081,29082,29083,
29084,29085,29086,29087,29088,29089,29090,29091,29092,29093,29094,29095,
29096,29097,29098,29099,29100,29101,29102,29103,29104,29105,29106,29107,
29108,29109,29110,29111,29112,29113,29114,29115,29116,29117,29118,29119,
29120,29121,29122,29123,29124,29125,29126,29127,29128,29129,29130,29131,
29132,29133,29134,29135,29136,29137,29138,29139,29140,29141,29142,29143,
29144,29145,29146,29147,29148,29149,29150,29151,29152,29153,29154,29155,
29156,29157,29158,29159,29160,29161,29162,29163,29164,29165,29166,29167,
29168,29169,29170,29171,29172,29173,29174,29175,29176,29177,29178,29179,
29180,29181,29182,29183,29184,29185,29186,29187,29188,29189,29190,29191,
29192,29193,29194,29195,29196,29197,29198,29199,29200,29201,29202,29203,
29204,29205,29206,29207,29208,29209,29210,29211,29212,29213,29214,29215,
29216,29217,29218,29219,29220,29221,29222,29223,29224,29225,29226,29227,
29228,29229,29230,29231,29232,29233,29234,29235,29236,29237,29238,29239,
29240,29241,29242,29243,29244,29245,29246,29247,29248,29249,29250,29251,
29252,29253,29254,29255,29256,29257,29258,29259,29260,29261,29262,29263,
29264,29265,29266,29267,29268,29269,29270,29271,29272,29273,29274,29275,
29276,29277,29278,29279,29280,29281,29282,29283,29284,29285,29286,29287,
29288,29289,29290,29291,29292,29293,29294,29295,29296,29297,29298,29299,
29300,29301,29302,29303,29304,29305,29306,29307,29308,29309,29310,29311,
29312,29313,29314,29315,29316,29317,29318,29319,29320,29321,29322,29323,
29324,29325,29326,29327,29328,29329,29330,29331,29332,29333,29334,29335,
29336,29337,29338,29339,29340,29341,29342,29343,29344,29345,29346,29347,
29348,29349,29350,29351,29352,29353,29354,29355,29356,29357,29358,29359,
29360,29361,29362,29363,29364,29365,29366,29367,29368,29369,29370,29371,
29372,29373,29374,29375,29376,29377,29378,29379,29380,29381,29382,29383,
29384,29385,29386,29387,29388,29389,29390,29391,29392,29393,29394,29395,
29396,29397,29398,29399,29400,29401,29402,29403,29404,29405,29406,29407,
29408,29409,29410,29411,29412,29413,29414,29415,29416,29417,29418,29419,
29420,29421,29422,29423,29424,29425,29426,29427,29428,29429,29430,29431,
29432,29433,29434,29435,29436,29437,29438,29439,29440,29441,29442,29443,
29444,29445,29446,29447,29448,29449,29450,29451,29452,29453,29454,29455,
29456,29457,29458,29459,29460,29461,29462,29463,29464,29465,29466,29467,
29468,29469,29470,29471,29472,29473,29474,29475,29476,29477,29478,29479,
29480,29481,29482,29483,29484,29485,29486,29487,29488,29489,29490,29491,
29492,29493,29494,29495,29496,29497,29498,29499,29500,29501,29502,29503,
29504,29505,29506,29507,29508,29509,29510,29511,29512,29513,29514,29515,
29516,29517,29518,29519,29520,29521,29522,29523,29524,29525,29526,29527,
29528,29529,29530,29531,29532,29533,29534,29535,29536,29537,29538,29539,
29540,29541,29542,29543,29544,29545,29546,29547,29548,29549,29550,29551,
29552,29553,29554,29555,29556,29557,29558,29559,29560,29561,29562,29563,
29564,29565,29566,29567,29568,29569,29570,29571,29572,29573,29574,29575,
29576,29577,29578,29579,29580,29581,29582,29583,29584,29585,29586,29587,
29588,29589,29590,29591,29592,29593,29594,29595,29596,29597,29598,29599,
29600,29601,29602,29603,29604,29605,29606,29607,29608,29609,29610,29611,
29612,29613,29614,29615,29616,29617,29618,29619,29620,29621,29622,29623,
29624,29625,29626,29627,29628,29629,29630,29631,29632,29633,29634,29635,
29636,29637,29638,29639,29640,29641,29642,29643,29644,29645,29646,29647,
29648,29649,29650,29651,29652,29653,29654,29655,29656,29657,29658,29659,
29660,29661,29662,29663,29664,29665,29666,29667,29668,29669,29670,29671,
29672,29673,29674,29675,29676,29677,29678,29679,29680,29681,29682,29683,
29684,29685,29686,29687,29688,29689,29690,29691,29692,29693,29694,29695,
29696,29697,29698,29699,29700,29701,29702,29703,29704,29705,29706,29707,
29708,29709,29710,29711,29712,29713,29714,29715,29716,29717,29718,29719,
29720,29721,29722,29723,29724,29725,29726,29727,29728,29729,29730,29731,
29732,29733,29734,29735,29736,29737,29738,29739,29740,29741,29742,29743,
29744,29745,29746,29747,29748,29749,29750,29751,29752,29753,29754,29755,
29756,29757,29758,29759,29760,29761,29762,29763,29764,29765,29766,29767,
29768,29769,29770,29771,29772,29773,29774,29775,29776,29777,29778,29779,
29780,29781,29782,29783,29784,29785,29786,29787,29788,29789,29790,29791,
29792,29793,29794,29795,29796,29797,29798,29799,29800,29801,29802,29803,
29804,29805,29806,29807,29808,29809,29810,29811,29812,29813,29814,29815,
29816,29817,29818,29819,29820,29821,29822,29823,29824,29825,29826,29827,
29828,29829,29830,29831,29832,29833,29834,29835,29836,29837,29838,29839,
29840,29841,29842,29843,29844,29845,29846,29847,29848,29849,29850,29851,
29852,29853,29854,29855,29856,29857,29858,29859,29860,29861,29862,29863,
29864,29865,29866,29867,29868,29869,29870,29871,29872,29873,29874,29875,
29876,29877,29878,29879,29880,29881,29882,29883,29884,29885,29886,29887,
29888,29889,29890,29891,29892,29893,29894,29895,29896,29897,29898,29899,
29900,29901,29902,29903,29904,29905,29906,29907,29908,29909,29910,29911,
29912,29913,29914,29915,29916,29917,29918,29919,29920,29921,29922,29923,
29924,29925,29926,29927,29928,29929,29930,29931,29932,29933,29934,29935,
29936,29937,29938,29939,29940,29941,29942,29943,29944,29945,29946,29947,
29948,29949,29950,29951,29952,29953,29954,29955,29956,29957,29958,29959,
29960,29961,29962,29963,29964,29965,29966,29967,29968,29969,29970,29971,
29972,29973,29974,29975,29976,29977,29978,29979,29980,29981,29982,29983,
29984,29985,29986,29987,29988,29989,29990,29991,29992,29993,29994,29995,
29996,29997,29998,29999,30000,30001,30002,30003,30004,30005,30006,30007,
30008,30009,30010,30011,30012,30013,30014,30015,30016,30017,30018,30019,
30020,30021,30022,30023,30024,30025,30026,30027,30028,30029,30030,30031,
30032,30033,30034,30035,30036,30037,30038,30039,30040,30041,30042,30043,
30044,30045,30046,30047,30048,30049,30050,30051,30052,30053,30054,30055,
30056,30057,30058,30059,30060,30061,30062,30063,30064,30065,30066,30067,
30068,30069,30070,30071,30072,30073,30074,30075,30076,30077,30078,30079,
30080,30081,30082,30083,30084,30085,30086,30087,30088,30089,30090,30091,
30092,30093,30094,30095,30096,30097,30098,30099,30100,30101,30102,30103,
30104,30105,30106,30107,30108,30109,30110,30111,30112,30113,30114,30115,
30116,30117,30118,30119,30120,30121,30122,30123,30124,30125,30126,30127,
30128,30129,30130,30131,30132,30133,30134,30135,30136,30137,30138,30139,
30140,30141,30142,30143,30144,30145,30146,30147,30148,30149,30150,30151,
30152,30153,30154,30155,30156,30157,30158,30159,30160,30161,30162,30163,
30164,30165,30166,30167,30168,30169,30170,30171,30172,30173,30174,30175,
30176,30177,30178,30179,30180,30181,30182,30183,30184,30185,30186,30187,
30188,30189,30190,30191,30192,30193,30194,30195,30196,30197,30198,30199,
30200,30201,30202,30203,30204,30205,30206,30207,30208,30209,30210,30211,
30212,30213,30214,30215,30216,30217,30218,30219,30220,30221,30222,30223,
30224,30225,30226,30227,30228,30229,30230,30231,30232,30233,30234,30235,
30236,30237,30238,30239,30240,30241,30242,30243,30244,30245,30246,30247,
30248,30249,30250,30251,30252,30253,30254,30255,30256,30257,30258,30259,
30260,30261,30262,30263,30264,30265,30266,30267,30268,30269,30270,30271,
30272,30273,30274,30275,30276,30277,30278,30279,30280,30281,30282,30283,
30284,30285,30286,30287,30288,30289,30290,30291,30292,30293,30294,30295,
30296,30297,30298,30299,30300,30301,30302,30303,30304,30305,30306,30307,
30308,30309,30310,30311,30312,30313,30314,30315,30316,30317,30318,30319,
30320,30321,30322,30323,30324,30325,30326,30327,30328,30329,30330,30331,
30332,30333,30334,30335,30336,30337,30338,30339,30340,30341,30342,30343,
30344,30345,30346,30347,30348,30349,30350,30351,30352,30353,30354,30355,
30356,30357,30358,30359,30360,30361,30362,30363,30364,30365,30366,30367,
30368,30369,30370,30371,30372,30373,30374,30375,30376,30377,30378,30379,
30380,30381,30382,30383,30384,30385,30386,30387,30388,30389,30390,30391,
30392,30393,30394,30395,30396,30397,30398,30399,30400,30401,30402,30403,
30404,30405,30406,30407,30408,30409,30410,30411,30412,30413,30414,30415,
30416,30417,30418,30419,30420,30421,30422,30423,30424,30425,30426,30427,
30428,30429,30430,30431,30432,30433,30434,30435,30436,30437,30438,30439,
30440,30441,30442,30443,30444,30445,30446,30447,30448,30449,30450,30451,
30452,30453,30454,30455,30456,30457,30458,30459,30460,30461,30462,30463,
30464,30465,30466,30467,30468,30469,30470,30471,30472,30473,30474,30475,
30476,30477,30478,30479,30480,30481,30482,30483,30484,30485,30486,30487,
30488,30489,30490,30491,30492,30493,30494,30495,30496,30497,30498,30499,
30500,30501,30502,30503,30504,30505,30506,30507,30508,30509,30510,30511,
30512,30513,30514,30515,30516,30517,30518,30519,30520,30521,30522,30523,
30524,30525,30526,30527,30528,30529,30530,30531,30532,30533,30534,30535,
30536,30537,30538,30539,30540,30541,30542,30543,30544,30545,30546,30547,
30548,30549,30550,30551,30552,30553,30554,30555,30556,30557,30558,30559,
30560,30561,30562,30563,30564,30565,30566,30567,30568,30569,30570,30571,
30572,30573,30574,30575,30576,30577,30578,30579,30580,30581,30582,30583,
30584,30585,30586,30587,30588,30589,30590,30591,30592,30593,30594,30595,
30596,30597,30598,30599,30600,30601,30602,30603,30604,30605,30606,30607,
30608,30609,30610,30611,30612,30613,30614,30615,30616,30617,30618,30619,
30620,30621,30622,30623,30624,30625,30626,30627,30628,30629,30630,30631,
30632,30633,30634,30635,30636,30637,30638,30639,30640,30641,30642,30643,
30644,30645,30646,30647,30648,30649,30650,30651,30652,30653,30654,30655,
30656,30657,30658,30659,30660,30661,30662,30663,30664,30665,30666,30667,
30668,30669,30670,30671,30672,30673,30674,30675,30676,30677,30678,30679,
30680,30681,30682,30683,30684,30685,30686,30687,30688,30689,30690,30691,
30692,30693,30694,30695,30696,30697,30698,30699,30700,30701,30702,30703,
30704,30705,30706,30707,30708,30709,30710,30711,30712,30713,30714,30715,
30716,30717,30718,30719,30720,30721,30722,30723,30724,30725,30726,30727,
30728,30729,30730,30731,30732,30733,30734,30735,30736,30737,30738,30739,
30740,30741,30742,30743,30744,30745,30746,30747,30748,30749,30750,30751,
30752,30753,30754,30755,30756,30757,30758,30759,30760,30761,30762,30763,
30764,30765,30766,30767,30768,30769,30770,30771,30772,30773,30774,30775,
30776,30777,30778,30779,30780,30781,30782,30783,30784,30785,30786,30787,
30788,30789,30790,30791,30792,30793,30794,30795,30796,30797,30798,30799,
30800,30801,30802,30803,30804,30805,30806,30807,30808,30809,30810,30811,
30812,30813,30814,30815,30816,30817,30818,30819,30820,30821,30822,30823,
30824,30825,30826,30827,30828,30829,30830,30831,30832,30833,30834,30835,
30836,30837,30838,30839,30840,30841,30842,30843,30844,30845,30846,30847,
30848,30849,30850,30851,30852,30853,30854,30855,30856,30857,30858,30859,
30860,30861,30862,30863,30864,30865,30866,30867,30868,30869,30870,30871,
30872,30873,30874,30875,30876,30877,30878,30879,30880,30881,30882,30883,
30884,30885,30886,30887,30888,30889,30890,30891,30892,30893,30894,30895,
30896,30897,30898,30899,30900,30901,30902,30903,30904,30905,30906,30907,
30908,30909,30910,30911,30912,30913,30914,30915,30916,30917,30918,30919,
30920,30921,30922,30923,30924,30925,30926,30927,30928,30929,30930,30931,
30932,30933,30934,30935,30936,30937,30938,30939,30940,30941,30942,30943,
30944,30945,30946,30947,30948,30949,30950,30951,30952,30953,30954,30955,
30956,30957,30958,30959,30960,30961,30962,30963,30964,30965,30966,30967,
30968,30969,30970,30971,30972,30973,30974,30975,30976,30977,30978,30979,
30980,30981,30982,30983,30984,30985,30986,30987,30988,30989,30990,30991,
30992,30993,30994,30995,30996,30997,30998,30999,31000,31001,31002,31003,
31004,31005,31006,31007,31008,31009,31010,31011,31012,31013,31014,31015,
31016,31017,31018,31019,31020,31021,31022,31023,31024,31025,31026,31027,
31028,31029,31030,31031,31032,31033,31034,31035,31036,31037,31038,31039,
31040,31041,31042,31043,31044,31045,31046,31047,31048,31049,31050,31051,
31052,31053,31054,31055,31056,31057,31058,31059,31060,31061,31062,31063,
31064,31065,31066,31067,31068,31069,31070,31071,31072,31073,31074,31075,
31076,31077,31078,31079,31080,31081,31082,31083,31084,31085,31086,31087,
31088,31089,31090,31091,31092,31093,31094,31095,31096,31097,31098,31099,
31100,31101,31102,31103,31104,31105,31106,31107,31108,31109,31110,31111,
31112,31113,31114,31115,31116,31117,31118,31119,31120,31121,31122,31123,
31124,31125,31126,31127,31128,31129,31130,31131,31132,31133,31134,31135,
31136,31137,31138,31139,31140,31141,31142,31143,31144,31145,31146,31147,
31148,31149,31150,31151,31152,31153,31154,31155,31156,31157,31158,31159,
31160,31161,31162,31163,31164,31165,31166,31167,31168,31169,31170,31171,
31172,31173,31174,31175,31176,31177,31178,31179,31180,31181,31182,31183,
31184,31185,31186,31187,31188,31189,31190,31191,31192,31193,31194,31195,
31196,31197,31198,31199,31200,31201,31202,31203,31204,31205,31206,31207,
31208,31209,31210,31211,31212,31213,31214,31215,31216,31217,31218,31219,
31220,31221,31222,31223,31224,31225,31226,31227,31228,31229,31230,31231,
31232,31233,31234,31235,31236,31237,31238,31239,31240,31241,31242,31243,
31244,31245,31246,31247,31248,31249,31250,31251,31252,31253,31254,31255,
31256,31257,31258,31259,31260,31261,31262,31263,31264,31265,31266,31267,
31268,31269,31270,31271,31272,31273,31274,31275,31276,31277,31278,31279,
31280,31281,31282,31283,31284,31285,31286,31287,31288,31289,31290,31291,
31292,31293,31294,31295,31296,31297,31298,31299,31300,31301,31302,31303,
31304,31305,31306,31307,31308,31309,31310,31311,31312,31313,31314,31315,
31316,31317,31318,31319,31320,31321,31322,31323,31324,31325,31326,31327,
31328,31329,31330,31331,31332,31333,31334,31335,31336,31337,31338,31339,
31340,31341,31342,31343,31344,31345,31346,31347,31348,31349,31350,31351,
31352,31353,31354,31355,31356,31357,31358,31359,31360,31361,31362,31363,
31364,31365,31366,31367,31368,31369,31370,31371,31372,31373,31374,31375,
31376,31377,31378,31379,31380,31381,31382,31383,31384,31385,31386,31387,
31388,31389,31390,31391,31392,31393,31394,31395,31396,31397,31398,31399,
31400,31401,31402,31403,31404,31405,31406,31407,31408,31409,31410,31411,
31412,31413,31414,31415,31416,31417,31418,31419,31420,31421,31422,31423,
31424,31425,31426,31427,31428,31429,31430,31431,31432,31433,31434,31435,
31436,31437,31438,31439,31440,31441,31442,31443,31444,31445,31446,31447,
31448,31449,31450,31451,31452,31453,31454,31455,31456,31457,31458,31459,
31460,31461,31462,31463,31464,31465,31466,31467,31468,31469,31470,31471,
31472,31473,31474,31475,31476,31477,31478,31479,31480,31481,31482,31483,
31484,31485,31486,31487,31488,31489,31490,31491,31492,31493,31494,31495,
31496,31497,31498,31499,31500,31501,31502,31503,31504,31505,31506,31507,
31508,31509,31510,31511,31512,31513,31514,31515,31516,31517,31518,31519,
31520,31521,31522,31523,31524,31525,31526,31527,31528,31529,31530,31531,
31532,31533,31534,31535,31536,31537,31538,31539,31540,31541,31542,31543,
31544,31545,31546,31547,31548,31549,31550,31551,31552,31553,31554,31555,
31556,31557,31558,31559,31560,31561,31562,31563,31564,31565,31566,31567,
31568,31569,31570,31571,31572,31573,31574,31575,31576,31577,31578,31579,
31580,31581,31582,31583,31584,31585,31586,31587,31588,31589,31590,31591,
31592,31593,31594,31595,31596,31597,31598,31599,31600,31601,31602,31603,
31604,31605,31606,31607,31608,31609,31610,31611,31612,31613,31614,31615,
31616,31617,31618,31619,31620,31621,31622,31623,31624,31625,31626,31627,
31628,31629,31630,31631,31632,31633,31634,31635,31636,31637,31638,31639,
31640,31641,31642,31643,31644,31645,31646,31647,31648,31649,31650,31651,
31652,31653,31654,31655,31656,31657,31658,31659,31660,31661,31662,31663,
31664,31665,31666,31667,31668,31669,31670,31671,31672,31673,31674,31675,
31676,31677,31678,31679,31680,31681,31682,31683,31684,31685,31686,31687,
31688,31689,31690,31691,31692,31693,31694,31695,31696,31697,31698,31699,
31700,31701,31702,31703,31704,31705,31706,31707,31708,31709,31710,31711,
31712,31713,31714,31715,31716,31717,31718,31719,31720,31721,31722,31723,
31724,31725,31726,31727,31728,31729,31730,31731,31732,31733,31734,31735,
31736,31737,31738,31739,31740,31741,31742,31743,31744,31745,31746,31747,
31748,31749,31750,31751,31752,31753,31754,31755,31756,31757,31758,31759,
31760,31761,31762,31763,31764,31765,31766,31767,31768,31769,31770,31771,
31772,31773,31774,31775,31776,31777,31778,31779,31780,31781,31782,31783,
31784,31785,31786,31787,31788,31789,31790,31791,31792,31793,31794,31795,
31796,31797,31798,31799,31800,31801,31802,31803,31804,31805,31806,31807,
31808,31809,31810,31811,31812,31813,31814,31815,31816,31817,31818,31819,
31820,31821,31822,31823,31824,31825,31826,31827,31828,31829,31830,31831,
31832,31833,31834,31835,31836,31837,31838,31839,31840,31841,31842,31843,
31844,31845,31846,31847,31848,31849,31850,31851,31852,31853,31854,31855,
31856,31857,31858,31859,31860,31861,31862,31863,31864,31865,31866,31867,
31868,31869,31870,31871,31872,31873,31874,31875,31876,31877,31878,31879,
31880,31881,31882,31883,31884,31885,31886,31887,31888,31889,31890,31891,
31892,31893,31894,31895,31896,31897,31898,31899,31900,31901,31902,31903,
31904,31905,31906,31907,31908,31909,31910,31911,31912,31913,31914,31915,
31916,31917,31918,31919,31920,31921,31922,31923,31924,31925,31926,31927,
31928,31929,31930,31931,31932,31933,31934,31935,31936,31937,31938,31939,
31940,31941,31942,31943,31944,31945,31946,31947,31948,31949,31950,31951,
31952,31953,31954,31955,31956,31957,31958,31959,31960,31961,31962,31963,
31964,31965,31966,31967,31968,31969,31970,31971,31972,31973,31974,31975,
31976,31977,31978,31979,31980,31981,31982,31983,31984,31985,31986,31987,
31988,31989,31990,31991,31992,31993,31994,31995,31996,31997,31998,31999,
32000,32001,32002,32003,32004,32005,32006,32007,32008,32009,32010,32011,
32012,32013,32014,32015,32016,32017,32018,32019,32020,32021,32022,32023,
32024,32025,32026,32027,32028,32029,32030,32031,32032,32033,32034,32035,
32036,32037,32038,32039,32040,32041,32042,32043,32044,32045,32046,32047,
32048,32049,32050,32051,32052,32053,32054,32055,32056,32057,32058,32059,
32060,32061,32062,32063,32064,32065,32066,32067,32068,32069,32070,32071,
32072,32073,32074,32075,32076,32077,32078,32079,32080,32081,32082,32083,
32084,32085,32086,32087,32088,32089,32090,32091,32092,32093,32094,32095,
32096,32097,32098,32099,32100,32101,32102,32103,32104,32105,32106,32107,
32108,32109,32110,32111,32112,32113,32114,32115,32116,32117,32118,32119,
32120,32121,32122,32123,32124,32125,32126,32127,32128,32129,32130,32131,
32132,32133,32134,32135,32136,32137,32138,32139,32140,32141,32142,32143,
32144,32145,32146,32147,32148,32149,32150,32151,32152,32153,32154,32155,
32156,32157,32158,32159,32160,32161,32162,32163,32164,32165,32166,32167,
32168,32169,32170,32171,32172,32173,32174,32175,32176,32177,32178,32179,
32180,32181,32182,32183,32184,32185,32186,32187,32188,32189,32190,32191,
32192,32193,32194,32195,32196,32197,32198,32199,32200,32201,32202,32203,
32204,32205,32206,32207,32208,32209,32210,32211,32212,32213,32214,32215,
32216,32217,32218,32219,32220,32221,32222,32223,32224,32225,32226,32227,
32228,32229,32230,32231,32232,32233,32234,32235,32236,32237,32238,32239,
32240,32241,32242,32243,32244,32245,32246,32247,32248,32249,32250,32251,
32252,32253,32254,32255,32256,32257,32258,32259,32260,32261,32262,32263,
32264,32265,32266,32267,32268,32269,32270,32271,32272,32273,32274,32275,
32276,32277,32278,32279,32280,32281,32282,32283,32284,32285,32286,32287,
32288,32289,32290,32291,32292,32293,32294,32295,32296,32297,32298,32299,
32300,32301,32302,32303,32304,32305,32306,32307,32308,32309,32310,32311,
32312,32313,32314,32315,32316,32317,32318,32319,32320,32321,32322,32323,
32324,32325,32326,32327,32328,32329,32330,32331,32332,32333,32334,32335,
32336,32337,32338,32339,32340,32341,32342,32343,32344,32345,32346,32347,
32348,32349,32350,32351,32352,32353,32354,32355,32356,32357,32358,32359,
32360,32361,32362,32363,32364,32365,32366,32367,32368,32369,32370,32371,
32372,32373,32374,32375,32376,32377,32378,32379,32380,32381,32382,32383,
32384,32385,32386,32387,32388,32389,32390,32391,32392,32393,32394,32395,
32396,32397,32398,32399,32400,32401,32402,32403,32404,32405,32406,32407,
32408,32409,32410,32411,32412,32413,32414,32415,32416,32417,32418,32419,
32420,32421,32422,32423,32424,32425,32426,32427,32428,32429,32430,32431,
32432,32433,32434,32435,32436,32437,32438,32439,32440,32441,32442,32443,
32444,32445,32446,32447,32448,32449,32450,32451,32452,32453,32454,32455,
32456,32457,32458,32459,32460,32461,32462,32463,32464,32465,32466,32467,
32468,32469,32470,32471,32472,32473,32474,32475,32476,32477,32478,32479,
32480,32481,32482,32483,32484,32485,32486,32487,32488,32489,32490,32491,
32492,32493,32494,32495,32496,32497,32498,32499,32500,32501,32502,32503,
32504,32505,32506,32507,32508,32509,32510,32511,32512,32513,32514,32515,
32516,32517,32518,32519,32520,32521,32522,32523,32524,32525,32526,32527,
32528,32529,32530,32531,32532,32533,32534,32535,32536,32537,32538,32539,
32540,32541,32542,32543,32544,32545,32546,32547,32548,32549,32550,32551,
32552,32553,32554,32555,32556,32557,32558,32559,32560,32561,32562,32563,
32564,32565,32566,32567,32568,32569,32570,32571,32572,32573,32574,32575,
32576,32577,32578,32579,32580,32581,32582,32583,32584,32585,32586,32587,
32588,32589,32590,32591,32592,32593,32594,32595,32596,32597,32598,32599,
32600,32601,32602,32603,32604,32605,32606,32607,32608,32609,32610,32611,
32612,32613,32614,32615,32616,32617,32618,32619,32620,32621,32622,32623,
32624,32625,32626,32627,32628,32629,32630,32631,32632,32633,32634,32635,
32636,32637,32638,32639,32640,32641,32642,32643,32644,32645,32646,32647,
32648,32649,32650,32651,32652,32653,32654,32655,32656,32657,32658,32659,
32660,32661,32662,32663,32664,32665,32666,32667,32668,32669,32670,32671,
32672,32673,32674,32675,32676,32677,32678,32679,32680,32681,32682,32683,
32684,32685,32686,32687,32688,32689,32690,32691,32692,32693,32694,32695,
32696,32697,32698,32699,32700,32701,32702,32703,32704,32705,32706,32707,
32708,32709,32710,32711,32712,32713,32714,32715,32716,32717,32718,32719,
32720,32721,32722,32723,32724,32725,32726,32727,32728,32729,32730,32731,
32732,32733,32734,32735,32736,32737,32738,32739,32740,32741,32742,32743,
32744,32745,32746,32747,32748,32749,32750,32751,32752,32753,32754,32755,
32756,32757,32758,32759,32760,32761,32762,32763,32764,32765,32766,32767,
32768L,32769L,32770L,32771L,32772L,32773L,32774L,32775L,32776L,32777L,
32778L,32779L,32780L,32781L,32782L,32783L,32784L,32785L,32786L,32787L,
32788L,32789L,32790L,32791L,32792L,32793L,32794L,32795L,32796L,32797L,
32798L,32799L,32800L,32801L,32802L,32803L,32804L,32805L,32806L,32807L,
32808L,32809L,32810L,32811L,32812L,32813L,32814L,32815L,32816L,32817L,
32818L,32819L,32820L,32821L,32822L,32823L,32824L,32825L,32826L,32827L,
32828L,32829L,32830L,32831L,32832L,32833L,32834L,32835L,32836L,32837L,
32838L,32839L,32840L,32841L,32842L,32843L,32844L,32845L,32846L,32847L,
32848L,32849L,32850L,32851L,32852L,32853L,32854L,32855L,32856L,32857L,
32858L,32859L,32860L,32861L,32862L,32863L,32864L,32865L,32866L,32867L,
32868L,32869L,32870L,32871L,32872L,32873L,32874L,32875L,32876L,32877L,
32878L,32879L,32880L,32881L,32882L,32883L,32884L,32885L,32886L,32887L,
32888L,32889L,32890L,32891L,32892L,32893L,32894L,32895L,32896L,32897L,
32898L,32899L,32900L,32901L,32902L,32903L,32904L,32905L,32906L,32907L,
32908L,32909L,32910L,32911L,32912L,32913L,32914L,32915L,32916L,32917L,
32918L,32919L,32920L,32921L,32922L,32923L,32924L,32925L,32926L,32927L,
32928L,32929L,32930L,32931L,32932L,32933L,32934L,32935L,32936L,32937L,
32938L,32939L,32940L,32941L,32942L,32943L,32944L,32945L,32946L,32947L,
32948L,32949L,32950L,32951L,32952L,32953L,32954L,32955L,32956L,32957L,
32958L,32959L,32960L,32961L,32962L,32963L,32964L,32965L,32966L,32967L,
32968L,32969L,32970L,32971L,32972L,32973L,32974L,32975L,32976L,32977L,
32978L,32979L,32980L,32981L,32982L,32983L,32984L,32985L,32986L,32987L,
32988L,32989L,32990L,32991L,32992L,32993L,32994L,32995L,32996L,32997L,
32998L,32999L,33000L,33001L,33002L,33003L,33004L,33005L,33006L,33007L,
33008L,33009L,33010L,33011L,33012L,33013L,33014L,33015L,33016L,33017L,
33018L,33019L,33020L,33021L,33022L,33023L,33024L,33025L,33026L,33027L,
33028L,33029L,33030L,33031L,33032L,33033L,33034L,33035L,33036L,33037L,
33038L,33039L,33040L,33041L,33042L,33043L,33044L,33045L,33046L,33047L,
33048L,33049L,33050L,33051L,33052L,33053L,33054L,33055L,33056L,33057L,
33058L,33059L,33060L,33061L,33062L,33063L,33064L,33065L,33066L,33067L,
33068L,33069L,33070L,33071L,33072L,33073L,33074L,33075L,33076L,33077L,
33078L,33079L,33080L,33081L,33082L,33083L,33084L,33085L,33086L,33087L,
33088L,33089L,33090L,33091L,33092L,33093L,33094L,33095L,33096L,33097L,
33098L,33099L,33100L,33101L,33102L,33103L,33104L,33105L,33106L,33107L,
33108L,33109L,33110L,33111L,33112L,33113L,33114L,33115L,33116L,33117L,
33118L,33119L,33120L,33121L,33122L,33123L,33124L,33125L,33126L,33127L,
33128L,33129L,33130L,33131L,33132L,33133L,33134L,33135L,33136L,33137L,
33138L,33139L,33140L,33141L,33142L,33143L,33144L,33145L,33146L,33147L,
33148L,33149L,33150L,33151L,33152L,33153L,33154L,33155L,33156L,33157L,
33158L,33159L,33160L,33161L,33162L,33163L,33164L,33165L,33166L,33167L,
33168L,33169L,33170L,33171L,33172L,33173L,33174L,33175L,33176L,33177L,
33178L,33179L,33180L,33181L,33182L,33183L,33184L,33185L,33186L,33187L,
33188L,33189L,33190L,33191L,33192L,33193L,33194L,33195L,33196L,33197L,
33198L,33199L,33200L,33201L,33202L,33203L,33204L,33205L,33206L,33207L,
33208L,33209L,33210L,33211L,33212L,33213L,33214L,33215L,33216L,33217L,
33218L,33219L,33220L,33221L,33222L,33223L,33224L,33225L,33226L,33227L,
33228L,33229L,33230L,33231L,33232L,33233L,33234L,33235L,33236L,33237L,
33238L,33239L,33240L,33241L,33242L,33243L,33244L,33245L,33246L,33247L,
33248L,33249L,33250L,33251L,33252L,33253L,33254L,33255L,33256L,33257L,
33258L,33259L,33260L,33261L,33262L,33263L,33264L,33265L,33266L,33267L,
33268L,33269L,33270L,33271L,33272L,33273L,33274L,33275L,33276L,33277L,
33278L,33279L,33280L,33281L,33282L,33283L,33284L,33285L,33286L,33287L,
33288L,33289L,33290L,33291L,33292L,33293L,33294L,33295L,33296L,33297L,
33298L,33299L,33300L,33301L,33302L,33303L,33304L,33305L,33306L,33307L,
33308L,33309L,33310L,33311L,33312L,33313L,33314L,33315L,33316L,33317L,
33318L,33319L,33320L,33321L,33322L,33323L,33324L,33325L,33326L,33327L,
33328L,33329L,33330L,33331L,33332L,33333L,33334L,33335L,33336L,33337L,
33338L,33339L,33340L,33341L,33342L,33343L,33344L,33345L,33346L,33347L,
33348L,33349L,33350L,33351L,33352L,33353L,33354L,33355L,33356L,33357L,
33358L,33359L,33360L,33361L,33362L,33363L,33364L,33365L,33366L,33367L,
33368L,33369L,33370L,33371L,33372L,33373L,33374L,33375L,33376L,33377L,
33378L,33379L,33380L,33381L,33382L,33383L,33384L,33385L,33386L,33387L,
33388L,33389L,33390L,33391L,33392L,33393L,33394L,33395L,33396L,33397L,
33398L,33399L,33400L,33401L,33402L,33403L,33404L,33405L,33406L,33407L,
33408L,33409L,33410L,33411L,33412L,33413L,33414L,33415L,33416L,33417L,
33418L,33419L,33420L,33421L,33422L,33423L,33424L,33425L,33426L,33427L,
33428L,33429L,33430L,33431L,33432L,33433L,33434L,33435L,33436L,33437L,
33438L,33439L,33440L,33441L,33442L,33443L,33444L,33445L,33446L,33447L,
33448L,33449L,33450L,33451L,33452L,33453L,33454L,33455L,33456L,33457L,
33458L,33459L,33460L,33461L,33462L,33463L,33464L,33465L,33466L,33467L,
33468L,33469L,33470L,33471L,33472L,33473L,33474L,33475L,33476L,33477L,
33478L,33479L,33480L,33481L,33482L,33483L,33484L,33485L,33486L,33487L,
33488L,33489L,33490L,33491L,33492L,33493L,33494L,33495L,33496L,33497L,
33498L,33499L,33500L,33501L,33502L,33503L,33504L,33505L,33506L,33507L,
33508L,33509L,33510L,33511L,33512L,33513L,33514L,33515L,33516L,33517L,
33518L,33519L,33520L,33521L,33522L,33523L,33524L,33525L,33526L,33527L,
33528L,33529L,33530L,33531L,33532L,33533L,33534L,33535L,33536L,33537L,
33538L,33539L,33540L,33541L,33542L,33543L,33544L,33545L,33546L,33547L,
33548L,33549L,33550L,33551L,33552L,33553L,33554L,33555L,33556L,33557L,
33558L,33559L,33560L,33561L,33562L,33563L,33564L,33565L,33566L,33567L,
33568L,33569L,33570L,33571L,33572L,33573L,33574L,33575L,33576L,33577L,
33578L,33579L,33580L,33581L,33582L,33583L,33584L,33585L,33586L,33587L,
33588L,33589L,33590L,33591L,33592L,33593L,33594L,33595L,33596L,33597L,
33598L,33599L,33600L,33601L,33602L,33603L,33604L,33605L,33606L,33607L,
33608L,33609L,33610L,33611L,33612L,33613L,33614L,33615L,33616L,33617L,
33618L,33619L,33620L,33621L,33622L,33623L,33624L,33625L,33626L,33627L,
33628L,33629L,33630L,33631L,33632L,33633L,33634L,33635L,33636L,33637L,
33638L,33639L,33640L,33641L,33642L,33643L,33644L,33645L,33646L,33647L,
33648L,33649L,33650L,33651L,33652L,33653L,33654L,33655L,33656L,33657L,
33658L,33659L,33660L,33661L,33662L,33663L,33664L,33665L,33666L,33667L,
33668L,33669L,33670L,33671L,33672L,33673L,33674L,33675L,33676L,33677L,
33678L,33679L,33680L,33681L,33682L,33683L,33684L,33685L,33686L,33687L,
33688L,33689L,33690L,33691L,33692L,33693L,33694L,33695L,33696L,33697L,
33698L,33699L,33700L,33701L,33702L,33703L,33704L,33705L,33706L,33707L,
33708L,33709L,33710L,33711L,33712L,33713L,33714L,33715L,33716L,33717L,
33718L,33719L,33720L,33721L,33722L,33723L,33724L,33725L,33726L,33727L,
33728L,33729L,33730L,33731L,33732L,33733L,33734L,33735L,33736L,33737L,
33738L,33739L,33740L,33741L,33742L,33743L,33744L,33745L,33746L,33747L,
33748L,33749L,33750L,33751L,33752L,33753L,33754L,33755L,33756L,33757L,
33758L,33759L,33760L,33761L,33762L,33763L,33764L,33765L,33766L,33767L,
33768L,33769L,33770L,33771L,33772L,33773L,33774L,33775L,33776L,33777L,
33778L,33779L,33780L,33781L,33782L,33783L,33784L,33785L,33786L,33787L,
33788L,33789L,33790L,33791L,33792L,33793L,33794L,33795L,33796L,33797L,
33798L,33799L,33800L,33801L,33802L,33803L,33804L,33805L,33806L,33807L,
33808L,33809L,33810L,33811L,33812L,33813L,33814L,33815L,33816L,33817L,
33818L,33819L,33820L,33821L,33822L,33823L,33824L,33825L,33826L,33827L,
33828L,33829L,33830L,33831L,33832L,33833L,33834L,33835L,33836L,33837L,
33838L,33839L,33840L,33841L,33842L,33843L,33844L,33845L,33846L,33847L,
33848L,33849L,33850L,33851L,33852L,33853L,33854L,33855L,33856L,33857L,
33858L,33859L,33860L,33861L,33862L,33863L,33864L,33865L,33866L,33867L,
33868L,33869L,33870L,33871L,33872L,33873L,33874L,33875L,33876L,33877L,
33878L,33879L,33880L,33881L,33882L,33883L,33884L,33885L,33886L,33887L,
33888L,33889L,33890L,33891L,33892L,33893L,33894L,33895L,33896L,33897L,
33898L,33899L,33900L,33901L,33902L,33903L,33904L,33905L,33906L,33907L,
33908L,33909L,33910L,33911L,33912L,33913L,33914L,33915L,33916L,33917L,
33918L,33919L,33920L,33921L,33922L,33923L,33924L,33925L,33926L,33927L,
33928L,33929L,33930L,33931L,33932L,33933L,33934L,33935L,33936L,33937L,
33938L,33939L,33940L,33941L,33942L,33943L,33944L,33945L,33946L,33947L,
33948L,33949L,33950L,33951L,33952L,33953L,33954L,33955L,33956L,33957L,
33958L,33959L,33960L,33961L,33962L,33963L,33964L,33965L,33966L,33967L,
33968L,33969L,33970L,33971L,33972L,33973L,33974L,33975L,33976L,33977L,
33978L,33979L,33980L,33981L,33982L,33983L,33984L,33985L,33986L,33987L,
33988L,33989L,33990L,33991L,33992L,33993L,33994L,33995L,33996L,33997L,
33998L,33999L,34000L,34001L,34002L,34003L,34004L,34005L,34006L,34007L,
34008L,34009L,34010L,34011L,34012L,34013L,34014L,34015L,34016L,34017L,
34018L,34019L,34020L,34021L,34022L,34023L,34024L,34025L,34026L,34027L,
34028L,34029L,34030L,34031L,34032L,34033L,34034L,34035L,34036L,34037L,
34038L,34039L,34040L,34041L,34042L,34043L,34044L,34045L,34046L,34047L,
34048L,34049L,34050L,34051L,34052L,34053L,34054L,34055L,34056L,34057L,
34058L,34059L,34060L,34061L,34062L,34063L,34064L,34065L,34066L,34067L,
34068L,34069L,34070L,34071L,34072L,34073L,34074L,34075L,34076L,34077L,
34078L,34079L,34080L,34081L,34082L,34083L,34084L,34085L,34086L,34087L,
34088L,34089L,34090L,34091L,34092L,34093L,34094L,34095L,34096L,34097L,
34098L,34099L,34100L,34101L,34102L,34103L,34104L,34105L,34106L,34107L,
34108L,34109L,34110L,34111L,34112L,34113L,34114L,34115L,34116L,34117L,
34118L,34119L,34120L,34121L,34122L,34123L,34124L,34125L,34126L,34127L,
34128L,34129L,34130L,34131L,34132L,34133L,34134L,34135L,34136L,34137L,
34138L,34139L,34140L,34141L,34142L,34143L,34144L,34145L,34146L,34147L,
34148L,34149L,34150L,34151L,34152L,34153L,34154L,34155L,34156L,34157L,
34158L,34159L,34160L,34161L,34162L,34163L,34164L,34165L,34166L,34167L,
34168L,34169L,34170L,34171L,34172L,34173L,34174L,34175L,34176L,34177L,
34178L,34179L,34180L,34181L,34182L,34183L,34184L,34185L,34186L,34187L,
34188L,34189L,34190L,34191L,34192L,34193L,34194L,34195L,34196L,34197L,
34198L,34199L,34200L,34201L,34202L,34203L,34204L,34205L,34206L,34207L,
34208L,34209L,34210L,34211L,34212L,34213L,34214L,34215L,34216L,34217L,
34218L,34219L,34220L,34221L,34222L,34223L,34224L,34225L,34226L,34227L,
34228L,34229L,34230L,34231L,34232L,34233L,34234L,34235L,34236L,34237L,
34238L,34239L,34240L,34241L,34242L,34243L,34244L,34245L,34246L,34247L,
34248L,34249L,34250L,34251L,34252L,34253L,34254L,34255L,34256L,34257L,
34258L,34259L,34260L,34261L,34262L,34263L,34264L,34265L,34266L,34267L,
34268L,34269L,34270L,34271L,34272L,34273L,34274L,34275L,34276L,34277L,
34278L,34279L,34280L,34281L,34282L,34283L,34284L,34285L,34286L,34287L,
34288L,34289L,34290L,34291L,34292L,34293L,34294L,34295L,34296L,34297L,
34298L,34299L,34300L,34301L,34302L,34303L,34304L,34305L,34306L,34307L,
34308L,34309L,34310L,34311L,34312L,34313L,34314L,34315L,34316L,34317L,
34318L,34319L,34320L,34321L,34322L,34323L,34324L,34325L,34326L,34327L,
34328L,34329L,34330L,34331L,34332L,34333L,34334L,34335L,34336L,34337L,
34338L,34339L,34340L,34341L,34342L,34343L,34344L,34345L,34346L,34347L,
34348L,34349L,34350L,34351L,34352L,34353L,34354L,34355L,34356L,34357L,
34358L,34359L,34360L,34361L,34362L,34363L,34364L,34365L,34366L,34367L,
34368L,34369L,34370L,34371L,34372L,34373L,34374L,34375L,34376L,34377L,
34378L,34379L,34380L,34381L,34382L,34383L,34384L,34385L,34386L,34387L,
34388L,34389L,34390L,34391L,34392L,34393L,34394L,34395L,34396L,34397L,
34398L,34399L,34400L,34401L,34402L,34403L,34404L,34405L,34406L,34407L,
34408L,34409L,34410L,34411L,34412L,34413L,34414L,34415L,34416L,34417L,
34418L,34419L,34420L,34421L,34422L,34423L,34424L,34425L,34426L,34427L,
34428L,34429L,34430L,34431L,34432L,34433L,34434L,34435L,34436L,34437L,
34438L,34439L,34440L,34441L,34442L,34443L,34444L,34445L,34446L,34447L,
34448L,34449L,34450L,34451L,34452L,34453L,34454L,34455L,34456L,34457L,
34458L,34459L,34460L,34461L,34462L,34463L,34464L,34465L,34466L,34467L,
34468L,34469L,34470L,34471L,34472L,34473L,34474L,34475L,34476L,34477L,
34478L,34479L,34480L,34481L,34482L,34483L,34484L,34485L,34486L,34487L,
34488L,34489L,34490L,34491L,34492L,34493L,34494L,34495L,34496L,34497L,
34498L,34499L,34500L,34501L,34502L,34503L,34504L,34505L,34506L,34507L,
34508L,34509L,34510L,34511L,34512L,34513L,34514L,34515L,34516L,34517L,
34518L,34519L,34520L,34521L,34522L,34523L,34524L,34525L,34526L,34527L,
34528L,34529L,34530L,34531L,34532L,34533L,34534L,34535L,34536L,34537L,
34538L,34539L,34540L,34541L,34542L,34543L,34544L,34545L,34546L,34547L,
34548L,34549L,34550L,34551L,34552L,34553L,34554L,34555L,34556L,34557L,
34558L,34559L,34560L,34561L,34562L,34563L,34564L,34565L,34566L,34567L,
34568L,34569L,34570L,34571L,34572L,34573L,34574L,34575L,34576L,34577L,
34578L,34579L,34580L,34581L,34582L,34583L,34584L,34585L,34586L,34587L,
34588L,34589L,34590L,34591L,34592L,34593L,34594L,34595L,34596L,34597L,
34598L,34599L,34600L,34601L,34602L,34603L,34604L,34605L,34606L,34607L,
34608L,34609L,34610L,34611L,34612L,34613L,34614L,34615L,34616L,34617L,
34618L,34619L,34620L,34621L,34622L,34623L,34624L,34625L,34626L,34627L,
34628L,34629L,34630L,34631L,34632L,34633L,34634L,34635L,34636L,34637L,
34638L,34639L,34640L,34641L,34642L,34643L,34644L,34645L,34646L,34647L,
34648L,34649L,34650L,34651L,34652L,34653L,34654L,34655L,34656L,34657L,
34658L,34659L,34660L,34661L,34662L,34663L,34664L,34665L,34666L,34667L,
34668L,34669L,34670L,34671L,34672L,34673L,34674L,34675L,34676L,34677L,
34678L,34679L,34680L,34681L,34682L,34683L,34684L,34685L,34686L,34687L,
34688L,34689L,34690L,34691L,34692L,34693L,34694L,34695L,34696L,34697L,
34698L,34699L,34700L,34701L,34702L,34703L,34704L,34705L,34706L,34707L,
34708L,34709L,34710L,34711L,34712L,34713L,34714L,34715L,34716L,34717L,
34718L,34719L,34720L,34721L,34722L,34723L,34724L,34725L,34726L,34727L,
34728L,34729L,34730L,34731L,34732L,34733L,34734L,34735L,34736L,34737L,
34738L,34739L,34740L,34741L,34742L,34743L,34744L,34745L,34746L,34747L,
34748L,34749L,34750L,34751L,34752L,34753L,34754L,34755L,34756L,34757L,
34758L,34759L,34760L,34761L,34762L,34763L,34764L,34765L,34766L,34767L,
34768L,34769L,34770L,34771L,34772L,34773L,34774L,34775L,34776L,34777L,
34778L,34779L,34780L,34781L,34782L,34783L,34784L,34785L,34786L,34787L,
34788L,34789L,34790L,34791L,34792L,34793L,34794L,34795L,34796L,34797L,
34798L,34799L,34800L,34801L,34802L,34803L,34804L,34805L,34806L,34807L,
34808L,34809L,34810L,34811L,34812L,34813L,34814L,34815L,34816L,34817L,
34818L,34819L,34820L,34821L,34822L,34823L,34824L,34825L,34826L,34827L,
34828L,34829L,34830L,34831L,34832L,34833L,34834L,34835L,34836L,34837L,
34838L,34839L,34840L,34841L,34842L,34843L,34844L,34845L,34846L,34847L,
34848L,34849L,34850L,34851L,34852L,34853L,34854L,34855L,34856L,34857L,
34858L,34859L,34860L,34861L,34862L,34863L,34864L,34865L,34866L,34867L,
34868L,34869L,34870L,34871L,34872L,34873L,34874L,34875L,34876L,34877L,
34878L,34879L,34880L,34881L,34882L,34883L,34884L,34885L,34886L,34887L,
34888L,34889L,34890L,34891L,34892L,34893L,34894L,34895L,34896L,34897L,
34898L,34899L,34900L,34901L,34902L,34903L,34904L,34905L,34906L,34907L,
34908L,34909L,34910L,34911L,34912L,34913L,34914L,34915L,34916L,34917L,
34918L,34919L,34920L,34921L,34922L,34923L,34924L,34925L,34926L,34927L,
34928L,34929L,34930L,34931L,34932L,34933L,34934L,34935L,34936L,34937L,
34938L,34939L,34940L,34941L,34942L,34943L,34944L,34945L,34946L,34947L,
34948L,34949L,34950L,34951L,34952L,34953L,34954L,34955L,34956L,34957L,
34958L,34959L,34960L,34961L,34962L,34963L,34964L,34965L,34966L,34967L,
34968L,34969L,34970L,34971L,34972L,34973L,34974L,34975L,34976L,34977L,
34978L,34979L,34980L,34981L,34982L,34983L,34984L,34985L,34986L,34987L,
34988L,34989L,34990L,34991L,34992L,34993L,34994L,34995L,34996L,34997L,
34998L,34999L,35000L,35001L,35002L,35003L,35004L,35005L,35006L,35007L,
35008L,35009L,35010L,35011L,35012L,35013L,35014L,35015L,35016L,35017L,
35018L,35019L,35020L,35021L,35022L,35023L,35024L,35025L,35026L,35027L,
35028L,35029L,35030L,35031L,35032L,35033L,35034L,35035L,35036L,35037L,
35038L,35039L,35040L,35041L,35042L,35043L,35044L,35045L,35046L,35047L,
35048L,35049L,35050L,35051L,35052L,35053L,35054L,35055L,35056L,35057L,
35058L,35059L,35060L,35061L,35062L,35063L,35064L,35065L,35066L,35067L,
35068L,35069L,35070L,35071L,35072L,35073L,35074L,35075L,35076L,35077L,
35078L,35079L,35080L,35081L,35082L,35083L,35084L,35085L,35086L,35087L,
35088L,35089L,35090L,35091L,35092L,35093L,35094L,35095L,35096L,35097L,
35098L,35099L,35100L,35101L,35102L,35103L,35104L,35105L,35106L,35107L,
35108L,35109L,35110L,35111L,35112L,35113L,35114L,35115L,35116L,35117L,
35118L,35119L,35120L,35121L,35122L,35123L,35124L,35125L,35126L,35127L,
35128L,35129L,35130L,35131L,35132L,35133L,35134L,35135L,35136L,35137L,
35138L,35139L,35140L,35141L,35142L,35143L,35144L,35145L,35146L,35147L,
35148L,35149L,35150L,35151L,35152L,35153L,35154L,35155L,35156L,35157L,
35158L,35159L,35160L,35161L,35162L,35163L,35164L,35165L,35166L,35167L,
35168L,35169L,35170L,35171L,35172L,35173L,35174L,35175L,35176L,35177L,
35178L,35179L,35180L,35181L,35182L,35183L,35184L,35185L,35186L,35187L,
35188L,35189L,35190L,35191L,35192L,35193L,35194L,35195L,35196L,35197L,
35198L,35199L,35200L,35201L,35202L,35203L,35204L,35205L,35206L,35207L,
35208L,35209L,35210L,35211L,35212L,35213L,35214L,35215L,35216L,35217L,
35218L,35219L,35220L,35221L,35222L,35223L,35224L,35225L,35226L,35227L,
35228L,35229L,35230L,35231L,35232L,35233L,35234L,35235L,35236L,35237L,
35238L,35239L,35240L,35241L,35242L,35243L,35244L,35245L,35246L,35247L,
35248L,35249L,35250L,35251L,35252L,35253L,35254L,35255L,35256L,35257L,
35258L,35259L,35260L,35261L,35262L,35263L,35264L,35265L,35266L,35267L,
35268L,35269L,35270L,35271L,35272L,35273L,35274L,35275L,35276L,35277L,
35278L,35279L,35280L,35281L,35282L,35283L,35284L,35285L,35286L,35287L,
35288L,35289L,35290L,35291L,35292L,35293L,35294L,35295L,35296L,35297L,
35298L,35299L,35300L,35301L,35302L,35303L,35304L,35305L,35306L,35307L,
35308L,35309L,35310L,35311L,35312L,35313L,35314L,35315L,35316L,35317L,
35318L,35319L,35320L,35321L,35322L,35323L,35324L,35325L,35326L,35327L,
35328L,35329L,35330L,35331L,35332L,35333L,35334L,35335L,35336L,35337L,
35338L,35339L,35340L,35341L,35342L,35343L,35344L,35345L,35346L,35347L,
35348L,35349L,35350L,35351L,35352L,35353L,35354L,35355L,35356L,35357L,
35358L,35359L,35360L,35361L,35362L,35363L,35364L,35365L,35366L,35367L,
35368L,35369L,35370L,35371L,35372L,35373L,35374L,35375L,35376L,35377L,
35378L,35379L,35380L,35381L,35382L,35383L,35384L,35385L,35386L,35387L,
35388L,35389L,35390L,35391L,35392L,35393L,35394L,35395L,35396L,35397L,
35398L,35399L,35400L,35401L,35402L,35403L,35404L,35405L,35406L,35407L,
35408L,35409L,35410L,35411L,35412L,35413L,35414L,35415L,35416L,35417L,
35418L,35419L,35420L,35421L,35422L,35423L,35424L,35425L,35426L,35427L,
35428L,35429L,35430L,35431L,35432L,35433L,35434L,35435L,35436L,35437L,
35438L,35439L,35440L,35441L,35442L,35443L,35444L,35445L,35446L,35447L,
35448L,35449L,35450L,35451L,35452L,35453L,35454L,35455L,35456L,35457L,
35458L,35459L,35460L,35461L,35462L,35463L,35464L,35465L,35466L,35467L,
35468L,35469L,35470L,35471L,35472L,35473L,35474L,35475L,35476L,35477L,
35478L,35479L,35480L,35481L,35482L,35483L,35484L,35485L,35486L,35487L,
35488L,35489L,35490L,35491L,35492L,35493L,35494L,35495L,35496L,35497L,
35498L,35499L,35500L,35501L,35502L,35503L,35504L,35505L,35506L,35507L,
35508L,35509L,35510L,35511L,35512L,35513L,35514L,35515L,35516L,35517L,
35518L,35519L,35520L,35521L,35522L,35523L,35524L,35525L,35526L,35527L,
35528L,35529L,35530L,35531L,35532L,35533L,35534L,35535L,35536L,35537L,
35538L,35539L,35540L,35541L,35542L,35543L,35544L,35545L,35546L,35547L,
35548L,35549L,35550L,35551L,35552L,35553L,35554L,35555L,35556L,35557L,
35558L,35559L,35560L,35561L,35562L,35563L,35564L,35565L,35566L,35567L,
35568L,35569L,35570L,35571L,35572L,35573L,35574L,35575L,35576L,35577L,
35578L,35579L,35580L,35581L,35582L,35583L,35584L,35585L,35586L,35587L,
35588L,35589L,35590L,35591L,35592L,35593L,35594L,35595L,35596L,35597L,
35598L,35599L,35600L,35601L,35602L,35603L,35604L,35605L,35606L,35607L,
35608L,35609L,35610L,35611L,35612L,35613L,35614L,35615L,35616L,35617L,
35618L,35619L,35620L,35621L,35622L,35623L,35624L,35625L,35626L,35627L,
35628L,35629L,35630L,35631L,35632L,35633L,35634L,35635L,35636L,35637L,
35638L,35639L,35640L,35641L,35642L,35643L,35644L,35645L,35646L,35647L,
35648L,35649L,35650L,35651L,35652L,35653L,35654L,35655L,35656L,35657L,
35658L,35659L,35660L,35661L,35662L,35663L,35664L,35665L,35666L,35667L,
35668L,35669L,35670L,35671L,35672L,35673L,35674L,35675L,35676L,35677L,
35678L,35679L,35680L,35681L,35682L,35683L,35684L,35685L,35686L,35687L,
35688L,35689L,35690L,35691L,35692L,35693L,35694L,35695L,35696L,35697L,
35698L,35699L,35700L,35701L,35702L,35703L,35704L,35705L,35706L,35707L,
35708L,35709L,35710L,35711L,35712L,35713L,35714L,35715L,35716L,35717L,
35718L,35719L,35720L,35721L,35722L,35723L,35724L,35725L,35726L,35727L,
35728L,35729L,35730L,35731L,35732L,35733L,35734L,35735L,35736L,35737L,
35738L,35739L,35740L,35741L,35742L,35743L,35744L,35745L,35746L,35747L,
35748L,35749L,35750L,35751L,35752L,35753L,35754L,35755L,35756L,35757L,
35758L,35759L,35760L,35761L,35762L,35763L,35764L,35765L,35766L,35767L,
35768L,35769L,35770L,35771L,35772L,35773L,35774L,35775L,35776L,35777L,
35778L,35779L,35780L,35781L,35782L,35783L,35784L,35785L,35786L,35787L,
35788L,35789L,35790L,35791L,35792L,35793L,35794L,35795L,35796L,35797L,
35798L,35799L,35800L,35801L,35802L,35803L,35804L,35805L,35806L,35807L,
35808L,35809L,35810L,35811L,35812L,35813L,35814L,35815L,35816L,35817L,
35818L,35819L,35820L,35821L,35822L,35823L,35824L,35825L,35826L,35827L,
35828L,35829L,35830L,35831L,35832L,35833L,35834L,35835L,35836L,35837L,
35838L,35839L,35840L,35841L,35842L,35843L,35844L,35845L,35846L,35847L,
35848L,35849L,35850L,35851L,35852L,35853L,35854L,35855L,35856L,35857L,
35858L,35859L,35860L,35861L,35862L,35863L,35864L,35865L,35866L,35867L,
35868L,35869L,35870L,35871L,35872L,35873L,35874L,35875L,35876L,35877L,
35878L,35879L,35880L,35881L,35882L,35883L,35884L,35885L,35886L,35887L,
35888L,35889L,35890L,35891L,35892L,35893L,35894L,35895L,35896L,35897L,
35898L,35899L,35900L,35901L,35902L,35903L,35904L,35905L,35906L,35907L,
35908L,35909L,35910L,35911L,35912L,35913L,35914L,35915L,35916L,35917L,
35918L,35919L,35920L,35921L,35922L,35923L,35924L,35925L,35926L,35927L,
35928L,35929L,35930L,35931L,35932L,35933L,35934L,35935L,35936L,35937L,
35938L,35939L,35940L,35941L,35942L,35943L,35944L,35945L,35946L,35947L,
35948L,35949L,35950L,35951L,35952L,35953L,35954L,35955L,35956L,35957L,
35958L,35959L,35960L,35961L,35962L,35963L,35964L,35965L,35966L,35967L,
35968L,35969L,35970L,35971L,35972L,35973L,35974L,35975L,35976L,35977L,
35978L,35979L,35980L,35981L,35982L,35983L,35984L,35985L,35986L,35987L,
35988L,35989L,35990L,35991L,35992L,35993L,35994L,35995L,35996L,35997L,
35998L,35999L,36000L,36001L,36002L,36003L,36004L,36005L,36006L,36007L,
36008L,36009L,36010L,36011L,36012L,36013L,36014L,36015L,36016L,36017L,
36018L,36019L,36020L,36021L,36022L,36023L,36024L,36025L,36026L,36027L,
36028L,36029L,36030L,36031L,36032L,36033L,36034L,36035L,36036L,36037L,
36038L,36039L,36040L,36041L,36042L,36043L,36044L,36045L,36046L,36047L,
36048L,36049L,36050L,36051L,36052L,36053L,36054L,36055L,36056L,36057L,
36058L,36059L,36060L,36061L,36062L,36063L,36064L,36065L,36066L,36067L,
36068L,36069L,36070L,36071L,36072L,36073L,36074L,36075L,36076L,36077L,
36078L,36079L,36080L,36081L,36082L,36083L,36084L,36085L,36086L,36087L,
36088L,36089L,36090L,36091L,36092L,36093L,36094L,36095L,36096L,36097L,
36098L,36099L,36100L,36101L,36102L,36103L,36104L,36105L,36106L,36107L,
36108L,36109L,36110L,36111L,36112L,36113L,36114L,36115L,36116L,36117L,
36118L,36119L,36120L,36121L,36122L,36123L,36124L,36125L,36126L,36127L,
36128L,36129L,36130L,36131L,36132L,36133L,36134L,36135L,36136L,36137L,
36138L,36139L,36140L,36141L,36142L,36143L,36144L,36145L,36146L,36147L,
36148L,36149L,36150L,36151L,36152L,36153L,36154L,36155L,36156L,36157L,
36158L,36159L,36160L,36161L,36162L,36163L,36164L,36165L,36166L,36167L,
36168L,36169L,36170L,36171L,36172L,36173L,36174L,36175L,36176L,36177L,
36178L,36179L,36180L,36181L,36182L,36183L,36184L,36185L,36186L,36187L,
36188L,36189L,36190L,36191L,36192L,36193L,36194L,36195L,36196L,36197L,
36198L,36199L,36200L,36201L,36202L,36203L,36204L,36205L,36206L,36207L,
36208L,36209L,36210L,36211L,36212L,36213L,36214L,36215L,36216L,36217L,
36218L,36219L,36220L,36221L,36222L,36223L,36224L,36225L,36226L,36227L,
36228L,36229L,36230L,36231L,36232L,36233L,36234L,36235L,36236L,36237L,
36238L,36239L,36240L,36241L,36242L,36243L,36244L,36245L,36246L,36247L,
36248L,36249L,36250L,36251L,36252L,36253L,36254L,36255L,36256L,36257L,
36258L,36259L,36260L,36261L,36262L,36263L,36264L,36265L,36266L,36267L,
36268L,36269L,36270L,36271L,36272L,36273L,36274L,36275L,36276L,36277L,
36278L,36279L,36280L,36281L,36282L,36283L,36284L,36285L,36286L,36287L,
36288L,36289L,36290L,36291L,36292L,36293L,36294L,36295L,36296L,36297L,
36298L,36299L,36300L,36301L,36302L,36303L,36304L,36305L,36306L,36307L,
36308L,36309L,36310L,36311L,36312L,36313L,36314L,36315L,36316L,36317L,
36318L,36319L,36320L,36321L,36322L,36323L,36324L,36325L,36326L,36327L,
36328L,36329L,36330L,36331L,36332L,36333L,36334L,36335L,36336L,36337L,
36338L,36339L,36340L,36341L,36342L,36343L,36344L,36345L,36346L,36347L,
36348L,36349L,36350L,36351L,36352L,36353L,36354L,36355L,36356L,36357L,
36358L,36359L,36360L,36361L,36362L,36363L,36364L,36365L,36366L,36367L,
36368L,36369L,36370L,36371L,36372L,36373L,36374L,36375L,36376L,36377L,
36378L,36379L,36380L,36381L,36382L,36383L,36384L,36385L,36386L,36387L,
36388L,36389L,36390L,36391L,36392L,36393L,36394L,36395L,36396L,36397L,
36398L,36399L,36400L,36401L,36402L,36403L,36404L,36405L,36406L,36407L,
36408L,36409L,36410L,36411L,36412L,36413L,36414L,36415L,36416L,36417L,
36418L,36419L,36420L,36421L,36422L,36423L,36424L,36425L,36426L,36427L,
36428L,36429L,36430L,36431L,36432L,36433L,36434L,36435L,36436L,36437L,
36438L,36439L,36440L,36441L,36442L,36443L,36444L,36445L,36446L,36447L,
36448L,36449L,36450L,36451L,36452L,36453L,36454L,36455L,36456L,36457L,
36458L,36459L,36460L,36461L,36462L,36463L,36464L,36465L,36466L,36467L,
36468L,36469L,36470L,36471L,36472L,36473L,36474L,36475L,36476L,36477L,
36478L,36479L,36480L,36481L,36482L,36483L,36484L,36485L,36486L,36487L,
36488L,36489L,36490L,36491L,36492L,36493L,36494L,36495L,36496L,36497L,
36498L,36499L,36500L,36501L,36502L,36503L,36504L,36505L,36506L,36507L,
36508L,36509L,36510L,36511L,36512L,36513L,36514L,36515L,36516L,36517L,
36518L,36519L,36520L,36521L,36522L,36523L,36524L,36525L,36526L,36527L,
36528L,36529L,36530L,36531L,36532L,36533L,36534L,36535L,36536L,36537L,
36538L,36539L,36540L,36541L,36542L,36543L,36544L,36545L,36546L,36547L,
36548L,36549L,36550L,36551L,36552L,36553L,36554L,36555L,36556L,36557L,
36558L,36559L,36560L,36561L,36562L,36563L,36564L,36565L,36566L,36567L,
36568L,36569L,36570L,36571L,36572L,36573L,36574L,36575L,36576L,36577L,
36578L,36579L,36580L,36581L,36582L,36583L,36584L,36585L,36586L,36587L,
36588L,36589L,36590L,36591L,36592L,36593L,36594L,36595L,36596L,36597L,
36598L,36599L,36600L,36601L,36602L,36603L,36604L,36605L,36606L,36607L,
36608L,36609L,36610L,36611L,36612L,36613L,36614L,36615L,36616L,36617L,
36618L,36619L,36620L,36621L,36622L,36623L,36624L,36625L,36626L,36627L,
36628L,36629L,36630L,36631L,36632L,36633L,36634L,36635L,36636L,36637L,
36638L,36639L,36640L,36641L,36642L,36643L,36644L,36645L,36646L,36647L,
36648L,36649L,36650L,36651L,36652L,36653L,36654L,36655L,36656L,36657L,
36658L,36659L,36660L,36661L,36662L,36663L,36664L,36665L,36666L,36667L,
36668L,36669L,36670L,36671L,36672L,36673L,36674L,36675L,36676L,36677L,
36678L,36679L,36680L,36681L,36682L,36683L,36684L,36685L,36686L,36687L,
36688L,36689L,36690L,36691L,36692L,36693L,36694L,36695L,36696L,36697L,
36698L,36699L,36700L,36701L,36702L,36703L,36704L,36705L,36706L,36707L,
36708L,36709L,36710L,36711L,36712L,36713L,36714L,36715L,36716L,36717L,
36718L,36719L,36720L,36721L,36722L,36723L,36724L,36725L,36726L,36727L,
36728L,36729L,36730L,36731L,36732L,36733L,36734L,36735L,36736L,36737L,
36738L,36739L,36740L,36741L,36742L,36743L,36744L,36745L,36746L,36747L,
36748L,36749L,36750L,36751L,36752L,36753L,36754L,36755L,36756L,36757L,
36758L,36759L,36760L,36761L,36762L,36763L,36764L,36765L,36766L,36767L,
36768L,36769L,36770L,36771L,36772L,36773L,36774L,36775L,36776L,36777L,
36778L,36779L,36780L,36781L,36782L,36783L,36784L,36785L,36786L,36787L,
36788L,36789L,36790L,36791L,36792L,36793L,36794L,36795L,36796L,36797L,
36798L,36799L,36800L,36801L,36802L,36803L,36804L,36805L,36806L,36807L,
36808L,36809L,36810L,36811L,36812L,36813L,36814L,36815L,36816L,36817L,
36818L,36819L,36820L,36821L,36822L,36823L,36824L,36825L,36826L,36827L,
36828L,36829L,36830L,36831L,36832L,36833L,36834L,36835L,36836L,36837L,
36838L,36839L,36840L,36841L,36842L,36843L,36844L,36845L,36846L,36847L,
36848L,36849L,36850L,36851L,36852L,36853L,36854L,36855L,36856L,36857L,
36858L,36859L,36860L,36861L,36862L,36863L,36864L,36865L,36866L,36867L,
36868L,36869L,36870L,36871L,36872L,36873L,36874L,36875L,36876L,36877L,
36878L,36879L,36880L,36881L,36882L,36883L,36884L,36885L,36886L,36887L,
36888L,36889L,36890L,36891L,36892L,36893L,36894L,36895L,36896L,36897L,
36898L,36899L,36900L,36901L,36902L,36903L,36904L,36905L,36906L,36907L,
36908L,36909L,36910L,36911L,36912L,36913L,36914L,36915L,36916L,36917L,
36918L,36919L,36920L,36921L,36922L,36923L,36924L,36925L,36926L,36927L,
36928L,36929L,36930L,36931L,36932L,36933L,36934L,36935L,36936L,36937L,
36938L,36939L,36940L,36941L,36942L,36943L,36944L,36945L,36946L,36947L,
36948L,36949L,36950L,36951L,36952L,36953L,36954L,36955L,36956L,36957L,
36958L,36959L,36960L,36961L,36962L,36963L,36964L,36965L,36966L,36967L,
36968L,36969L,36970L,36971L,36972L,36973L,36974L,36975L,36976L,36977L,
36978L,36979L,36980L,36981L,36982L,36983L,36984L,36985L,36986L,36987L,
36988L,36989L,36990L,36991L,36992L,36993L,36994L,36995L,36996L,36997L,
36998L,36999L,37000L,37001L,37002L,37003L,37004L,37005L,37006L,37007L,
37008L,37009L,37010L,37011L,37012L,37013L,37014L,37015L,37016L,37017L,
37018L,37019L,37020L,37021L,37022L,37023L,37024L,37025L,37026L,37027L,
37028L,37029L,37030L,37031L,37032L,37033L,37034L,37035L,37036L,37037L,
37038L,37039L,37040L,37041L,37042L,37043L,37044L,37045L,37046L,37047L,
37048L,37049L,37050L,37051L,37052L,37053L,37054L,37055L,37056L,37057L,
37058L,37059L,37060L,37061L,37062L,37063L,37064L,37065L,37066L,37067L,
37068L,37069L,37070L,37071L,37072L,37073L,37074L,37075L,37076L,37077L,
37078L,37079L,37080L,37081L,37082L,37083L,37084L,37085L,37086L,37087L,
37088L,37089L,37090L,37091L,37092L,37093L,37094L,37095L,37096L,37097L,
37098L,37099L,37100L,37101L,37102L,37103L,37104L,37105L,37106L,37107L,
37108L,37109L,37110L,37111L,37112L,37113L,37114L,37115L,37116L,37117L,
37118L,37119L,37120L,37121L,37122L,37123L,37124L,37125L,37126L,37127L,
37128L,37129L,37130L,37131L,37132L,37133L,37134L,37135L,37136L,37137L,
37138L,37139L,37140L,37141L,37142L,37143L,37144L,37145L,37146L,37147L,
37148L,37149L,37150L,37151L,37152L,37153L,37154L,37155L,37156L,37157L,
37158L,37159L,37160L,37161L,37162L,37163L,37164L,37165L,37166L,37167L,
37168L,37169L,37170L,37171L,37172L,37173L,37174L,37175L,37176L,37177L,
37178L,37179L,37180L,37181L,37182L,37183L,37184L,37185L,37186L,37187L,
37188L,37189L,37190L,37191L,37192L,37193L,37194L,37195L,37196L,37197L,
37198L,37199L,37200L,37201L,37202L,37203L,37204L,37205L,37206L,37207L,
37208L,37209L,37210L,37211L,37212L,37213L,37214L,37215L,37216L,37217L,
37218L,37219L,37220L,37221L,37222L,37223L,37224L,37225L,37226L,37227L,
37228L,37229L,37230L,37231L,37232L,37233L,37234L,37235L,37236L,37237L,
37238L,37239L,37240L,37241L,37242L,37243L,37244L,37245L,37246L,37247L,
37248L,37249L,37250L,37251L,37252L,37253L,37254L,37255L,37256L,37257L,
37258L,37259L,37260L,37261L,37262L,37263L,37264L,37265L,37266L,37267L,
37268L,37269L,37270L,37271L,37272L,37273L,37274L,37275L,37276L,37277L,
37278L,37279L,37280L,37281L,37282L,37283L,37284L,37285L,37286L,37287L,
37288L,37289L,37290L,37291L,37292L,37293L,37294L,37295L,37296L,37297L,
37298L,37299L,37300L,37301L,37302L,37303L,37304L,37305L,37306L,37307L,
37308L,37309L,37310L,37311L,37312L,37313L,37314L,37315L,37316L,37317L,
37318L,37319L,37320L,37321L,37322L,37323L,37324L,37325L,37326L,37327L,
37328L,37329L,37330L,37331L,37332L,37333L,37334L,37335L,37336L,37337L,
37338L,37339L,37340L,37341L,37342L,37343L,37344L,37345L,37346L,37347L,
37348L,37349L,37350L,37351L,37352L,37353L,37354L,37355L,37356L,37357L,
37358L,37359L,37360L,37361L,37362L,37363L,37364L,37365L,37366L,37367L,
37368L,37369L,37370L,37371L,37372L,37373L,37374L,37375L,37376L,37377L,
37378L,37379L,37380L,37381L,37382L,37383L,37384L,37385L,37386L,37387L,
37388L,37389L,37390L,37391L,37392L,37393L,37394L,37395L,37396L,37397L,
37398L,37399L,37400L,37401L,37402L,37403L,37404L,37405L,37406L,37407L,
37408L,37409L,37410L,37411L,37412L,37413L,37414L,37415L,37416L,37417L,
37418L,37419L,37420L,37421L,37422L,37423L,37424L,37425L,37426L,37427L,
37428L,37429L,37430L,37431L,37432L,37433L,37434L,37435L,37436L,37437L,
37438L,37439L,37440L,37441L,37442L,37443L,37444L,37445L,37446L,37447L,
37448L,37449L,37450L,37451L,37452L,37453L,37454L,37455L,37456L,37457L,
37458L,37459L,37460L,37461L,37462L,37463L,37464L,37465L,37466L,37467L,
37468L,37469L,37470L,37471L,37472L,37473L,37474L,37475L,37476L,37477L,
37478L,37479L,37480L,37481L,37482L,37483L,37484L,37485L,37486L,37487L,
37488L,37489L,37490L,37491L,37492L,37493L,37494L,37495L,37496L,37497L,
37498L,37499L,37500L,37501L,37502L,37503L,37504L,37505L,37506L,37507L,
37508L,37509L,37510L,37511L,37512L,37513L,37514L,37515L,37516L,37517L,
37518L,37519L,37520L,37521L,37522L,37523L,37524L,37525L,37526L,37527L,
37528L,37529L,37530L,37531L,37532L,37533L,37534L,37535L,37536L,37537L,
37538L,37539L,37540L,37541L,37542L,37543L,37544L,37545L,37546L,37547L,
37548L,37549L,37550L,37551L,37552L,37553L,37554L,37555L,37556L,37557L,
37558L,37559L,37560L,37561L,37562L,37563L,37564L,37565L,37566L,37567L,
37568L,37569L,37570L,37571L,37572L,37573L,37574L,37575L,37576L,37577L,
37578L,37579L,37580L,37581L,37582L,37583L,37584L,37585L,37586L,37587L,
37588L,37589L,37590L,37591L,37592L,37593L,37594L,37595L,37596L,37597L,
37598L,37599L,37600L,37601L,37602L,37603L,37604L,37605L,37606L,37607L,
37608L,37609L,37610L,37611L,37612L,37613L,37614L,37615L,37616L,37617L,
37618L,37619L,37620L,37621L,37622L,37623L,37624L,37625L,37626L,37627L,
37628L,37629L,37630L,37631L,37632L,37633L,37634L,37635L,37636L,37637L,
37638L,37639L,37640L,37641L,37642L,37643L,37644L,37645L,37646L,37647L,
37648L,37649L,37650L,37651L,37652L,37653L,37654L,37655L,37656L,37657L,
37658L,37659L,37660L,37661L,37662L,37663L,37664L,37665L,37666L,37667L,
37668L,37669L,37670L,37671L,37672L,37673L,37674L,37675L,37676L,37677L,
37678L,37679L,37680L,37681L,37682L,37683L,37684L,37685L,37686L,37687L,
37688L,37689L,37690L,37691L,37692L,37693L,37694L,37695L,37696L,37697L,
37698L,37699L,37700L,37701L,37702L,37703L,37704L,37705L,37706L,37707L,
37708L,37709L,37710L,37711L,37712L,37713L,37714L,37715L,37716L,37717L,
37718L,37719L,37720L,37721L,37722L,37723L,37724L,37725L,37726L,37727L,
37728L,37729L,37730L,37731L,37732L,37733L,37734L,37735L,37736L,37737L,
37738L,37739L,37740L,37741L,37742L,37743L,37744L,37745L,37746L,37747L,
37748L,37749L,37750L,37751L,37752L,37753L,37754L,37755L,37756L,37757L,
37758L,37759L,37760L,37761L,37762L,37763L,37764L,37765L,37766L,37767L,
37768L,37769L,37770L,37771L,37772L,37773L,37774L,37775L,37776L,37777L,
37778L,37779L,37780L,37781L,37782L,37783L,37784L,37785L,37786L,37787L,
37788L,37789L,37790L,37791L,37792L,37793L,37794L,37795L,37796L,37797L,
37798L,37799L,37800L,37801L,37802L,37803L,37804L,37805L,37806L,37807L,
37808L,37809L,37810L,37811L,37812L,37813L,37814L,37815L,37816L,37817L,
37818L,37819L,37820L,37821L,37822L,37823L,37824L,37825L,37826L,37827L,
37828L,37829L,37830L,37831L,37832L,37833L,37834L,37835L,37836L,37837L,
37838L,37839L,37840L,37841L,37842L,37843L,37844L,37845L,37846L,37847L,
37848L,37849L,37850L,37851L,37852L,37853L,37854L,37855L,37856L,37857L,
37858L,37859L,37860L,37861L,37862L,37863L,37864L,37865L,37866L,37867L,
37868L,37869L,37870L,37871L,37872L,37873L,37874L,37875L,37876L,37877L,
37878L,37879L,37880L,37881L,37882L,37883L,37884L,37885L,37886L,37887L,
37888L,37889L,37890L,37891L,37892L,37893L,37894L,37895L,37896L,37897L,
37898L,37899L,37900L,37901L,37902L,37903L,37904L,37905L,37906L,37907L,
37908L,37909L,37910L,37911L,37912L,37913L,37914L,37915L,37916L,37917L,
37918L,37919L,37920L,37921L,37922L,37923L,37924L,37925L,37926L,37927L,
37928L,37929L,37930L,37931L,37932L,37933L,37934L,37935L,37936L,37937L,
37938L,37939L,37940L,37941L,37942L,37943L,37944L,37945L,37946L,37947L,
37948L,37949L,37950L,37951L,37952L,37953L,37954L,37955L,37956L,37957L,
37958L,37959L,37960L,37961L,37962L,37963L,37964L,37965L,37966L,37967L,
37968L,37969L,37970L,37971L,37972L,37973L,37974L,37975L,37976L,37977L,
37978L,37979L,37980L,37981L,37982L,37983L,37984L,37985L,37986L,37987L,
37988L,37989L,37990L,37991L,37992L,37993L,37994L,37995L,37996L,37997L,
37998L,37999L,38000L,38001L,38002L,38003L,38004L,38005L,38006L,38007L,
38008L,38009L,38010L,38011L,38012L,38013L,38014L,38015L,38016L,38017L,
38018L,38019L,38020L,38021L,38022L,38023L,38024L,38025L,38026L,38027L,
38028L,38029L,38030L,38031L,38032L,38033L,38034L,38035L,38036L,38037L,
38038L,38039L,38040L,38041L,38042L,38043L,38044L,38045L,38046L,38047L,
38048L,38049L,38050L,38051L,38052L,38053L,38054L,38055L,38056L,38057L,
38058L,38059L,38060L,38061L,38062L,38063L,38064L,38065L,38066L,38067L,
38068L,38069L,38070L,38071L,38072L,38073L,38074L,38075L,38076L,38077L,
38078L,38079L,38080L,38081L,38082L,38083L,38084L,38085L,38086L,38087L,
38088L,38089L,38090L,38091L,38092L,38093L,38094L,38095L,38096L,38097L,
38098L,38099L,38100L,38101L,38102L,38103L,38104L,38105L,38106L,38107L,
38108L,38109L,38110L,38111L,38112L,38113L,38114L,38115L,38116L,38117L,
38118L,38119L,38120L,38121L,38122L,38123L,38124L,38125L,38126L,38127L,
38128L,38129L,38130L,38131L,38132L,38133L,38134L,38135L,38136L,38137L,
38138L,38139L,38140L,38141L,38142L,38143L,38144L,38145L,38146L,38147L,
38148L,38149L,38150L,38151L,38152L,38153L,38154L,38155L,38156L,38157L,
38158L,38159L,38160L,38161L,38162L,38163L,38164L,38165L,38166L,38167L,
38168L,38169L,38170L,38171L,38172L,38173L,38174L,38175L,38176L,38177L,
38178L,38179L,38180L,38181L,38182L,38183L,38184L,38185L,38186L,38187L,
38188L,38189L,38190L,38191L,38192L,38193L,38194L,38195L,38196L,38197L,
38198L,38199L,38200L,38201L,38202L,38203L,38204L,38205L,38206L,38207L,
38208L,38209L,38210L,38211L,38212L,38213L,38214L,38215L,38216L,38217L,
38218L,38219L,38220L,38221L,38222L,38223L,38224L,38225L,38226L,38227L,
38228L,38229L,38230L,38231L,38232L,38233L,38234L,38235L,38236L,38237L,
38238L,38239L,38240L,38241L,38242L,38243L,38244L,38245L,38246L,38247L,
38248L,38249L,38250L,38251L,38252L,38253L,38254L,38255L,38256L,38257L,
38258L,38259L,38260L,38261L,38262L,38263L,38264L,38265L,38266L,38267L,
38268L,38269L,38270L,38271L,38272L,38273L,38274L,38275L,38276L,38277L,
38278L,38279L,38280L,38281L,38282L,38283L,38284L,38285L,38286L,38287L,
38288L,38289L,38290L,38291L,38292L,38293L,38294L,38295L,38296L,38297L,
38298L,38299L,38300L,38301L,38302L,38303L,38304L,38305L,38306L,38307L,
38308L,38309L,38310L,38311L,38312L,38313L,38314L,38315L,38316L,38317L,
38318L,38319L,38320L,38321L,38322L,38323L,38324L,38325L,38326L,38327L,
38328L,38329L,38330L,38331L,38332L,38333L,38334L,38335L,38336L,38337L,
38338L,38339L,38340L,38341L,38342L,38343L,38344L,38345L,38346L,38347L,
38348L,38349L,38350L,38351L,38352L,38353L,38354L,38355L,38356L,38357L,
38358L,38359L,38360L,38361L,38362L,38363L,38364L,38365L,38366L,38367L,
38368L,38369L,38370L,38371L,38372L,38373L,38374L,38375L,38376L,38377L,
38378L,38379L,38380L,38381L,38382L,38383L,38384L,38385L,38386L,38387L,
38388L,38389L,38390L,38391L,38392L,38393L,38394L,38395L,38396L,38397L,
38398L,38399L,38400L,38401L,38402L,38403L,38404L,38405L,38406L,38407L,
38408L,38409L,38410L,38411L,38412L,38413L,38414L,38415L,38416L,38417L,
38418L,38419L,38420L,38421L,38422L,38423L,38424L,38425L,38426L,38427L,
38428L,38429L,38430L,38431L,38432L,38433L,38434L,38435L,38436L,38437L,
38438L,38439L,38440L,38441L,38442L,38443L,38444L,38445L,38446L,38447L,
38448L,38449L,38450L,38451L,38452L,38453L,38454L,38455L,38456L,38457L,
38458L,38459L,38460L,38461L,38462L,38463L,38464L,38465L,38466L,38467L,
38468L,38469L,38470L,38471L,38472L,38473L,38474L,38475L,38476L,38477L,
38478L,38479L,38480L,38481L,38482L,38483L,38484L,38485L,38486L,38487L,
38488L,38489L,38490L,38491L,38492L,38493L,38494L,38495L,38496L,38497L,
38498L,38499L,38500L,38501L,38502L,38503L,38504L,38505L,38506L,38507L,
38508L,38509L,38510L,38511L,38512L,38513L,38514L,38515L,38516L,38517L,
38518L,38519L,38520L,38521L,38522L,38523L,38524L,38525L,38526L,38527L,
38528L,38529L,38530L,38531L,38532L,38533L,38534L,38535L,38536L,38537L,
38538L,38539L,38540L,38541L,38542L,38543L,38544L,38545L,38546L,38547L,
38548L,38549L,38550L,38551L,38552L,38553L,38554L,38555L,38556L,38557L,
38558L,38559L,38560L,38561L,38562L,38563L,38564L,38565L,38566L,38567L,
38568L,38569L,38570L,38571L,38572L,38573L,38574L,38575L,38576L,38577L,
38578L,38579L,38580L,38581L,38582L,38583L,38584L,38585L,38586L,38587L,
38588L,38589L,38590L,38591L,38592L,38593L,38594L,38595L,38596L,38597L,
38598L,38599L,38600L,38601L,38602L,38603L,38604L,38605L,38606L,38607L,
38608L,38609L,38610L,38611L,38612L,38613L,38614L,38615L,38616L,38617L,
38618L,38619L,38620L,38621L,38622L,38623L,38624L,38625L,38626L,38627L,
38628L,38629L,38630L,38631L,38632L,38633L,38634L,38635L,38636L,38637L,
38638L,38639L,38640L,38641L,38642L,38643L,38644L,38645L,38646L,38647L,
38648L,38649L,38650L,38651L,38652L,38653L,38654L,38655L,38656L,38657L,
38658L,38659L,38660L,38661L,38662L,38663L,38664L,38665L,38666L,38667L,
38668L,38669L,38670L,38671L,38672L,38673L,38674L,38675L,38676L,38677L,
38678L,38679L,38680L,38681L,38682L,38683L,38684L,38685L,38686L,38687L,
38688L,38689L,38690L,38691L,38692L,38693L,38694L,38695L,38696L,38697L,
38698L,38699L,38700L,38701L,38702L,38703L,38704L,38705L,38706L,38707L,
38708L,38709L,38710L,38711L,38712L,38713L,38714L,38715L,38716L,38717L,
38718L,38719L,38720L,38721L,38722L,38723L,38724L,38725L,38726L,38727L,
38728L,38729L,38730L,38731L,38732L,38733L,38734L,38735L,38736L,38737L,
38738L,38739L,38740L,38741L,38742L,38743L,38744L,38745L,38746L,38747L,
38748L,38749L,38750L,38751L,38752L,38753L,38754L,38755L,38756L,38757L,
38758L,38759L,38760L,38761L,38762L,38763L,38764L,38765L,38766L,38767L,
38768L,38769L,38770L,38771L,38772L,38773L,38774L,38775L,38776L,38777L,
38778L,38779L,38780L,38781L,38782L,38783L,38784L,38785L,38786L,38787L,
38788L,38789L,38790L,38791L,38792L,38793L,38794L,38795L,38796L,38797L,
38798L,38799L,38800L,38801L,38802L,38803L,38804L,38805L,38806L,38807L,
38808L,38809L,38810L,38811L,38812L,38813L,38814L,38815L,38816L,38817L,
38818L,38819L,38820L,38821L,38822L,38823L,38824L,38825L,38826L,38827L,
38828L,38829L,38830L,38831L,38832L,38833L,38834L,38835L,38836L,38837L,
38838L,38839L,38840L,38841L,38842L,38843L,38844L,38845L,38846L,38847L,
38848L,38849L,38850L,38851L,38852L,38853L,38854L,38855L,38856L,38857L,
38858L,38859L,38860L,38861L,38862L,38863L,38864L,38865L,38866L,38867L,
38868L,38869L,38870L,38871L,38872L,38873L,38874L,38875L,38876L,38877L,
38878L,38879L,38880L,38881L,38882L,38883L,38884L,38885L,38886L,38887L,
38888L,38889L,38890L,38891L,38892L,38893L,38894L,38895L,38896L,38897L,
38898L,38899L,38900L,38901L,38902L,38903L,38904L,38905L,38906L,38907L,
38908L,38909L,38910L,38911L,38912L,38913L,38914L,38915L,38916L,38917L,
38918L,38919L,38920L,38921L,38922L,38923L,38924L,38925L,38926L,38927L,
38928L,38929L,38930L,38931L,38932L,38933L,38934L,38935L,38936L,38937L,
38938L,38939L,38940L,38941L,38942L,38943L,38944L,38945L,38946L,38947L,
38948L,38949L,38950L,38951L,38952L,38953L,38954L,38955L,38956L,38957L,
38958L,38959L,38960L,38961L,38962L,38963L,38964L,38965L,38966L,38967L,
38968L,38969L,38970L,38971L,38972L,38973L,38974L,38975L,38976L,38977L,
38978L,38979L,38980L,38981L,38982L,38983L,38984L,38985L,38986L,38987L,
38988L,38989L,38990L,38991L,38992L,38993L,38994L,38995L,38996L,38997L,
38998L,38999L,39000L,39001L,39002L,39003L,39004L,39005L,39006L,39007L,
39008L,39009L,39010L,39011L,39012L,39013L,39014L,39015L,39016L,39017L,
39018L,39019L,39020L,39021L,39022L,39023L,39024L,39025L,39026L,39027L,
39028L,39029L,39030L,39031L,39032L,39033L,39034L,39035L,39036L,39037L,
39038L,39039L,39040L,39041L,39042L,39043L,39044L,39045L,39046L,39047L,
39048L,39049L,39050L,39051L,39052L,39053L,39054L,39055L,39056L,39057L,
39058L,39059L,39060L,39061L,39062L,39063L,39064L,39065L,39066L,39067L,
39068L,39069L,39070L,39071L,39072L,39073L,39074L,39075L,39076L,39077L,
39078L,39079L,39080L,39081L,39082L,39083L,39084L,39085L,39086L,39087L,
39088L,39089L,39090L,39091L,39092L,39093L,39094L,39095L,39096L,39097L,
39098L,39099L,39100L,39101L,39102L,39103L,39104L,39105L,39106L,39107L,
39108L,39109L,39110L,39111L,39112L,39113L,39114L,39115L,39116L,39117L,
39118L,39119L,39120L,39121L,39122L,39123L,39124L,39125L,39126L,39127L,
39128L,39129L,39130L,39131L,39132L,39133L,39134L,39135L,39136L,39137L,
39138L,39139L,39140L,39141L,39142L,39143L,39144L,39145L,39146L,39147L,
39148L,39149L,39150L,39151L,39152L,39153L,39154L,39155L,39156L,39157L,
39158L,39159L,39160L,39161L,39162L,39163L,39164L,39165L,39166L,39167L,
39168L,39169L,39170L,39171L,39172L,39173L,39174L,39175L,39176L,39177L,
39178L,39179L,39180L,39181L,39182L,39183L,39184L,39185L,39186L,39187L,
39188L,39189L,39190L,39191L,39192L,39193L,39194L,39195L,39196L,39197L,
39198L,39199L,39200L,39201L,39202L,39203L,39204L,39205L,39206L,39207L,
39208L,39209L,39210L,39211L,39212L,39213L,39214L,39215L,39216L,39217L,
39218L,39219L,39220L,39221L,39222L,39223L,39224L,39225L,39226L,39227L,
39228L,39229L,39230L,39231L,39232L,39233L,39234L,39235L,39236L,39237L,
39238L,39239L,39240L,39241L,39242L,39243L,39244L,39245L,39246L,39247L,
39248L,39249L,39250L,39251L,39252L,39253L,39254L,39255L,39256L,39257L,
39258L,39259L,39260L,39261L,39262L,39263L,39264L,39265L,39266L,39267L,
39268L,39269L,39270L,39271L,39272L,39273L,39274L,39275L,39276L,39277L,
39278L,39279L,39280L,39281L,39282L,39283L,39284L,39285L,39286L,39287L,
39288L,39289L,39290L,39291L,39292L,39293L,39294L,39295L,39296L,39297L,
39298L,39299L,39300L,39301L,39302L,39303L,39304L,39305L,39306L,39307L,
39308L,39309L,39310L,39311L,39312L,39313L,39314L,39315L,39316L,39317L,
39318L,39319L,39320L,39321L,39322L,39323L,39324L,39325L,39326L,39327L,
39328L,39329L,39330L,39331L,39332L,39333L,39334L,39335L,39336L,39337L,
39338L,39339L,39340L,39341L,39342L,39343L,39344L,39345L,39346L,39347L,
39348L,39349L,39350L,39351L,39352L,39353L,39354L,39355L,39356L,39357L,
39358L,39359L,39360L,39361L,39362L,39363L,39364L,39365L,39366L,39367L,
39368L,39369L,39370L,39371L,39372L,39373L,39374L,39375L,39376L,39377L,
39378L,39379L,39380L,39381L,39382L,39383L,39384L,39385L,39386L,39387L,
39388L,39389L,39390L,39391L,39392L,39393L,39394L,39395L,39396L,39397L,
39398L,39399L,39400L,39401L,39402L,39403L,39404L,39405L,39406L,39407L,
39408L,39409L,39410L,39411L,39412L,39413L,39414L,39415L,39416L,39417L,
39418L,39419L,39420L,39421L,39422L,39423L,39424L,39425L,39426L,39427L,
39428L,39429L,39430L,39431L,39432L,39433L,39434L,39435L,39436L,39437L,
39438L,39439L,39440L,39441L,39442L,39443L,39444L,39445L,39446L,39447L,
39448L,39449L,39450L,39451L,39452L,39453L,39454L,39455L,39456L,39457L,
39458L,39459L,39460L,39461L,39462L,39463L,39464L,39465L,39466L,39467L,
39468L,39469L,39470L,39471L,39472L,39473L,39474L,39475L,39476L,39477L,
39478L,39479L,39480L,39481L,39482L,39483L,39484L,39485L,39486L,39487L,
39488L,39489L,39490L,39491L,39492L,39493L,39494L,39495L,39496L,39497L,
39498L,39499L,39500L,39501L,39502L,39503L,39504L,39505L,39506L,39507L,
39508L,39509L,39510L,39511L,39512L,39513L,39514L,39515L,39516L,39517L,
39518L,39519L,39520L,39521L,39522L,39523L,39524L,39525L,39526L,39527L,
39528L,39529L,39530L,39531L,39532L,39533L,39534L,39535L,39536L,39537L,
39538L,39539L,39540L,39541L,39542L,39543L,39544L,39545L,39546L,39547L,
39548L,39549L,39550L,39551L,39552L,39553L,39554L,39555L,39556L,39557L,
39558L,39559L,39560L,39561L,39562L,39563L,39564L,39565L,39566L,39567L,
39568L,39569L,39570L,39571L,39572L,39573L,39574L,39575L,39576L,39577L,
39578L,39579L,39580L,39581L,39582L,39583L,39584L,39585L,39586L,39587L,
39588L,39589L,39590L,39591L,39592L,39593L,39594L,39595L,39596L,39597L,
39598L,39599L,39600L,39601L,39602L,39603L,39604L,39605L,39606L,39607L,
39608L,39609L,39610L,39611L,39612L,39613L,39614L,39615L,39616L,39617L,
39618L,39619L,39620L,39621L,39622L,39623L,39624L,39625L,39626L,39627L,
39628L,39629L,39630L,39631L,39632L,39633L,39634L,39635L,39636L,39637L,
39638L,39639L,39640L,39641L,39642L,39643L,39644L,39645L,39646L,39647L,
39648L,39649L,39650L,39651L,39652L,39653L,39654L,39655L,39656L,39657L,
39658L,39659L,39660L,39661L,39662L,39663L,39664L,39665L,39666L,39667L,
39668L,39669L,39670L,39671L,39672L,39673L,39674L,39675L,39676L,39677L,
39678L,39679L,39680L,39681L,39682L,39683L,39684L,39685L,39686L,39687L,
39688L,39689L,39690L,39691L,39692L,39693L,39694L,39695L,39696L,39697L,
39698L,39699L,39700L,39701L,39702L,39703L,39704L,39705L,39706L,39707L,
39708L,39709L,39710L,39711L,39712L,39713L,39714L,39715L,39716L,39717L,
39718L,39719L,39720L,39721L,39722L,39723L,39724L,39725L,39726L,39727L,
39728L,39729L,39730L,39731L,39732L,39733L,39734L,39735L,39736L,39737L,
39738L,39739L,39740L,39741L,39742L,39743L,39744L,39745L,39746L,39747L,
39748L,39749L,39750L,39751L,39752L,39753L,39754L,39755L,39756L,39757L,
39758L,39759L,39760L,39761L,39762L,39763L,39764L,39765L,39766L,39767L,
39768L,39769L,39770L,39771L,39772L,39773L,39774L,39775L,39776L,39777L,
39778L,39779L,39780L,39781L,39782L,39783L,39784L,39785L,39786L,39787L,
39788L,39789L,39790L,39791L,39792L,39793L,39794L,39795L,39796L,39797L,
39798L,39799L,39800L,39801L,39802L,39803L,39804L,39805L,39806L,39807L,
39808L,39809L,39810L,39811L,39812L,39813L,39814L,39815L,39816L,39817L,
39818L,39819L,39820L,39821L,39822L,39823L,39824L,39825L,39826L,39827L,
39828L,39829L,39830L,39831L,39832L,39833L,39834L,39835L,39836L,39837L,
39838L,39839L,39840L,39841L,39842L,39843L,39844L,39845L,39846L,39847L,
39848L,39849L,39850L,39851L,39852L,39853L,39854L,39855L,39856L,39857L,
39858L,39859L,39860L,39861L,39862L,39863L,39864L,39865L,39866L,39867L,
39868L,39869L,39870L,39871L,39872L,39873L,39874L,39875L,39876L,39877L,
39878L,39879L,39880L,39881L,39882L,39883L,39884L,39885L,39886L,39887L,
39888L,39889L,39890L,39891L,39892L,39893L,39894L,39895L,39896L,39897L,
39898L,39899L,39900L,39901L,39902L,39903L,39904L,39905L,39906L,39907L,
39908L,39909L,39910L,39911L,39912L,39913L,39914L,39915L,39916L,39917L,
39918L,39919L,39920L,39921L,39922L,39923L,39924L,39925L,39926L,39927L,
39928L,39929L,39930L,39931L,39932L,39933L,39934L,39935L,39936L,39937L,
39938L,39939L,39940L,39941L,39942L,39943L,39944L,39945L,39946L,39947L,
39948L,39949L,39950L,39951L,39952L,39953L,39954L,39955L,39956L,39957L,
39958L,39959L,39960L,39961L,39962L,39963L,39964L,39965L,39966L,39967L,
39968L,39969L,39970L,39971L,39972L,39973L,39974L,39975L,39976L,39977L,
39978L,39979L,39980L,39981L,39982L,39983L,39984L,39985L,39986L,39987L,
39988L,39989L,39990L,39991L,39992L,39993L,39994L,39995L,39996L,39997L,
39998L,39999L,40000L,40001L,40002L,40003L,40004L,40005L,40006L,40007L,
40008L,40009L,40010L,40011L,40012L,40013L,40014L,40015L,40016L,40017L,
40018L,40019L,40020L,40021L,40022L,40023L,40024L,40025L,40026L,40027L,
40028L,40029L,40030L,40031L,40032L,40033L,40034L,40035L,40036L,40037L,
40038L,40039L,40040L,40041L,40042L,40043L,40044L,40045L,40046L,40047L,
40048L,40049L,40050L,40051L,40052L,40053L,40054L,40055L,40056L,40057L,
40058L,40059L,40060L,40061L,40062L,40063L,40064L,40065L,40066L,40067L,
40068L,40069L,40070L,40071L,40072L,40073L,40074L,40075L,40076L,40077L,
40078L,40079L,40080L,40081L,40082L,40083L,40084L,40085L,40086L,40087L,
40088L,40089L,40090L,40091L,40092L,40093L,40094L,40095L,40096L,40097L,
40098L,40099L,40100L,40101L,40102L,40103L,40104L,40105L,40106L,40107L,
40108L,40109L,40110L,40111L,40112L,40113L,40114L,40115L,40116L,40117L,
40118L,40119L,40120L,40121L,40122L,40123L,40124L,40125L,40126L,40127L,
40128L,40129L,40130L,40131L,40132L,40133L,40134L,40135L,40136L,40137L,
40138L,40139L,40140L,40141L,40142L,40143L,40144L,40145L,40146L,40147L,
40148L,40149L,40150L,40151L,40152L,40153L,40154L,40155L,40156L,40157L,
40158L,40159L,40160L,40161L,40162L,40163L,40164L,40165L,40166L,40167L,
40168L,40169L,40170L,40171L,40172L,40173L,40174L,40175L,40176L,40177L,
40178L,40179L,40180L,40181L,40182L,40183L,40184L,40185L,40186L,40187L,
40188L,40189L,40190L,40191L,40192L,40193L,40194L,40195L,40196L,40197L,
40198L,40199L,40200L,40201L,40202L,40203L,40204L,40205L,40206L,40207L,
40208L,40209L,40210L,40211L,40212L,40213L,40214L,40215L,40216L,40217L,
40218L,40219L,40220L,40221L,40222L,40223L,40224L,40225L,40226L,40227L,
40228L,40229L,40230L,40231L,40232L,40233L,40234L,40235L,40236L,40237L,
40238L,40239L,40240L,40241L,40242L,40243L,40244L,40245L,40246L,40247L,
40248L,40249L,40250L,40251L,40252L,40253L,40254L,40255L,40256L,40257L,
40258L,40259L,40260L,40261L,40262L,40263L,40264L,40265L,40266L,40267L,
40268L,40269L,40270L,40271L,40272L,40273L,40274L,40275L,40276L,40277L,
40278L,40279L,40280L,40281L,40282L,40283L,40284L,40285L,40286L,40287L,
40288L,40289L,40290L,40291L,40292L,40293L,40294L,40295L,40296L,40297L,
40298L,40299L,40300L,40301L,40302L,40303L,40304L,40305L,40306L,40307L,
40308L,40309L,40310L,40311L,40312L,40313L,40314L,40315L,40316L,40317L,
40318L,40319L,40320L,40321L,40322L,40323L,40324L,40325L,40326L,40327L,
40328L,40329L,40330L,40331L,40332L,40333L,40334L,40335L,40336L,40337L,
40338L,40339L,40340L,40341L,40342L,40343L,40344L,40345L,40346L,40347L,
40348L,40349L,40350L,40351L,40352L,40353L,40354L,40355L,40356L,40357L,
40358L,40359L,40360L,40361L,40362L,40363L,40364L,40365L,40366L,40367L,
40368L,40369L,40370L,40371L,40372L,40373L,40374L,40375L,40376L,40377L,
40378L,40379L,40380L,40381L,40382L,40383L,40384L,40385L,40386L,40387L,
40388L,40389L,40390L,40391L,40392L,40393L,40394L,40395L,40396L,40397L,
40398L,40399L,40400L,40401L,40402L,40403L,40404L,40405L,40406L,40407L,
40408L,40409L,40410L,40411L,40412L,40413L,40414L,40415L,40416L,40417L,
40418L,40419L,40420L,40421L,40422L,40423L,40424L,40425L,40426L,40427L,
40428L,40429L,40430L,40431L,40432L,40433L,40434L,40435L,40436L,40437L,
40438L,40439L,40440L,40441L,40442L,40443L,40444L,40445L,40446L,40447L,
40448L,40449L,40450L,40451L,40452L,40453L,40454L,40455L,40456L,40457L,
40458L,40459L,40460L,40461L,40462L,40463L,40464L,40465L,40466L,40467L,
40468L,40469L,40470L,40471L,40472L,40473L,40474L,40475L,40476L,40477L,
40478L,40479L,40480L,40481L,40482L,40483L,40484L,40485L,40486L,40487L,
40488L,40489L,40490L,40491L,40492L,40493L,40494L,40495L,40496L,40497L,
40498L,40499L,40500L,40501L,40502L,40503L,40504L,40505L,40506L,40507L,
40508L,40509L,40510L,40511L,40512L,40513L,40514L,40515L,40516L,40517L,
40518L,40519L,40520L,40521L,40522L,40523L,40524L,40525L,40526L,40527L,
40528L,40529L,40530L,40531L,40532L,40533L,40534L,40535L,40536L,40537L,
40538L,40539L,40540L,40541L,40542L,40543L,40544L,40545L,40546L,40547L,
40548L,40549L,40550L,40551L,40552L,40553L,40554L,40555L,40556L,40557L,
40558L,40559L,40560L,40561L,40562L,40563L,40564L,40565L,40566L,40567L,
40568L,40569L,40570L,40571L,40572L,40573L,40574L,40575L,40576L,40577L,
40578L,40579L,40580L,40581L,40582L,40583L,40584L,40585L,40586L,40587L,
40588L,40589L,40590L,40591L,40592L,40593L,40594L,40595L,40596L,40597L,
40598L,40599L,40600L,40601L,40602L,40603L,40604L,40605L,40606L,40607L,
40608L,40609L,40610L,40611L,40612L,40613L,40614L,40615L,40616L,40617L,
40618L,40619L,40620L,40621L,40622L,40623L,40624L,40625L,40626L,40627L,
40628L,40629L,40630L,40631L,40632L,40633L,40634L,40635L,40636L,40637L,
40638L,40639L,40640L,40641L,40642L,40643L,40644L,40645L,40646L,40647L,
40648L,40649L,40650L,40651L,40652L,40653L,40654L,40655L,40656L,40657L,
40658L,40659L,40660L,40661L,40662L,40663L,40664L,40665L,40666L,40667L,
40668L,40669L,40670L,40671L,40672L,40673L,40674L,40675L,40676L,40677L,
40678L,40679L,40680L,40681L,40682L,40683L,40684L,40685L,40686L,40687L,
40688L,40689L,40690L,40691L,40692L,40693L,40694L,40695L,40696L,40697L,
40698L,40699L,40700L,40701L,40702L,40703L,40704L,40705L,40706L,40707L,
40708L,40709L,40710L,40711L,40712L,40713L,40714L,40715L,40716L,40717L,
40718L,40719L,40720L,40721L,40722L,40723L,40724L,40725L,40726L,40727L,
40728L,40729L,40730L,40731L,40732L,40733L,40734L,40735L,40736L,40737L,
40738L,40739L,40740L,40741L,40742L,40743L,40744L,40745L,40746L,40747L,
40748L,40749L,40750L,40751L,40752L,40753L,40754L,40755L,40756L,40757L,
40758L,40759L,40760L,40761L,40762L,40763L,40764L,40765L,40766L,40767L,
40768L,40769L,40770L,40771L,40772L,40773L,40774L,40775L,40776L,40777L,
40778L,40779L,40780L,40781L,40782L,40783L,40784L,40785L,40786L,40787L,
40788L,40789L,40790L,40791L,40792L,40793L,40794L,40795L,40796L,40797L,
40798L,40799L,40800L,40801L,40802L,40803L,40804L,40805L,40806L,40807L,
40808L,40809L,40810L,40811L,40812L,40813L,40814L,40815L,40816L,40817L,
40818L,40819L,40820L,40821L,40822L,40823L,40824L,40825L,40826L,40827L,
40828L,40829L,40830L,40831L,40832L,40833L,40834L,40835L,40836L,40837L,
40838L,40839L,40840L,40841L,40842L,40843L,40844L,40845L,40846L,40847L,
40848L,40849L,40850L,40851L,40852L,40853L,40854L,40855L,40856L,40857L,
40858L,40859L,40860L,40861L,40862L,40863L,40864L,40865L,40866L,40867L,
40868L,40869L,40870L,40871L,40872L,40873L,40874L,40875L,40876L,40877L,
40878L,40879L,40880L,40881L,40882L,40883L,40884L,40885L,40886L,40887L,
40888L,40889L,40890L,40891L,40892L,40893L,40894L,40895L,40896L,40897L,
40898L,40899L,40900L,40901L,40902L,40903L,40904L,40905L,40906L,40907L,
40908L,40909L,40910L,40911L,40912L,40913L,40914L,40915L,40916L,40917L,
40918L,40919L,40920L,40921L,40922L,40923L,40924L,40925L,40926L,40927L,
40928L,40929L,40930L,40931L,40932L,40933L,40934L,40935L,40936L,40937L,
40938L,40939L,40940L,40941L,40942L,40943L,40944L,40945L,40946L,40947L,
40948L,40949L,40950L,40951L,40952L,40953L,40954L,40955L,40956L,40957L,
40958L,40959L,40960L,40961L,40962L,40963L,40964L,40965L,40966L,40967L,
40968L,40969L,40970L,40971L,40972L,40973L,40974L,40975L,40976L,40977L,
40978L,40979L,40980L,40981L,40982L,40983L,40984L,40985L,40986L,40987L,
40988L,40989L,40990L,40991L,40992L,40993L,40994L,40995L,40996L,40997L,
40998L,40999L,41000L,41001L,41002L,41003L,41004L,41005L,41006L,41007L,
41008L,41009L,41010L,41011L,41012L,41013L,41014L,41015L,41016L,41017L,
41018L,41019L,41020L,41021L,41022L,41023L,41024L,41025L,41026L,41027L,
41028L,41029L,41030L,41031L,41032L,41033L,41034L,41035L,41036L,41037L,
41038L,41039L,41040L,41041L,41042L,41043L,41044L,41045L,41046L,41047L,
41048L,41049L,41050L,41051L,41052L,41053L,41054L,41055L,41056L,41057L,
41058L,41059L,41060L,41061L,41062L,41063L,41064L,41065L,41066L,41067L,
41068L,41069L,41070L,41071L,41072L,41073L,41074L,41075L,41076L,41077L,
41078L,41079L,41080L,41081L,41082L,41083L,41084L,41085L,41086L,41087L,
41088L,41089L,41090L,41091L,41092L,41093L,41094L,41095L,41096L,41097L,
41098L,41099L,41100L,41101L,41102L,41103L,41104L,41105L,41106L,41107L,
41108L,41109L,41110L,41111L,41112L,41113L,41114L,41115L,41116L,41117L,
41118L,41119L,41120L,41121L,41122L,41123L,41124L,41125L,41126L,41127L,
41128L,41129L,41130L,41131L,41132L,41133L,41134L,41135L,41136L,41137L,
41138L,41139L,41140L,41141L,41142L,41143L,41144L,41145L,41146L,41147L,
41148L,41149L,41150L,41151L,41152L,41153L,41154L,41155L,41156L,41157L,
41158L,41159L,41160L,41161L,41162L,41163L,41164L,41165L,41166L,41167L,
41168L,41169L,41170L,41171L,41172L,41173L,41174L,41175L,41176L,41177L,
41178L,41179L,41180L,41181L,41182L,41183L,41184L,41185L,41186L,41187L,
41188L,41189L,41190L,41191L,41192L,41193L,41194L,41195L,41196L,41197L,
41198L,41199L,41200L,41201L,41202L,41203L,41204L,41205L,41206L,41207L,
41208L,41209L,41210L,41211L,41212L,41213L,41214L,41215L,41216L,41217L,
41218L,41219L,41220L,41221L,41222L,41223L,41224L,41225L,41226L,41227L,
41228L,41229L,41230L,41231L,41232L,41233L,41234L,41235L,41236L,41237L,
41238L,41239L,41240L,41241L,41242L,41243L,41244L,41245L,41246L,41247L,
41248L,41249L,41250L,41251L,41252L,41253L,41254L,41255L,41256L,41257L,
41258L,41259L,41260L,41261L,41262L,41263L,41264L,41265L,41266L,41267L,
41268L,41269L,41270L,41271L,41272L,41273L,41274L,41275L,41276L,41277L,
41278L,41279L,41280L,41281L,41282L,41283L,41284L,41285L,41286L,41287L,
41288L,41289L,41290L,41291L,41292L,41293L,41294L,41295L,41296L,41297L,
41298L,41299L,41300L,41301L,41302L,41303L,41304L,41305L,41306L,41307L,
41308L,41309L,41310L,41311L,41312L,41313L,41314L,41315L,41316L,41317L,
41318L,41319L,41320L,41321L,41322L,41323L,41324L,41325L,41326L,41327L,
41328L,41329L,41330L,41331L,41332L,41333L,41334L,41335L,41336L,41337L,
41338L,41339L,41340L,41341L,41342L,41343L,41344L,41345L,41346L,41347L,
41348L,41349L,41350L,41351L,41352L,41353L,41354L,41355L,41356L,41357L,
41358L,41359L,41360L,41361L,41362L,41363L,41364L,41365L,41366L,41367L,
41368L,41369L,41370L,41371L,41372L,41373L,41374L,41375L,41376L,41377L,
41378L,41379L,41380L,41381L,41382L,41383L,41384L,41385L,41386L,41387L,
41388L,41389L,41390L,41391L,41392L,41393L,41394L,41395L,41396L,41397L,
41398L,41399L,41400L,41401L,41402L,41403L,41404L,41405L,41406L,41407L,
41408L,41409L,41410L,41411L,41412L,41413L,41414L,41415L,41416L,41417L,
41418L,41419L,41420L,41421L,41422L,41423L,41424L,41425L,41426L,41427L,
41428L,41429L,41430L,41431L,41432L,41433L,41434L,41435L,41436L,41437L,
41438L,41439L,41440L,41441L,41442L,41443L,41444L,41445L,41446L,41447L,
41448L,41449L,41450L,41451L,41452L,41453L,41454L,41455L,41456L,41457L,
41458L,41459L,41460L,41461L,41462L,41463L,41464L,41465L,41466L,41467L,
41468L,41469L,41470L,41471L,41472L,41473L,41474L,41475L,41476L,41477L,
41478L,41479L,41480L,41481L,41482L,41483L,41484L,41485L,41486L,41487L,
41488L,41489L,41490L,41491L,41492L,41493L,41494L,41495L,41496L,41497L,
41498L,41499L,41500L,41501L,41502L,41503L,41504L,41505L,41506L,41507L,
41508L,41509L,41510L,41511L,41512L,41513L,41514L,41515L,41516L,41517L,
41518L,41519L,41520L,41521L,41522L,41523L,41524L,41525L,41526L,41527L,
41528L,41529L,41530L,41531L,41532L,41533L,41534L,41535L,41536L,41537L,
41538L,41539L,41540L,41541L,41542L,41543L,41544L,41545L,41546L,41547L,
41548L,41549L,41550L,41551L,41552L,41553L,41554L,41555L,41556L,41557L,
41558L,41559L,41560L,41561L,41562L,41563L,41564L,41565L,41566L,41567L,
41568L,41569L,41570L,41571L,41572L,41573L,41574L,41575L,41576L,41577L,
41578L,41579L,41580L,41581L,41582L,41583L,41584L,41585L,41586L,41587L,
41588L,41589L,41590L,41591L,41592L,41593L,41594L,41595L,41596L,41597L,
41598L,41599L,41600L,41601L,41602L,41603L,41604L,41605L,41606L,41607L,
41608L,41609L,41610L,41611L,41612L,41613L,41614L,41615L,41616L,41617L,
41618L,41619L,41620L,41621L,41622L,41623L,41624L,41625L,41626L,41627L,
41628L,41629L,41630L,41631L,41632L,41633L,41634L,41635L,41636L,41637L,
41638L,41639L,41640L,41641L,41642L,41643L,41644L,41645L,41646L,41647L,
41648L,41649L,41650L,41651L,41652L,41653L,41654L,41655L,41656L,41657L,
41658L,41659L,41660L,41661L,41662L,41663L,41664L,41665L,41666L,41667L,
41668L,41669L,41670L,41671L,41672L,41673L,41674L,41675L,41676L,41677L,
41678L,41679L,41680L,41681L,41682L,41683L,41684L,41685L,41686L,41687L,
41688L,41689L,41690L,41691L,41692L,41693L,41694L,41695L,41696L,41697L,
41698L,41699L,41700L,41701L,41702L,41703L,41704L,41705L,41706L,41707L,
41708L,41709L,41710L,41711L,41712L,41713L,41714L,41715L,41716L,41717L,
41718L,41719L,41720L,41721L,41722L,41723L,41724L,41725L,41726L,41727L,
41728L,41729L,41730L,41731L,41732L,41733L,41734L,41735L,41736L,41737L,
41738L,41739L,41740L,41741L,41742L,41743L,41744L,41745L,41746L,41747L,
41748L,41749L,41750L,41751L,41752L,41753L,41754L,41755L,41756L,41757L,
41758L,41759L,41760L,41761L,41762L,41763L,41764L,41765L,41766L,41767L,
41768L,41769L,41770L,41771L,41772L,41773L,41774L,41775L,41776L,41777L,
41778L,41779L,41780L,41781L,41782L,41783L,41784L,41785L,41786L,41787L,
41788L,41789L,41790L,41791L,41792L,41793L,41794L,41795L,41796L,41797L,
41798L,41799L,41800L,41801L,41802L,41803L,41804L,41805L,41806L,41807L,
41808L,41809L,41810L,41811L,41812L,41813L,41814L,41815L,41816L,41817L,
41818L,41819L,41820L,41821L,41822L,41823L,41824L,41825L,41826L,41827L,
41828L,41829L,41830L,41831L,41832L,41833L,41834L,41835L,41836L,41837L,
41838L,41839L,41840L,41841L,41842L,41843L,41844L,41845L,41846L,41847L,
41848L,41849L,41850L,41851L,41852L,41853L,41854L,41855L,41856L,41857L,
41858L,41859L,41860L,41861L,41862L,41863L,41864L,41865L,41866L,41867L,
41868L,41869L,41870L,41871L,41872L,41873L,41874L,41875L,41876L,41877L,
41878L,41879L,41880L,41881L,41882L,41883L,41884L,41885L,41886L,41887L,
41888L,41889L,41890L,41891L,41892L,41893L,41894L,41895L,41896L,41897L,
41898L,41899L,41900L,41901L,41902L,41903L,41904L,41905L,41906L,41907L,
41908L,41909L,41910L,41911L,41912L,41913L,41914L,41915L,41916L,41917L,
41918L,41919L,41920L,41921L,41922L,41923L,41924L,41925L,41926L,41927L,
41928L,41929L,41930L,41931L,41932L,41933L,41934L,41935L,41936L,41937L,
41938L,41939L,41940L,41941L,41942L,41943L,41944L,41945L,41946L,41947L,
41948L,41949L,41950L,41951L,41952L,41953L,41954L,41955L,41956L,41957L,
41958L,41959L,41960L,41961L,41962L,41963L,41964L,41965L,41966L,41967L,
41968L,41969L,41970L,41971L,41972L,41973L,41974L,41975L,41976L,41977L,
41978L,41979L,41980L,41981L,41982L,41983L,41984L,41985L,41986L,41987L,
41988L,41989L,41990L,41991L,41992L,41993L,41994L,41995L,41996L,41997L,
41998L,41999L,42000L,42001L,42002L,42003L,42004L,42005L,42006L,42007L,
42008L,42009L,42010L,42011L,42012L,42013L,42014L,42015L,42016L,42017L,
42018L,42019L,42020L,42021L,42022L,42023L,42024L,42025L,42026L,42027L,
42028L,42029L,42030L,42031L,42032L,42033L,42034L,42035L,42036L,42037L,
42038L,42039L,42040L,42041L,42042L,42043L,42044L,42045L,42046L,42047L,
42048L,42049L,42050L,42051L,42052L,42053L,42054L,42055L,42056L,42057L,
42058L,42059L,42060L,42061L,42062L,42063L,42064L,42065L,42066L,42067L,
42068L,42069L,42070L,42071L,42072L,42073L,42074L,42075L,42076L,42077L,
42078L,42079L,42080L,42081L,42082L,42083L,42084L,42085L,42086L,42087L,
42088L,42089L,42090L,42091L,42092L,42093L,42094L,42095L,42096L,42097L,
42098L,42099L,42100L,42101L,42102L,42103L,42104L,42105L,42106L,42107L,
42108L,42109L,42110L,42111L,42112L,42113L,42114L,42115L,42116L,42117L,
42118L,42119L,42120L,42121L,42122L,42123L,42124L,42125L,42126L,42127L,
42128L,42129L,42130L,42131L,42132L,42133L,42134L,42135L,42136L,42137L,
42138L,42139L,42140L,42141L,42142L,42143L,42144L,42145L,42146L,42147L,
42148L,42149L,42150L,42151L,42152L,42153L,42154L,42155L,42156L,42157L,
42158L,42159L,42160L,42161L,42162L,42163L,42164L,42165L,42166L,42167L,
42168L,42169L,42170L,42171L,42172L,42173L,42174L,42175L,42176L,42177L,
42178L,42179L,42180L,42181L,42182L,42183L,42184L,42185L,42186L,42187L,
42188L,42189L,42190L,42191L,42192L,42193L,42194L,42195L,42196L,42197L,
42198L,42199L,42200L,42201L,42202L,42203L,42204L,42205L,42206L,42207L,
42208L,42209L,42210L,42211L,42212L,42213L,42214L,42215L,42216L,42217L,
42218L,42219L,42220L,42221L,42222L,42223L,42224L,42225L,42226L,42227L,
42228L,42229L,42230L,42231L,42232L,42233L,42234L,42235L,42236L,42237L,
42238L,42239L,42240L,42241L,42242L,42243L,42244L,42245L,42246L,42247L,
42248L,42249L,42250L,42251L,42252L,42253L,42254L,42255L,42256L,42257L,
42258L,42259L,42260L,42261L,42262L,42263L,42264L,42265L,42266L,42267L,
42268L,42269L,42270L,42271L,42272L,42273L,42274L,42275L,42276L,42277L,
42278L,42279L,42280L,42281L,42282L,42283L,42284L,42285L,42286L,42287L,
42288L,42289L,42290L,42291L,42292L,42293L,42294L,42295L,42296L,42297L,
42298L,42299L,42300L,42301L,42302L,42303L,42304L,42305L,42306L,42307L,
42308L,42309L,42310L,42311L,42312L,42313L,42314L,42315L,42316L,42317L,
42318L,42319L,42320L,42321L,42322L,42323L,42324L,42325L,42326L,42327L,
42328L,42329L,42330L,42331L,42332L,42333L,42334L,42335L,42336L,42337L,
42338L,42339L,42340L,42341L,42342L,42343L,42344L,42345L,42346L,42347L,
42348L,42349L,42350L,42351L,42352L,42353L,42354L,42355L,42356L,42357L,
42358L,42359L,42360L,42361L,42362L,42363L,42364L,42365L,42366L,42367L,
42368L,42369L,42370L,42371L,42372L,42373L,42374L,42375L,42376L,42377L,
42378L,42379L,42380L,42381L,42382L,42383L,42384L,42385L,42386L,42387L,
42388L,42389L,42390L,42391L,42392L,42393L,42394L,42395L,42396L,42397L,
42398L,42399L,42400L,42401L,42402L,42403L,42404L,42405L,42406L,42407L,
42408L,42409L,42410L,42411L,42412L,42413L,42414L,42415L,42416L,42417L,
42418L,42419L,42420L,42421L,42422L,42423L,42424L,42425L,42426L,42427L,
42428L,42429L,42430L,42431L,42432L,42433L,42434L,42435L,42436L,42437L,
42438L,42439L,42440L,42441L,42442L,42443L,42444L,42445L,42446L,42447L,
42448L,42449L,42450L,42451L,42452L,42453L,42454L,42455L,42456L,42457L,
42458L,42459L,42460L,42461L,42462L,42463L,42464L,42465L,42466L,42467L,
42468L,42469L,42470L,42471L,42472L,42473L,42474L,42475L,42476L,42477L,
42478L,42479L,42480L,42481L,42482L,42483L,42484L,42485L,42486L,42487L,
42488L,42489L,42490L,42491L,42492L,42493L,42494L,42495L,42496L,42497L,
42498L,42499L,42500L,42501L,42502L,42503L,42504L,42505L,42506L,42507L,
42508L,42509L,42510L,42511L,42512L,42513L,42514L,42515L,42516L,42517L,
42518L,42519L,42520L,42521L,42522L,42523L,42524L,42525L,42526L,42527L,
42528L,42529L,42530L,42531L,42532L,42533L,42534L,42535L,42536L,42537L,
42538L,42539L,42540L,42541L,42542L,42543L,42544L,42545L,42546L,42547L,
42548L,42549L,42550L,42551L,42552L,42553L,42554L,42555L,42556L,42557L,
42558L,42559L,42560L,42560L,42562L,42562L,42564L,42564L,42566L,42566L,
42568L,42568L,42570L,42570L,42572L,42572L,42574L,42574L,42576L,42576L,
42578L,42578L,42580L,42580L,42582L,42582L,42584L,42584L,42586L,42586L,
42588L,42588L,42590L,42590L,42592L,42592L,42594L,42594L,42596L,42596L,
42598L,42598L,42600L,42600L,42602L,42602L,42604L,42604L,42606L,42607L,
42608L,42609L,42610L,42611L,42612L,42613L,42614L,42615L,42616L,42617L,
42618L,42619L,42620L,42621L,42622L,42623L,42624L,42624L,42626L,42626L,
42628L,42628L,42630L,42630L,42632L,42632L,42634L,42634L,42636L,42636L,
42638L,42638L,42640L,42640L,42642L,42642L,42644L,42644L,42646L,42646L,
42648L,42648L,42650L,42650L,42652L,42653L,42654L,42655L,42656L,42657L,
42658L,42659L,42660L,42661L,42662L,42663L,42664L,42665L,42666L,42667L,
42668L,42669L,42670L,42671L,42672L,42673L,42674L,42675L,42676L,42677L,
42678L,42679L,42680L,42681L,42682L,42683L,42684L,42685L,42686L,42687L,
42688L,42689L,42690L,42691L,42692L,42693L,42694L,42695L,42696L,42697L,
42698L,42699L,42700L,42701L,42702L,42703L,42704L,42705L,42706L,42707L,
42708L,42709L,42710L,42711L,42712L,42713L,42714L,42715L,42716L,42717L,
42718L,42719L,42720L,42721L,42722L,42723L,42724L,42725L,42726L,42727L,
42728L,42729L,42730L,42731L,42732L,42733L,42734L,42735L,42736L,42737L,
42738L,42739L,42740L,42741L,42742L,42743L,42744L,42745L,42746L,42747L,
42748L,42749L,42750L,42751L,42752L,42753L,42754L,42755L,42756L,42757L,
42758L,42759L,42760L,42761L,42762L,42763L,42764L,42765L,42766L,42767L,
42768L,42769L,42770L,42771L,42772L,42773L,42774L,42775L,42776L,42777L,
42778L,42779L,42780L,42781L,42782L,42783L,42784L,42785L,42786L,42786L,
42788L,42788L,42790L,42790L,42792L,42792L,42794L,42794L,42796L,42796L,
42798L,42798L,42800L,42801L,42802L,42802L,42804L,42804L,42806L,42806L,
42808L,42808L,42810L,42810L,42812L,42812L,42814L,42814L,42816L,42816L,
42818L,42818L,42820L,42820L,42822L,42822L,42824L,42824L,42826L,42826L,
42828L,42828L,42830L,42830L,42832L,42832L,42834L,42834L,42836L,42836L,
42838L,42838L,42840L,42840L,42842L,42842L,42844L,42844L,42846L,42846L,
42848L,42848L,42850L,42850L,42852L,42852L,42854L,42854L,42856L,42856L,
42858L,42858L,42860L,42860L,42862L,42862L,42864L,42865L,42866L,42867L,
42868L,42869L,42870L,42871L,42872L,42873L,42873L,42875L,42875L,42877L,
42878L,42878L,42880L,42880L,42882L,42882L,42884L,42884L,42886L,42886L,
42888L,42889L,42890L,42891L,42891L,42893L,42894L,42895L,42896L,42896L,
42898L,42898L,42948L,42901L,42902L,42902L,42904L,42904L,42906L,42906L,
42908L,42908L,42910L,42910L,42912L,42912L,42914L,42914L,42916L,42916L,
42918L,42918L,42920L,42920L,42922L,42923L,42924L,42925L,42926L,42927L,
42928L,42929L,42930L,42931L,42932L,42932L,42934L,42934L,42936L,42936L,
42938L,42938L,42940L,42940L,42942L,42942L,42944L,42945L,42946L,42946L,
42948L,42949L,42950L,42951L,42952L,42953L,42954L,42955L,42956L,42957L,
42958L,42959L,42960L,42961L,42962L,42963L,42964L,42965L,42966L,42967L,
42968L,42969L,42970L,42971L,42972L,42973L,42974L,42975L,42976L,42977L,
42978L,42979L,42980L,42981L,42982L,42983L,42984L,42985L,42986L,42987L,
42988L,42989L,42990L,42991L,42992L,42993L,42994L,42995L,42996L,42997L,
42998L,42999L,43000L,43001L,43002L,43003L,43004L,43005L,43006L,43007L,
43008L,43009L,43010L,43011L,43012L,43013L,43014L,43015L,43016L,43017L,
43018L,43019L,43020L,43021L,43022L,43023L,43024L,43025L,43026L,43027L,
43028L,43029L,43030L,43031L,43032L,43033L,43034L,43035L,43036L,43037L,
43038L,43039L,43040L,43041L,43042L,43043L,43044L,43045L,43046L,43047L,
43048L,43049L,43050L,43051L,43052L,43053L,43054L,43055L,43056L,43057L,
43058L,43059L,43060L,43061L,43062L,43063L,43064L,43065L,43066L,43067L,
43068L,43069L,43070L,43071L,43072L,43073L,43074L,43075L,43076L,43077L,
43078L,43079L,43080L,43081L,43082L,43083L,43084L,43085L,43086L,43087L,
43088L,43089L,43090L,43091L,43092L,43093L,43094L,43095L,43096L,43097L,
43098L,43099L,43100L,43101L,43102L,43103L,43104L,43105L,43106L,43107L,
43108L,43109L,43110L,43111L,43112L,43113L,43114L,43115L,43116L,43117L,
43118L,43119L,43120L,43121L,43122L,43123L,43124L,43125L,43126L,43127L,
43128L,43129L,43130L,43131L,43132L,43133L,43134L,43135L,43136L,43137L,
43138L,43139L,43140L,43141L,43142L,43143L,43144L,43145L,43146L,43147L,
43148L,43149L,43150L,43151L,43152L,43153L,43154L,43155L,43156L,43157L,
43158L,43159L,43160L,43161L,43162L,43163L,43164L,43165L,43166L,43167L,
43168L,43169L,43170L,43171L,43172L,43173L,43174L,43175L,43176L,43177L,
43178L,43179L,43180L,43181L,43182L,43183L,43184L,43185L,43186L,43187L,
43188L,43189L,43190L,43191L,43192L,43193L,43194L,43195L,43196L,43197L,
43198L,43199L,43200L,43201L,43202L,43203L,43204L,43205L,43206L,43207L,
43208L,43209L,43210L,43211L,43212L,43213L,43214L,43215L,43216L,43217L,
43218L,43219L,43220L,43221L,43222L,43223L,43224L,43225L,43226L,43227L,
43228L,43229L,43230L,43231L,43232L,43233L,43234L,43235L,43236L,43237L,
43238L,43239L,43240L,43241L,43242L,43243L,43244L,43245L,43246L,43247L,
43248L,43249L,43250L,43251L,43252L,43253L,43254L,43255L,43256L,43257L,
43258L,43259L,43260L,43261L,43262L,43263L,43264L,43265L,43266L,43267L,
43268L,43269L,43270L,43271L,43272L,43273L,43274L,43275L,43276L,43277L,
43278L,43279L,43280L,43281L,43282L,43283L,43284L,43285L,43286L,43287L,
43288L,43289L,43290L,43291L,43292L,43293L,43294L,43295L,43296L,43297L,
43298L,43299L,43300L,43301L,43302L,43303L,43304L,43305L,43306L,43307L,
43308L,43309L,43310L,43311L,43312L,43313L,43314L,43315L,43316L,43317L,
43318L,43319L,43320L,43321L,43322L,43323L,43324L,43325L,43326L,43327L,
43328L,43329L,43330L,43331L,43332L,43333L,43334L,43335L,43336L,43337L,
43338L,43339L,43340L,43341L,43342L,43343L,43344L,43345L,43346L,43347L,
43348L,43349L,43350L,43351L,43352L,43353L,43354L,43355L,43356L,43357L,
43358L,43359L,43360L,43361L,43362L,43363L,43364L,43365L,43366L,43367L,
43368L,43369L,43370L,43371L,43372L,43373L,43374L,43375L,43376L,43377L,
43378L,43379L,43380L,43381L,43382L,43383L,43384L,43385L,43386L,43387L,
43388L,43389L,43390L,43391L,43392L,43393L,43394L,43395L,43396L,43397L,
43398L,43399L,43400L,43401L,43402L,43403L,43404L,43405L,43406L,43407L,
43408L,43409L,43410L,43411L,43412L,43413L,43414L,43415L,43416L,43417L,
43418L,43419L,43420L,43421L,43422L,43423L,43424L,43425L,43426L,43427L,
43428L,43429L,43430L,43431L,43432L,43433L,43434L,43435L,43436L,43437L,
43438L,43439L,43440L,43441L,43442L,43443L,43444L,43445L,43446L,43447L,
43448L,43449L,43450L,43451L,43452L,43453L,43454L,43455L,43456L,43457L,
43458L,43459L,43460L,43461L,43462L,43463L,43464L,43465L,43466L,43467L,
43468L,43469L,43470L,43471L,43472L,43473L,43474L,43475L,43476L,43477L,
43478L,43479L,43480L,43481L,43482L,43483L,43484L,43485L,43486L,43487L,
43488L,43489L,43490L,43491L,43492L,43493L,43494L,43495L,43496L,43497L,
43498L,43499L,43500L,43501L,43502L,43503L,43504L,43505L,43506L,43507L,
43508L,43509L,43510L,43511L,43512L,43513L,43514L,43515L,43516L,43517L,
43518L,43519L,43520L,43521L,43522L,43523L,43524L,43525L,43526L,43527L,
43528L,43529L,43530L,43531L,43532L,43533L,43534L,43535L,43536L,43537L,
43538L,43539L,43540L,43541L,43542L,43543L,43544L,43545L,43546L,43547L,
43548L,43549L,43550L,43551L,43552L,43553L,43554L,43555L,43556L,43557L,
43558L,43559L,43560L,43561L,43562L,43563L,43564L,43565L,43566L,43567L,
43568L,43569L,43570L,43571L,43572L,43573L,43574L,43575L,43576L,43577L,
43578L,43579L,43580L,43581L,43582L,43583L,43584L,43585L,43586L,43587L,
43588L,43589L,43590L,43591L,43592L,43593L,43594L,43595L,43596L,43597L,
43598L,43599L,43600L,43601L,43602L,43603L,43604L,43605L,43606L,43607L,
43608L,43609L,43610L,43611L,43612L,43613L,43614L,43615L,43616L,43617L,
43618L,43619L,43620L,43621L,43622L,43623L,43624L,43625L,43626L,43627L,
43628L,43629L,43630L,43631L,43632L,43633L,43634L,43635L,43636L,43637L,
43638L,43639L,43640L,43641L,43642L,43643L,43644L,43645L,43646L,43647L,
43648L,43649L,43650L,43651L,43652L,43653L,43654L,43655L,43656L,43657L,
43658L,43659L,43660L,43661L,43662L,43663L,43664L,43665L,43666L,43667L,
43668L,43669L,43670L,43671L,43672L,43673L,43674L,43675L,43676L,43677L,
43678L,43679L,43680L,43681L,43682L,43683L,43684L,43685L,43686L,43687L,
43688L,43689L,43690L,43691L,43692L,43693L,43694L,43695L,43696L,43697L,
43698L,43699L,43700L,43701L,43702L,43703L,43704L,43705L,43706L,43707L,
43708L,43709L,43710L,43711L,43712L,43713L,43714L,43715L,43716L,43717L,
43718L,43719L,43720L,43721L,43722L,43723L,43724L,43725L,43726L,43727L,
43728L,43729L,43730L,43731L,43732L,43733L,43734L,43735L,43736L,43737L,
43738L,43739L,43740L,43741L,43742L,43743L,43744L,43745L,43746L,43747L,
43748L,43749L,43750L,43751L,43752L,43753L,43754L,43755L,43756L,43757L,
43758L,43759L,43760L,43761L,43762L,43763L,43764L,43765L,43766L,43767L,
43768L,43769L,43770L,43771L,43772L,43773L,43774L,43775L,43776L,43777L,
43778L,43779L,43780L,43781L,43782L,43783L,43784L,43785L,43786L,43787L,
43788L,43789L,43790L,43791L,43792L,43793L,43794L,43795L,43796L,43797L,
43798L,43799L,43800L,43801L,43802L,43803L,43804L,43805L,43806L,43807L,
43808L,43809L,43810L,43811L,43812L,43813L,43814L,43815L,43816L,43817L,
43818L,43819L,43820L,43821L,43822L,43823L,43824L,43825L,43826L,43827L,
43828L,43829L,43830L,43831L,43832L,43833L,43834L,43835L,43836L,43837L,
43838L,43839L,43840L,43841L,43842L,43843L,43844L,43845L,43846L,43847L,
43848L,43849L,43850L,43851L,43852L,43853L,43854L,43855L,43856L,43857L,
43858L,42931L,43860L,43861L,43862L,43863L,43864L,43865L,43866L,43867L,
43868L,43869L,43870L,43871L,43872L,43873L,43874L,43875L,43876L,43877L,
43878L,43879L,43880L,43881L,43882L,43883L,43884L,43885L,43886L,43887L,5024,
5025,5026,5027,5028,5029,5030,5031,5032,5033,5034,5035,5036,5037,5038,5039,
5040,5041,5042,5043,5044,5045,5046,5047,5048,5049,5050,5051,5052,5053,5054,
5055,5056,5057,5058,5059,5060,5061,5062,5063,5064,5065,5066,5067,5068,5069,
5070,5071,5072,5073,5074,5075,5076,5077,5078,5079,5080,5081,5082,5083,5084,
5085,5086,5087,5088,5089,5090,5091,5092,5093,5094,5095,5096,5097,5098,5099,
5100,5101,5102,5103,43968L,43969L,43970L,43971L,43972L,43973L,43974L,
43975L,43976L,43977L,43978L,43979L,43980L,43981L,43982L,43983L,43984L,
43985L,43986L,43987L,43988L,43989L,43990L,43991L,43992L,43993L,43994L,
43995L,43996L,43997L,43998L,43999L,44000L,44001L,44002L,44003L,44004L,
44005L,44006L,44007L,44008L,44009L,44010L,44011L,44012L,44013L,44014L,
44015L,44016L,44017L,44018L,44019L,44020L,44021L,44022L,44023L,44024L,
44025L,44026L,44027L,44028L,44029L,44030L,44031L,44032L,44033L,44034L,
44035L,44036L,44037L,44038L,44039L,44040L,44041L,44042L,44043L,44044L,
44045L,44046L,44047L,44048L,44049L,44050L,44051L,44052L,44053L,44054L,
44055L,44056L,44057L,44058L,44059L,44060L,44061L,44062L,44063L,44064L,
44065L,44066L,44067L,44068L,44069L,44070L,44071L,44072L,44073L,44074L,
44075L,44076L,44077L,44078L,44079L,44080L,44081L,44082L,44083L,44084L,
44085L,44086L,44087L,44088L,44089L,44090L,44091L,44092L,44093L,44094L,
44095L,44096L,44097L,44098L,44099L,44100L,44101L,44102L,44103L,44104L,
44105L,44106L,44107L,44108L,44109L,44110L,44111L,44112L,44113L,44114L,
44115L,44116L,44117L,44118L,44119L,44120L,44121L,44122L,44123L,44124L,
44125L,44126L,44127L,44128L,44129L,44130L,44131L,44132L,44133L,44134L,
44135L,44136L,44137L,44138L,44139L,44140L,44141L,44142L,44143L,44144L,
44145L,44146L,44147L,44148L,44149L,44150L,44151L,44152L,44153L,44154L,
44155L,44156L,44157L,44158L,44159L,44160L,44161L,44162L,44163L,44164L,
44165L,44166L,44167L,44168L,44169L,44170L,44171L,44172L,44173L,44174L,
44175L,44176L,44177L,44178L,44179L,44180L,44181L,44182L,44183L,44184L,
44185L,44186L,44187L,44188L,44189L,44190L,44191L,44192L,44193L,44194L,
44195L,44196L,44197L,44198L,44199L,44200L,44201L,44202L,44203L,44204L,
44205L,44206L,44207L,44208L,44209L,44210L,44211L,44212L,44213L,44214L,
44215L,44216L,44217L,44218L,44219L,44220L,44221L,44222L,44223L,44224L,
44225L,44226L,44227L,44228L,44229L,44230L,44231L,44232L,44233L,44234L,
44235L,44236L,44237L,44238L,44239L,44240L,44241L,44242L,44243L,44244L,
44245L,44246L,44247L,44248L,44249L,44250L,44251L,44252L,44253L,44254L,
44255L,44256L,44257L,44258L,44259L,44260L,44261L,44262L,44263L,44264L,
44265L,44266L,44267L,44268L,44269L,44270L,44271L,44272L,44273L,44274L,
44275L,44276L,44277L,44278L,44279L,44280L,44281L,44282L,44283L,44284L,
44285L,44286L,44287L,44288L,44289L,44290L,44291L,44292L,44293L,44294L,
44295L,44296L,44297L,44298L,44299L,44300L,44301L,44302L,44303L,44304L,
44305L,44306L,44307L,44308L,44309L,44310L,44311L,44312L,44313L,44314L,
44315L,44316L,44317L,44318L,44319L,44320L,44321L,44322L,44323L,44324L,
44325L,44326L,44327L,44328L,44329L,44330L,44331L,44332L,44333L,44334L,
44335L,44336L,44337L,44338L,44339L,44340L,44341L,44342L,44343L,44344L,
44345L,44346L,44347L,44348L,44349L,44350L,44351L,44352L,44353L,44354L,
44355L,44356L,44357L,44358L,44359L,44360L,44361L,44362L,44363L,44364L,
44365L,44366L,44367L,44368L,44369L,44370L,44371L,44372L,44373L,44374L,
44375L,44376L,44377L,44378L,44379L,44380L,44381L,44382L,44383L,44384L,
44385L,44386L,44387L,44388L,44389L,44390L,44391L,44392L,44393L,44394L,
44395L,44396L,44397L,44398L,44399L,44400L,44401L,44402L,44403L,44404L,
44405L,44406L,44407L,44408L,44409L,44410L,44411L,44412L,44413L,44414L,
44415L,44416L,44417L,44418L,44419L,44420L,44421L,44422L,44423L,44424L,
44425L,44426L,44427L,44428L,44429L,44430L,44431L,44432L,44433L,44434L,
44435L,44436L,44437L,44438L,44439L,44440L,44441L,44442L,44443L,44444L,
44445L,44446L,44447L,44448L,44449L,44450L,44451L,44452L,44453L,44454L,
44455L,44456L,44457L,44458L,44459L,44460L,44461L,44462L,44463L,44464L,
44465L,44466L,44467L,44468L,44469L,44470L,44471L,44472L,44473L,44474L,
44475L,44476L,44477L,44478L,44479L,44480L,44481L,44482L,44483L,44484L,
44485L,44486L,44487L,44488L,44489L,44490L,44491L,44492L,44493L,44494L,
44495L,44496L,44497L,44498L,44499L,44500L,44501L,44502L,44503L,44504L,
44505L,44506L,44507L,44508L,44509L,44510L,44511L,44512L,44513L,44514L,
44515L,44516L,44517L,44518L,44519L,44520L,44521L,44522L,44523L,44524L,
44525L,44526L,44527L,44528L,44529L,44530L,44531L,44532L,44533L,44534L,
44535L,44536L,44537L,44538L,44539L,44540L,44541L,44542L,44543L,44544L,
44545L,44546L,44547L,44548L,44549L,44550L,44551L,44552L,44553L,44554L,
44555L,44556L,44557L,44558L,44559L,44560L,44561L,44562L,44563L,44564L,
44565L,44566L,44567L,44568L,44569L,44570L,44571L,44572L,44573L,44574L,
44575L,44576L,44577L,44578L,44579L,44580L,44581L,44582L,44583L,44584L,
44585L,44586L,44587L,44588L,44589L,44590L,44591L,44592L,44593L,44594L,
44595L,44596L,44597L,44598L,44599L,44600L,44601L,44602L,44603L,44604L,
44605L,44606L,44607L,44608L,44609L,44610L,44611L,44612L,44613L,44614L,
44615L,44616L,44617L,44618L,44619L,44620L,44621L,44622L,44623L,44624L,
44625L,44626L,44627L,44628L,44629L,44630L,44631L,44632L,44633L,44634L,
44635L,44636L,44637L,44638L,44639L,44640L,44641L,44642L,44643L,44644L,
44645L,44646L,44647L,44648L,44649L,44650L,44651L,44652L,44653L,44654L,
44655L,44656L,44657L,44658L,44659L,44660L,44661L,44662L,44663L,44664L,
44665L,44666L,44667L,44668L,44669L,44670L,44671L,44672L,44673L,44674L,
44675L,44676L,44677L,44678L,44679L,44680L,44681L,44682L,44683L,44684L,
44685L,44686L,44687L,44688L,44689L,44690L,44691L,44692L,44693L,44694L,
44695L,44696L,44697L,44698L,44699L,44700L,44701L,44702L,44703L,44704L,
44705L,44706L,44707L,44708L,44709L,44710L,44711L,44712L,44713L,44714L,
44715L,44716L,44717L,44718L,44719L,44720L,44721L,44722L,44723L,44724L,
44725L,44726L,44727L,44728L,44729L,44730L,44731L,44732L,44733L,44734L,
44735L,44736L,44737L,44738L,44739L,44740L,44741L,44742L,44743L,44744L,
44745L,44746L,44747L,44748L,44749L,44750L,44751L,44752L,44753L,44754L,
44755L,44756L,44757L,44758L,44759L,44760L,44761L,44762L,44763L,44764L,
44765L,44766L,44767L,44768L,44769L,44770L,44771L,44772L,44773L,44774L,
44775L,44776L,44777L,44778L,44779L,44780L,44781L,44782L,44783L,44784L,
44785L,44786L,44787L,44788L,44789L,44790L,44791L,44792L,44793L,44794L,
44795L,44796L,44797L,44798L,44799L,44800L,44801L,44802L,44803L,44804L,
44805L,44806L,44807L,44808L,44809L,44810L,44811L,44812L,44813L,44814L,
44815L,44816L,44817L,44818L,44819L,44820L,44821L,44822L,44823L,44824L,
44825L,44826L,44827L,44828L,44829L,44830L,44831L,44832L,44833L,44834L,
44835L,44836L,44837L,44838L,44839L,44840L,44841L,44842L,44843L,44844L,
44845L,44846L,44847L,44848L,44849L,44850L,44851L,44852L,44853L,44854L,
44855L,44856L,44857L,44858L,44859L,44860L,44861L,44862L,44863L,44864L,
44865L,44866L,44867L,44868L,44869L,44870L,44871L,44872L,44873L,44874L,
44875L,44876L,44877L,44878L,44879L,44880L,44881L,44882L,44883L,44884L,
44885L,44886L,44887L,44888L,44889L,44890L,44891L,44892L,44893L,44894L,
44895L,44896L,44897L,44898L,44899L,44900L,44901L,44902L,44903L,44904L,
44905L,44906L,44907L,44908L,44909L,44910L,44911L,44912L,44913L,44914L,
44915L,44916L,44917L,44918L,44919L,44920L,44921L,44922L,44923L,44924L,
44925L,44926L,44927L,44928L,44929L,44930L,44931L,44932L,44933L,44934L,
44935L,44936L,44937L,44938L,44939L,44940L,44941L,44942L,44943L,44944L,
44945L,44946L,44947L,44948L,44949L,44950L,44951L,44952L,44953L,44954L,
44955L,44956L,44957L,44958L,44959L,44960L,44961L,44962L,44963L,44964L,
44965L,44966L,44967L,44968L,44969L,44970L,44971L,44972L,44973L,44974L,
44975L,44976L,44977L,44978L,44979L,44980L,44981L,44982L,44983L,44984L,
44985L,44986L,44987L,44988L,44989L,44990L,44991L,44992L,44993L,44994L,
44995L,44996L,44997L,44998L,44999L,45000L,45001L,45002L,45003L,45004L,
45005L,45006L,45007L,45008L,45009L,45010L,45011L,45012L,45013L,45014L,
45015L,45016L,45017L,45018L,45019L,45020L,45021L,45022L,45023L,45024L,
45025L,45026L,45027L,45028L,45029L,45030L,45031L,45032L,45033L,45034L,
45035L,45036L,45037L,45038L,45039L,45040L,45041L,45042L,45043L,45044L,
45045L,45046L,45047L,45048L,45049L,45050L,45051L,45052L,45053L,45054L,
45055L,45056L,45057L,45058L,45059L,45060L,45061L,45062L,45063L,45064L,
45065L,45066L,45067L,45068L,45069L,45070L,45071L,45072L,45073L,45074L,
45075L,45076L,45077L,45078L,45079L,45080L,45081L,45082L,45083L,45084L,
45085L,45086L,45087L,45088L,45089L,45090L,45091L,45092L,45093L,45094L,
45095L,45096L,45097L,45098L,45099L,45100L,45101L,45102L,45103L,45104L,
45105L,45106L,45107L,45108L,45109L,45110L,45111L,45112L,45113L,45114L,
45115L,45116L,45117L,45118L,45119L,45120L,45121L,45122L,45123L,45124L,
45125L,45126L,45127L,45128L,45129L,45130L,45131L,45132L,45133L,45134L,
45135L,45136L,45137L,45138L,45139L,45140L,45141L,45142L,45143L,45144L,
45145L,45146L,45147L,45148L,45149L,45150L,45151L,45152L,45153L,45154L,
45155L,45156L,45157L,45158L,45159L,45160L,45161L,45162L,45163L,45164L,
45165L,45166L,45167L,45168L,45169L,45170L,45171L,45172L,45173L,45174L,
45175L,45176L,45177L,45178L,45179L,45180L,45181L,45182L,45183L,45184L,
45185L,45186L,45187L,45188L,45189L,45190L,45191L,45192L,45193L,45194L,
45195L,45196L,45197L,45198L,45199L,45200L,45201L,45202L,45203L,45204L,
45205L,45206L,45207L,45208L,45209L,45210L,45211L,45212L,45213L,45214L,
45215L,45216L,45217L,45218L,45219L,45220L,45221L,45222L,45223L,45224L,
45225L,45226L,45227L,45228L,45229L,45230L,45231L,45232L,45233L,45234L,
45235L,45236L,45237L,45238L,45239L,45240L,45241L,45242L,45243L,45244L,
45245L,45246L,45247L,45248L,45249L,45250L,45251L,45252L,45253L,45254L,
45255L,45256L,45257L,45258L,45259L,45260L,45261L,45262L,45263L,45264L,
45265L,45266L,45267L,45268L,45269L,45270L,45271L,45272L,45273L,45274L,
45275L,45276L,45277L,45278L,45279L,45280L,45281L,45282L,45283L,45284L,
45285L,45286L,45287L,45288L,45289L,45290L,45291L,45292L,45293L,45294L,
45295L,45296L,45297L,45298L,45299L,45300L,45301L,45302L,45303L,45304L,
45305L,45306L,45307L,45308L,45309L,45310L,45311L,45312L,45313L,45314L,
45315L,45316L,45317L,45318L,45319L,45320L,45321L,45322L,45323L,45324L,
45325L,45326L,45327L,45328L,45329L,45330L,45331L,45332L,45333L,45334L,
45335L,45336L,45337L,45338L,45339L,45340L,45341L,45342L,45343L,45344L,
45345L,45346L,45347L,45348L,45349L,45350L,45351L,45352L,45353L,45354L,
45355L,45356L,45357L,45358L,45359L,45360L,45361L,45362L,45363L,45364L,
45365L,45366L,45367L,45368L,45369L,45370L,45371L,45372L,45373L,45374L,
45375L,45376L,45377L,45378L,45379L,45380L,45381L,45382L,45383L,45384L,
45385L,45386L,45387L,45388L,45389L,45390L,45391L,45392L,45393L,45394L,
45395L,45396L,45397L,45398L,45399L,45400L,45401L,45402L,45403L,45404L,
45405L,45406L,45407L,45408L,45409L,45410L,45411L,45412L,45413L,45414L,
45415L,45416L,45417L,45418L,45419L,45420L,45421L,45422L,45423L,45424L,
45425L,45426L,45427L,45428L,45429L,45430L,45431L,45432L,45433L,45434L,
45435L,45436L,45437L,45438L,45439L,45440L,45441L,45442L,45443L,45444L,
45445L,45446L,45447L,45448L,45449L,45450L,45451L,45452L,45453L,45454L,
45455L,45456L,45457L,45458L,45459L,45460L,45461L,45462L,45463L,45464L,
45465L,45466L,45467L,45468L,45469L,45470L,45471L,45472L,45473L,45474L,
45475L,45476L,45477L,45478L,45479L,45480L,45481L,45482L,45483L,45484L,
45485L,45486L,45487L,45488L,45489L,45490L,45491L,45492L,45493L,45494L,
45495L,45496L,45497L,45498L,45499L,45500L,45501L,45502L,45503L,45504L,
45505L,45506L,45507L,45508L,45509L,45510L,45511L,45512L,45513L,45514L,
45515L,45516L,45517L,45518L,45519L,45520L,45521L,45522L,45523L,45524L,
45525L,45526L,45527L,45528L,45529L,45530L,45531L,45532L,45533L,45534L,
45535L,45536L,45537L,45538L,45539L,45540L,45541L,45542L,45543L,45544L,
45545L,45546L,45547L,45548L,45549L,45550L,45551L,45552L,45553L,45554L,
45555L,45556L,45557L,45558L,45559L,45560L,45561L,45562L,45563L,45564L,
45565L,45566L,45567L,45568L,45569L,45570L,45571L,45572L,45573L,45574L,
45575L,45576L,45577L,45578L,45579L,45580L,45581L,45582L,45583L,45584L,
45585L,45586L,45587L,45588L,45589L,45590L,45591L,45592L,45593L,45594L,
45595L,45596L,45597L,45598L,45599L,45600L,45601L,45602L,45603L,45604L,
45605L,45606L,45607L,45608L,45609L,45610L,45611L,45612L,45613L,45614L,
45615L,45616L,45617L,45618L,45619L,45620L,45621L,45622L,45623L,45624L,
45625L,45626L,45627L,45628L,45629L,45630L,45631L,45632L,45633L,45634L,
45635L,45636L,45637L,45638L,45639L,45640L,45641L,45642L,45643L,45644L,
45645L,45646L,45647L,45648L,45649L,45650L,45651L,45652L,45653L,45654L,
45655L,45656L,45657L,45658L,45659L,45660L,45661L,45662L,45663L,45664L,
45665L,45666L,45667L,45668L,45669L,45670L,45671L,45672L,45673L,45674L,
45675L,45676L,45677L,45678L,45679L,45680L,45681L,45682L,45683L,45684L,
45685L,45686L,45687L,45688L,45689L,45690L,45691L,45692L,45693L,45694L,
45695L,45696L,45697L,45698L,45699L,45700L,45701L,45702L,45703L,45704L,
45705L,45706L,45707L,45708L,45709L,45710L,45711L,45712L,45713L,45714L,
45715L,45716L,45717L,45718L,45719L,45720L,45721L,45722L,45723L,45724L,
45725L,45726L,45727L,45728L,45729L,45730L,45731L,45732L,45733L,45734L,
45735L,45736L,45737L,45738L,45739L,45740L,45741L,45742L,45743L,45744L,
45745L,45746L,45747L,45748L,45749L,45750L,45751L,45752L,45753L,45754L,
45755L,45756L,45757L,45758L,45759L,45760L,45761L,45762L,45763L,45764L,
45765L,45766L,45767L,45768L,45769L,45770L,45771L,45772L,45773L,45774L,
45775L,45776L,45777L,45778L,45779L,45780L,45781L,45782L,45783L,45784L,
45785L,45786L,45787L,45788L,45789L,45790L,45791L,45792L,45793L,45794L,
45795L,45796L,45797L,45798L,45799L,45800L,45801L,45802L,45803L,45804L,
45805L,45806L,45807L,45808L,45809L,45810L,45811L,45812L,45813L,45814L,
45815L,45816L,45817L,45818L,45819L,45820L,45821L,45822L,45823L,45824L,
45825L,45826L,45827L,45828L,45829L,45830L,45831L,45832L,45833L,45834L,
45835L,45836L,45837L,45838L,45839L,45840L,45841L,45842L,45843L,45844L,
45845L,45846L,45847L,45848L,45849L,45850L,45851L,45852L,45853L,45854L,
45855L,45856L,45857L,45858L,45859L,45860L,45861L,45862L,45863L,45864L,
45865L,45866L,45867L,45868L,45869L,45870L,45871L,45872L,45873L,45874L,
45875L,45876L,45877L,45878L,45879L,45880L,45881L,45882L,45883L,45884L,
45885L,45886L,45887L,45888L,45889L,45890L,45891L,45892L,45893L,45894L,
45895L,45896L,45897L,45898L,45899L,45900L,45901L,45902L,45903L,45904L,
45905L,45906L,45907L,45908L,45909L,45910L,45911L,45912L,45913L,45914L,
45915L,45916L,45917L,45918L,45919L,45920L,45921L,45922L,45923L,45924L,
45925L,45926L,45927L,45928L,45929L,45930L,45931L,45932L,45933L,45934L,
45935L,45936L,45937L,45938L,45939L,45940L,45941L,45942L,45943L,45944L,
45945L,45946L,45947L,45948L,45949L,45950L,45951L,45952L,45953L,45954L,
45955L,45956L,45957L,45958L,45959L,45960L,45961L,45962L,45963L,45964L,
45965L,45966L,45967L,45968L,45969L,45970L,45971L,45972L,45973L,45974L,
45975L,45976L,45977L,45978L,45979L,45980L,45981L,45982L,45983L,45984L,
45985L,45986L,45987L,45988L,45989L,45990L,45991L,45992L,45993L,45994L,
45995L,45996L,45997L,45998L,45999L,46000L,46001L,46002L,46003L,46004L,
46005L,46006L,46007L,46008L,46009L,46010L,46011L,46012L,46013L,46014L,
46015L,46016L,46017L,46018L,46019L,46020L,46021L,46022L,46023L,46024L,
46025L,46026L,46027L,46028L,46029L,46030L,46031L,46032L,46033L,46034L,
46035L,46036L,46037L,46038L,46039L,46040L,46041L,46042L,46043L,46044L,
46045L,46046L,46047L,46048L,46049L,46050L,46051L,46052L,46053L,46054L,
46055L,46056L,46057L,46058L,46059L,46060L,46061L,46062L,46063L,46064L,
46065L,46066L,46067L,46068L,46069L,46070L,46071L,46072L,46073L,46074L,
46075L,46076L,46077L,46078L,46079L,46080L,46081L,46082L,46083L,46084L,
46085L,46086L,46087L,46088L,46089L,46090L,46091L,46092L,46093L,46094L,
46095L,46096L,46097L,46098L,46099L,46100L,46101L,46102L,46103L,46104L,
46105L,46106L,46107L,46108L,46109L,46110L,46111L,46112L,46113L,46114L,
46115L,46116L,46117L,46118L,46119L,46120L,46121L,46122L,46123L,46124L,
46125L,46126L,46127L,46128L,46129L,46130L,46131L,46132L,46133L,46134L,
46135L,46136L,46137L,46138L,46139L,46140L,46141L,46142L,46143L,46144L,
46145L,46146L,46147L,46148L,46149L,46150L,46151L,46152L,46153L,46154L,
46155L,46156L,46157L,46158L,46159L,46160L,46161L,46162L,46163L,46164L,
46165L,46166L,46167L,46168L,46169L,46170L,46171L,46172L,46173L,46174L,
46175L,46176L,46177L,46178L,46179L,46180L,46181L,46182L,46183L,46184L,
46185L,46186L,46187L,46188L,46189L,46190L,46191L,46192L,46193L,46194L,
46195L,46196L,46197L,46198L,46199L,46200L,46201L,46202L,46203L,46204L,
46205L,46206L,46207L,46208L,46209L,46210L,46211L,46212L,46213L,46214L,
46215L,46216L,46217L,46218L,46219L,46220L,46221L,46222L,46223L,46224L,
46225L,46226L,46227L,46228L,46229L,46230L,46231L,46232L,46233L,46234L,
46235L,46236L,46237L,46238L,46239L,46240L,46241L,46242L,46243L,46244L,
46245L,46246L,46247L,46248L,46249L,46250L,46251L,46252L,46253L,46254L,
46255L,46256L,46257L,46258L,46259L,46260L,46261L,46262L,46263L,46264L,
46265L,46266L,46267L,46268L,46269L,46270L,46271L,46272L,46273L,46274L,
46275L,46276L,46277L,46278L,46279L,46280L,46281L,46282L,46283L,46284L,
46285L,46286L,46287L,46288L,46289L,46290L,46291L,46292L,46293L,46294L,
46295L,46296L,46297L,46298L,46299L,46300L,46301L,46302L,46303L,46304L,
46305L,46306L,46307L,46308L,46309L,46310L,46311L,46312L,46313L,46314L,
46315L,46316L,46317L,46318L,46319L,46320L,46321L,46322L,46323L,46324L,
46325L,46326L,46327L,46328L,46329L,46330L,46331L,46332L,46333L,46334L,
46335L,46336L,46337L,46338L,46339L,46340L,46341L,46342L,46343L,46344L,
46345L,46346L,46347L,46348L,46349L,46350L,46351L,46352L,46353L,46354L,
46355L,46356L,46357L,46358L,46359L,46360L,46361L,46362L,46363L,46364L,
46365L,46366L,46367L,46368L,46369L,46370L,46371L,46372L,46373L,46374L,
46375L,46376L,46377L,46378L,46379L,46380L,46381L,46382L,46383L,46384L,
46385L,46386L,46387L,46388L,46389L,46390L,46391L,46392L,46393L,46394L,
46395L,46396L,46397L,46398L,46399L,46400L,46401L,46402L,46403L,46404L,
46405L,46406L,46407L,46408L,46409L,46410L,46411L,46412L,46413L,46414L,
46415L,46416L,46417L,46418L,46419L,46420L,46421L,46422L,46423L,46424L,
46425L,46426L,46427L,46428L,46429L,46430L,46431L,46432L,46433L,46434L,
46435L,46436L,46437L,46438L,46439L,46440L,46441L,46442L,46443L,46444L,
46445L,46446L,46447L,46448L,46449L,46450L,46451L,46452L,46453L,46454L,
46455L,46456L,46457L,46458L,46459L,46460L,46461L,46462L,46463L,46464L,
46465L,46466L,46467L,46468L,46469L,46470L,46471L,46472L,46473L,46474L,
46475L,46476L,46477L,46478L,46479L,46480L,46481L,46482L,46483L,46484L,
46485L,46486L,46487L,46488L,46489L,46490L,46491L,46492L,46493L,46494L,
46495L,46496L,46497L,46498L,46499L,46500L,46501L,46502L,46503L,46504L,
46505L,46506L,46507L,46508L,46509L,46510L,46511L,46512L,46513L,46514L,
46515L,46516L,46517L,46518L,46519L,46520L,46521L,46522L,46523L,46524L,
46525L,46526L,46527L,46528L,46529L,46530L,46531L,46532L,46533L,46534L,
46535L,46536L,46537L,46538L,46539L,46540L,46541L,46542L,46543L,46544L,
46545L,46546L,46547L,46548L,46549L,46550L,46551L,46552L,46553L,46554L,
46555L,46556L,46557L,46558L,46559L,46560L,46561L,46562L,46563L,46564L,
46565L,46566L,46567L,46568L,46569L,46570L,46571L,46572L,46573L,46574L,
46575L,46576L,46577L,46578L,46579L,46580L,46581L,46582L,46583L,46584L,
46585L,46586L,46587L,46588L,46589L,46590L,46591L,46592L,46593L,46594L,
46595L,46596L,46597L,46598L,46599L,46600L,46601L,46602L,46603L,46604L,
46605L,46606L,46607L,46608L,46609L,46610L,46611L,46612L,46613L,46614L,
46615L,46616L,46617L,46618L,46619L,46620L,46621L,46622L,46623L,46624L,
46625L,46626L,46627L,46628L,46629L,46630L,46631L,46632L,46633L,46634L,
46635L,46636L,46637L,46638L,46639L,46640L,46641L,46642L,46643L,46644L,
46645L,46646L,46647L,46648L,46649L,46650L,46651L,46652L,46653L,46654L,
46655L,46656L,46657L,46658L,46659L,46660L,46661L,46662L,46663L,46664L,
46665L,46666L,46667L,46668L,46669L,46670L,46671L,46672L,46673L,46674L,
46675L,46676L,46677L,46678L,46679L,46680L,46681L,46682L,46683L,46684L,
46685L,46686L,46687L,46688L,46689L,46690L,46691L,46692L,46693L,46694L,
46695L,46696L,46697L,46698L,46699L,46700L,46701L,46702L,46703L,46704L,
46705L,46706L,46707L,46708L,46709L,46710L,46711L,46712L,46713L,46714L,
46715L,46716L,46717L,46718L,46719L,46720L,46721L,46722L,46723L,46724L,
46725L,46726L,46727L,46728L,46729L,46730L,46731L,46732L,46733L,46734L,
46735L,46736L,46737L,46738L,46739L,46740L,46741L,46742L,46743L,46744L,
46745L,46746L,46747L,46748L,46749L,46750L,46751L,46752L,46753L,46754L,
46755L,46756L,46757L,46758L,46759L,46760L,46761L,46762L,46763L,46764L,
46765L,46766L,46767L,46768L,46769L,46770L,46771L,46772L,46773L,46774L,
46775L,46776L,46777L,46778L,46779L,46780L,46781L,46782L,46783L,46784L,
46785L,46786L,46787L,46788L,46789L,46790L,46791L,46792L,46793L,46794L,
46795L,46796L,46797L,46798L,46799L,46800L,46801L,46802L,46803L,46804L,
46805L,46806L,46807L,46808L,46809L,46810L,46811L,46812L,46813L,46814L,
46815L,46816L,46817L,46818L,46819L,46820L,46821L,46822L,46823L,46824L,
46825L,46826L,46827L,46828L,46829L,46830L,46831L,46832L,46833L,46834L,
46835L,46836L,46837L,46838L,46839L,46840L,46841L,46842L,46843L,46844L,
46845L,46846L,46847L,46848L,46849L,46850L,46851L,46852L,46853L,46854L,
46855L,46856L,46857L,46858L,46859L,46860L,46861L,46862L,46863L,46864L,
46865L,46866L,46867L,46868L,46869L,46870L,46871L,46872L,46873L,46874L,
46875L,46876L,46877L,46878L,46879L,46880L,46881L,46882L,46883L,46884L,
46885L,46886L,46887L,46888L,46889L,46890L,46891L,46892L,46893L,46894L,
46895L,46896L,46897L,46898L,46899L,46900L,46901L,46902L,46903L,46904L,
46905L,46906L,46907L,46908L,46909L,46910L,46911L,46912L,46913L,46914L,
46915L,46916L,46917L,46918L,46919L,46920L,46921L,46922L,46923L,46924L,
46925L,46926L,46927L,46928L,46929L,46930L,46931L,46932L,46933L,46934L,
46935L,46936L,46937L,46938L,46939L,46940L,46941L,46942L,46943L,46944L,
46945L,46946L,46947L,46948L,46949L,46950L,46951L,46952L,46953L,46954L,
46955L,46956L,46957L,46958L,46959L,46960L,46961L,46962L,46963L,46964L,
46965L,46966L,46967L,46968L,46969L,46970L,46971L,46972L,46973L,46974L,
46975L,46976L,46977L,46978L,46979L,46980L,46981L,46982L,46983L,46984L,
46985L,46986L,46987L,46988L,46989L,46990L,46991L,46992L,46993L,46994L,
46995L,46996L,46997L,46998L,46999L,47000L,47001L,47002L,47003L,47004L,
47005L,47006L,47007L,47008L,47009L,47010L,47011L,47012L,47013L,47014L,
47015L,47016L,47017L,47018L,47019L,47020L,47021L,47022L,47023L,47024L,
47025L,47026L,47027L,47028L,47029L,47030L,47031L,47032L,47033L,47034L,
47035L,47036L,47037L,47038L,47039L,47040L,47041L,47042L,47043L,47044L,
47045L,47046L,47047L,47048L,47049L,47050L,47051L,47052L,47053L,47054L,
47055L,47056L,47057L,47058L,47059L,47060L,47061L,47062L,47063L,47064L,
47065L,47066L,47067L,47068L,47069L,47070L,47071L,47072L,47073L,47074L,
47075L,47076L,47077L,47078L,47079L,47080L,47081L,47082L,47083L,47084L,
47085L,47086L,47087L,47088L,47089L,47090L,47091L,47092L,47093L,47094L,
47095L,47096L,47097L,47098L,47099L,47100L,47101L,47102L,47103L,47104L,
47105L,47106L,47107L,47108L,47109L,47110L,47111L,47112L,47113L,47114L,
47115L,47116L,47117L,47118L,47119L,47120L,47121L,47122L,47123L,47124L,
47125L,47126L,47127L,47128L,47129L,47130L,47131L,47132L,47133L,47134L,
47135L,47136L,47137L,47138L,47139L,47140L,47141L,47142L,47143L,47144L,
47145L,47146L,47147L,47148L,47149L,47150L,47151L,47152L,47153L,47154L,
47155L,47156L,47157L,47158L,47159L,47160L,47161L,47162L,47163L,47164L,
47165L,47166L,47167L,47168L,47169L,47170L,47171L,47172L,47173L,47174L,
47175L,47176L,47177L,47178L,47179L,47180L,47181L,47182L,47183L,47184L,
47185L,47186L,47187L,47188L,47189L,47190L,47191L,47192L,47193L,47194L,
47195L,47196L,47197L,47198L,47199L,47200L,47201L,47202L,47203L,47204L,
47205L,47206L,47207L,47208L,47209L,47210L,47211L,47212L,47213L,47214L,
47215L,47216L,47217L,47218L,47219L,47220L,47221L,47222L,47223L,47224L,
47225L,47226L,47227L,47228L,47229L,47230L,47231L,47232L,47233L,47234L,
47235L,47236L,47237L,47238L,47239L,47240L,47241L,47242L,47243L,47244L,
47245L,47246L,47247L,47248L,47249L,47250L,47251L,47252L,47253L,47254L,
47255L,47256L,47257L,47258L,47259L,47260L,47261L,47262L,47263L,47264L,
47265L,47266L,47267L,47268L,47269L,47270L,47271L,47272L,47273L,47274L,
47275L,47276L,47277L,47278L,47279L,47280L,47281L,47282L,47283L,47284L,
47285L,47286L,47287L,47288L,47289L,47290L,47291L,47292L,47293L,47294L,
47295L,47296L,47297L,47298L,47299L,47300L,47301L,47302L,47303L,47304L,
47305L,47306L,47307L,47308L,47309L,47310L,47311L,47312L,47313L,47314L,
47315L,47316L,47317L,47318L,47319L,47320L,47321L,47322L,47323L,47324L,
47325L,47326L,47327L,47328L,47329L,47330L,47331L,47332L,47333L,47334L,
47335L,47336L,47337L,47338L,47339L,47340L,47341L,47342L,47343L,47344L,
47345L,47346L,47347L,47348L,47349L,47350L,47351L,47352L,47353L,47354L,
47355L,47356L,47357L,47358L,47359L,47360L,47361L,47362L,47363L,47364L,
47365L,47366L,47367L,47368L,47369L,47370L,47371L,47372L,47373L,47374L,
47375L,47376L,47377L,47378L,47379L,47380L,47381L,47382L,47383L,47384L,
47385L,47386L,47387L,47388L,47389L,47390L,47391L,47392L,47393L,47394L,
47395L,47396L,47397L,47398L,47399L,47400L,47401L,47402L,47403L,47404L,
47405L,47406L,47407L,47408L,47409L,47410L,47411L,47412L,47413L,47414L,
47415L,47416L,47417L,47418L,47419L,47420L,47421L,47422L,47423L,47424L,
47425L,47426L,47427L,47428L,47429L,47430L,47431L,47432L,47433L,47434L,
47435L,47436L,47437L,47438L,47439L,47440L,47441L,47442L,47443L,47444L,
47445L,47446L,47447L,47448L,47449L,47450L,47451L,47452L,47453L,47454L,
47455L,47456L,47457L,47458L,47459L,47460L,47461L,47462L,47463L,47464L,
47465L,47466L,47467L,47468L,47469L,47470L,47471L,47472L,47473L,47474L,
47475L,47476L,47477L,47478L,47479L,47480L,47481L,47482L,47483L,47484L,
47485L,47486L,47487L,47488L,47489L,47490L,47491L,47492L,47493L,47494L,
47495L,47496L,47497L,47498L,47499L,47500L,47501L,47502L,47503L,47504L,
47505L,47506L,47507L,47508L,47509L,47510L,47511L,47512L,47513L,47514L,
47515L,47516L,47517L,47518L,47519L,47520L,47521L,47522L,47523L,47524L,
47525L,47526L,47527L,47528L,47529L,47530L,47531L,47532L,47533L,47534L,
47535L,47536L,47537L,47538L,47539L,47540L,47541L,47542L,47543L,47544L,
47545L,47546L,47547L,47548L,47549L,47550L,47551L,47552L,47553L,47554L,
47555L,47556L,47557L,47558L,47559L,47560L,47561L,47562L,47563L,47564L,
47565L,47566L,47567L,47568L,47569L,47570L,47571L,47572L,47573L,47574L,
47575L,47576L,47577L,47578L,47579L,47580L,47581L,47582L,47583L,47584L,
47585L,47586L,47587L,47588L,47589L,47590L,47591L,47592L,47593L,47594L,
47595L,47596L,47597L,47598L,47599L,47600L,47601L,47602L,47603L,47604L,
47605L,47606L,47607L,47608L,47609L,47610L,47611L,47612L,47613L,47614L,
47615L,47616L,47617L,47618L,47619L,47620L,47621L,47622L,47623L,47624L,
47625L,47626L,47627L,47628L,47629L,47630L,47631L,47632L,47633L,47634L,
47635L,47636L,47637L,47638L,47639L,47640L,47641L,47642L,47643L,47644L,
47645L,47646L,47647L,47648L,47649L,47650L,47651L,47652L,47653L,47654L,
47655L,47656L,47657L,47658L,47659L,47660L,47661L,47662L,47663L,47664L,
47665L,47666L,47667L,47668L,47669L,47670L,47671L,47672L,47673L,47674L,
47675L,47676L,47677L,47678L,47679L,47680L,47681L,47682L,47683L,47684L,
47685L,47686L,47687L,47688L,47689L,47690L,47691L,47692L,47693L,47694L,
47695L,47696L,47697L,47698L,47699L,47700L,47701L,47702L,47703L,47704L,
47705L,47706L,47707L,47708L,47709L,47710L,47711L,47712L,47713L,47714L,
47715L,47716L,47717L,47718L,47719L,47720L,47721L,47722L,47723L,47724L,
47725L,47726L,47727L,47728L,47729L,47730L,47731L,47732L,47733L,47734L,
47735L,47736L,47737L,47738L,47739L,47740L,47741L,47742L,47743L,47744L,
47745L,47746L,47747L,47748L,47749L,47750L,47751L,47752L,47753L,47754L,
47755L,47756L,47757L,47758L,47759L,47760L,47761L,47762L,47763L,47764L,
47765L,47766L,47767L,47768L,47769L,47770L,47771L,47772L,47773L,47774L,
47775L,47776L,47777L,47778L,47779L,47780L,47781L,47782L,47783L,47784L,
47785L,47786L,47787L,47788L,47789L,47790L,47791L,47792L,47793L,47794L,
47795L,47796L,47797L,47798L,47799L,47800L,47801L,47802L,47803L,47804L,
47805L,47806L,47807L,47808L,47809L,47810L,47811L,47812L,47813L,47814L,
47815L,47816L,47817L,47818L,47819L,47820L,47821L,47822L,47823L,47824L,
47825L,47826L,47827L,47828L,47829L,47830L,47831L,47832L,47833L,47834L,
47835L,47836L,47837L,47838L,47839L,47840L,47841L,47842L,47843L,47844L,
47845L,47846L,47847L,47848L,47849L,47850L,47851L,47852L,47853L,47854L,
47855L,47856L,47857L,47858L,47859L,47860L,47861L,47862L,47863L,47864L,
47865L,47866L,47867L,47868L,47869L,47870L,47871L,47872L,47873L,47874L,
47875L,47876L,47877L,47878L,47879L,47880L,47881L,47882L,47883L,47884L,
47885L,47886L,47887L,47888L,47889L,47890L,47891L,47892L,47893L,47894L,
47895L,47896L,47897L,47898L,47899L,47900L,47901L,47902L,47903L,47904L,
47905L,47906L,47907L,47908L,47909L,47910L,47911L,47912L,47913L,47914L,
47915L,47916L,47917L,47918L,47919L,47920L,47921L,47922L,47923L,47924L,
47925L,47926L,47927L,47928L,47929L,47930L,47931L,47932L,47933L,47934L,
47935L,47936L,47937L,47938L,47939L,47940L,47941L,47942L,47943L,47944L,
47945L,47946L,47947L,47948L,47949L,47950L,47951L,47952L,47953L,47954L,
47955L,47956L,47957L,47958L,47959L,47960L,47961L,47962L,47963L,47964L,
47965L,47966L,47967L,47968L,47969L,47970L,47971L,47972L,47973L,47974L,
47975L,47976L,47977L,47978L,47979L,47980L,47981L,47982L,47983L,47984L,
47985L,47986L,47987L,47988L,47989L,47990L,47991L,47992L,47993L,47994L,
47995L,47996L,47997L,47998L,47999L,48000L,48001L,48002L,48003L,48004L,
48005L,48006L,48007L,48008L,48009L,48010L,48011L,48012L,48013L,48014L,
48015L,48016L,48017L,48018L,48019L,48020L,48021L,48022L,48023L,48024L,
48025L,48026L,48027L,48028L,48029L,48030L,48031L,48032L,48033L,48034L,
48035L,48036L,48037L,48038L,48039L,48040L,48041L,48042L,48043L,48044L,
48045L,48046L,48047L,48048L,48049L,48050L,48051L,48052L,48053L,48054L,
48055L,48056L,48057L,48058L,48059L,48060L,48061L,48062L,48063L,48064L,
48065L,48066L,48067L,48068L,48069L,48070L,48071L,48072L,48073L,48074L,
48075L,48076L,48077L,48078L,48079L,48080L,48081L,48082L,48083L,48084L,
48085L,48086L,48087L,48088L,48089L,48090L,48091L,48092L,48093L,48094L,
48095L,48096L,48097L,48098L,48099L,48100L,48101L,48102L,48103L,48104L,
48105L,48106L,48107L,48108L,48109L,48110L,48111L,48112L,48113L,48114L,
48115L,48116L,48117L,48118L,48119L,48120L,48121L,48122L,48123L,48124L,
48125L,48126L,48127L,48128L,48129L,48130L,48131L,48132L,48133L,48134L,
48135L,48136L,48137L,48138L,48139L,48140L,48141L,48142L,48143L,48144L,
48145L,48146L,48147L,48148L,48149L,48150L,48151L,48152L,48153L,48154L,
48155L,48156L,48157L,48158L,48159L,48160L,48161L,48162L,48163L,48164L,
48165L,48166L,48167L,48168L,48169L,48170L,48171L,48172L,48173L,48174L,
48175L,48176L,48177L,48178L,48179L,48180L,48181L,48182L,48183L,48184L,
48185L,48186L,48187L,48188L,48189L,48190L,48191L,48192L,48193L,48194L,
48195L,48196L,48197L,48198L,48199L,48200L,48201L,48202L,48203L,48204L,
48205L,48206L,48207L,48208L,48209L,48210L,48211L,48212L,48213L,48214L,
48215L,48216L,48217L,48218L,48219L,48220L,48221L,48222L,48223L,48224L,
48225L,48226L,48227L,48228L,48229L,48230L,48231L,48232L,48233L,48234L,
48235L,48236L,48237L,48238L,48239L,48240L,48241L,48242L,48243L,48244L,
48245L,48246L,48247L,48248L,48249L,48250L,48251L,48252L,48253L,48254L,
48255L,48256L,48257L,48258L,48259L,48260L,48261L,48262L,48263L,48264L,
48265L,48266L,48267L,48268L,48269L,48270L,48271L,48272L,48273L,48274L,
48275L,48276L,48277L,48278L,48279L,48280L,48281L,48282L,48283L,48284L,
48285L,48286L,48287L,48288L,48289L,48290L,48291L,48292L,48293L,48294L,
48295L,48296L,48297L,48298L,48299L,48300L,48301L,48302L,48303L,48304L,
48305L,48306L,48307L,48308L,48309L,48310L,48311L,48312L,48313L,48314L,
48315L,48316L,48317L,48318L,48319L,48320L,48321L,48322L,48323L,48324L,
48325L,48326L,48327L,48328L,48329L,48330L,48331L,48332L,48333L,48334L,
48335L,48336L,48337L,48338L,48339L,48340L,48341L,48342L,48343L,48344L,
48345L,48346L,48347L,48348L,48349L,48350L,48351L,48352L,48353L,48354L,
48355L,48356L,48357L,48358L,48359L,48360L,48361L,48362L,48363L,48364L,
48365L,48366L,48367L,48368L,48369L,48370L,48371L,48372L,48373L,48374L,
48375L,48376L,48377L,48378L,48379L,48380L,48381L,48382L,48383L,48384L,
48385L,48386L,48387L,48388L,48389L,48390L,48391L,48392L,48393L,48394L,
48395L,48396L,48397L,48398L,48399L,48400L,48401L,48402L,48403L,48404L,
48405L,48406L,48407L,48408L,48409L,48410L,48411L,48412L,48413L,48414L,
48415L,48416L,48417L,48418L,48419L,48420L,48421L,48422L,48423L,48424L,
48425L,48426L,48427L,48428L,48429L,48430L,48431L,48432L,48433L,48434L,
48435L,48436L,48437L,48438L,48439L,48440L,48441L,48442L,48443L,48444L,
48445L,48446L,48447L,48448L,48449L,48450L,48451L,48452L,48453L,48454L,
48455L,48456L,48457L,48458L,48459L,48460L,48461L,48462L,48463L,48464L,
48465L,48466L,48467L,48468L,48469L,48470L,48471L,48472L,48473L,48474L,
48475L,48476L,48477L,48478L,48479L,48480L,48481L,48482L,48483L,48484L,
48485L,48486L,48487L,48488L,48489L,48490L,48491L,48492L,48493L,48494L,
48495L,48496L,48497L,48498L,48499L,48500L,48501L,48502L,48503L,48504L,
48505L,48506L,48507L,48508L,48509L,48510L,48511L,48512L,48513L,48514L,
48515L,48516L,48517L,48518L,48519L,48520L,48521L,48522L,48523L,48524L,
48525L,48526L,48527L,48528L,48529L,48530L,48531L,48532L,48533L,48534L,
48535L,48536L,48537L,48538L,48539L,48540L,48541L,48542L,48543L,48544L,
48545L,48546L,48547L,48548L,48549L,48550L,48551L,48552L,48553L,48554L,
48555L,48556L,48557L,48558L,48559L,48560L,48561L,48562L,48563L,48564L,
48565L,48566L,48567L,48568L,48569L,48570L,48571L,48572L,48573L,48574L,
48575L,48576L,48577L,48578L,48579L,48580L,48581L,48582L,48583L,48584L,
48585L,48586L,48587L,48588L,48589L,48590L,48591L,48592L,48593L,48594L,
48595L,48596L,48597L,48598L,48599L,48600L,48601L,48602L,48603L,48604L,
48605L,48606L,48607L,48608L,48609L,48610L,48611L,48612L,48613L,48614L,
48615L,48616L,48617L,48618L,48619L,48620L,48621L,48622L,48623L,48624L,
48625L,48626L,48627L,48628L,48629L,48630L,48631L,48632L,48633L,48634L,
48635L,48636L,48637L,48638L,48639L,48640L,48641L,48642L,48643L,48644L,
48645L,48646L,48647L,48648L,48649L,48650L,48651L,48652L,48653L,48654L,
48655L,48656L,48657L,48658L,48659L,48660L,48661L,48662L,48663L,48664L,
48665L,48666L,48667L,48668L,48669L,48670L,48671L,48672L,48673L,48674L,
48675L,48676L,48677L,48678L,48679L,48680L,48681L,48682L,48683L,48684L,
48685L,48686L,48687L,48688L,48689L,48690L,48691L,48692L,48693L,48694L,
48695L,48696L,48697L,48698L,48699L,48700L,48701L,48702L,48703L,48704L,
48705L,48706L,48707L,48708L,48709L,48710L,48711L,48712L,48713L,48714L,
48715L,48716L,48717L,48718L,48719L,48720L,48721L,48722L,48723L,48724L,
48725L,48726L,48727L,48728L,48729L,48730L,48731L,48732L,48733L,48734L,
48735L,48736L,48737L,48738L,48739L,48740L,48741L,48742L,48743L,48744L,
48745L,48746L,48747L,48748L,48749L,48750L,48751L,48752L,48753L,48754L,
48755L,48756L,48757L,48758L,48759L,48760L,48761L,48762L,48763L,48764L,
48765L,48766L,48767L,48768L,48769L,48770L,48771L,48772L,48773L,48774L,
48775L,48776L,48777L,48778L,48779L,48780L,48781L,48782L,48783L,48784L,
48785L,48786L,48787L,48788L,48789L,48790L,48791L,48792L,48793L,48794L,
48795L,48796L,48797L,48798L,48799L,48800L,48801L,48802L,48803L,48804L,
48805L,48806L,48807L,48808L,48809L,48810L,48811L,48812L,48813L,48814L,
48815L,48816L,48817L,48818L,48819L,48820L,48821L,48822L,48823L,48824L,
48825L,48826L,48827L,48828L,48829L,48830L,48831L,48832L,48833L,48834L,
48835L,48836L,48837L,48838L,48839L,48840L,48841L,48842L,48843L,48844L,
48845L,48846L,48847L,48848L,48849L,48850L,48851L,48852L,48853L,48854L,
48855L,48856L,48857L,48858L,48859L,48860L,48861L,48862L,48863L,48864L,
48865L,48866L,48867L,48868L,48869L,48870L,48871L,48872L,48873L,48874L,
48875L,48876L,48877L,48878L,48879L,48880L,48881L,48882L,48883L,48884L,
48885L,48886L,48887L,48888L,48889L,48890L,48891L,48892L,48893L,48894L,
48895L,48896L,48897L,48898L,48899L,48900L,48901L,48902L,48903L,48904L,
48905L,48906L,48907L,48908L,48909L,48910L,48911L,48912L,48913L,48914L,
48915L,48916L,48917L,48918L,48919L,48920L,48921L,48922L,48923L,48924L,
48925L,48926L,48927L,48928L,48929L,48930L,48931L,48932L,48933L,48934L,
48935L,48936L,48937L,48938L,48939L,48940L,48941L,48942L,48943L,48944L,
48945L,48946L,48947L,48948L,48949L,48950L,48951L,48952L,48953L,48954L,
48955L,48956L,48957L,48958L,48959L,48960L,48961L,48962L,48963L,48964L,
48965L,48966L,48967L,48968L,48969L,48970L,48971L,48972L,48973L,48974L,
48975L,48976L,48977L,48978L,48979L,48980L,48981L,48982L,48983L,48984L,
48985L,48986L,48987L,48988L,48989L,48990L,48991L,48992L,48993L,48994L,
48995L,48996L,48997L,48998L,48999L,49000L,49001L,49002L,49003L,49004L,
49005L,49006L,49007L,49008L,49009L,49010L,49011L,49012L,49013L,49014L,
49015L,49016L,49017L,49018L,49019L,49020L,49021L,49022L,49023L,49024L,
49025L,49026L,49027L,49028L,49029L,49030L,49031L,49032L,49033L,49034L,
49035L,49036L,49037L,49038L,49039L,49040L,49041L,49042L,49043L,49044L,
49045L,49046L,49047L,49048L,49049L,49050L,49051L,49052L,49053L,49054L,
49055L,49056L,49057L,49058L,49059L,49060L,49061L,49062L,49063L,49064L,
49065L,49066L,49067L,49068L,49069L,49070L,49071L,49072L,49073L,49074L,
49075L,49076L,49077L,49078L,49079L,49080L,49081L,49082L,49083L,49084L,
49085L,49086L,49087L,49088L,49089L,49090L,49091L,49092L,49093L,49094L,
49095L,49096L,49097L,49098L,49099L,49100L,49101L,49102L,49103L,49104L,
49105L,49106L,49107L,49108L,49109L,49110L,49111L,49112L,49113L,49114L,
49115L,49116L,49117L,49118L,49119L,49120L,49121L,49122L,49123L,49124L,
49125L,49126L,49127L,49128L,49129L,49130L,49131L,49132L,49133L,49134L,
49135L,49136L,49137L,49138L,49139L,49140L,49141L,49142L,49143L,49144L,
49145L,49146L,49147L,49148L,49149L,49150L,49151L,49152L,49153L,49154L,
49155L,49156L,49157L,49158L,49159L,49160L,49161L,49162L,49163L,49164L,
49165L,49166L,49167L,49168L,49169L,49170L,49171L,49172L,49173L,49174L,
49175L,49176L,49177L,49178L,49179L,49180L,49181L,49182L,49183L,49184L,
49185L,49186L,49187L,49188L,49189L,49190L,49191L,49192L,49193L,49194L,
49195L,49196L,49197L,49198L,49199L,49200L,49201L,49202L,49203L,49204L,
49205L,49206L,49207L,49208L,49209L,49210L,49211L,49212L,49213L,49214L,
49215L,49216L,49217L,49218L,49219L,49220L,49221L,49222L,49223L,49224L,
49225L,49226L,49227L,49228L,49229L,49230L,49231L,49232L,49233L,49234L,
49235L,49236L,49237L,49238L,49239L,49240L,49241L,49242L,49243L,49244L,
49245L,49246L,49247L,49248L,49249L,49250L,49251L,49252L,49253L,49254L,
49255L,49256L,49257L,49258L,49259L,49260L,49261L,49262L,49263L,49264L,
49265L,49266L,49267L,49268L,49269L,49270L,49271L,49272L,49273L,49274L,
49275L,49276L,49277L,49278L,49279L,49280L,49281L,49282L,49283L,49284L,
49285L,49286L,49287L,49288L,49289L,49290L,49291L,49292L,49293L,49294L,
49295L,49296L,49297L,49298L,49299L,49300L,49301L,49302L,49303L,49304L,
49305L,49306L,49307L,49308L,49309L,49310L,49311L,49312L,49313L,49314L,
49315L,49316L,49317L,49318L,49319L,49320L,49321L,49322L,49323L,49324L,
49325L,49326L,49327L,49328L,49329L,49330L,49331L,49332L,49333L,49334L,
49335L,49336L,49337L,49338L,49339L,49340L,49341L,49342L,49343L,49344L,
49345L,49346L,49347L,49348L,49349L,49350L,49351L,49352L,49353L,49354L,
49355L,49356L,49357L,49358L,49359L,49360L,49361L,49362L,49363L,49364L,
49365L,49366L,49367L,49368L,49369L,49370L,49371L,49372L,49373L,49374L,
49375L,49376L,49377L,49378L,49379L,49380L,49381L,49382L,49383L,49384L,
49385L,49386L,49387L,49388L,49389L,49390L,49391L,49392L,49393L,49394L,
49395L,49396L,49397L,49398L,49399L,49400L,49401L,49402L,49403L,49404L,
49405L,49406L,49407L,49408L,49409L,49410L,49411L,49412L,49413L,49414L,
49415L,49416L,49417L,49418L,49419L,49420L,49421L,49422L,49423L,49424L,
49425L,49426L,49427L,49428L,49429L,49430L,49431L,49432L,49433L,49434L,
49435L,49436L,49437L,49438L,49439L,49440L,49441L,49442L,49443L,49444L,
49445L,49446L,49447L,49448L,49449L,49450L,49451L,49452L,49453L,49454L,
49455L,49456L,49457L,49458L,49459L,49460L,49461L,49462L,49463L,49464L,
49465L,49466L,49467L,49468L,49469L,49470L,49471L,49472L,49473L,49474L,
49475L,49476L,49477L,49478L,49479L,49480L,49481L,49482L,49483L,49484L,
49485L,49486L,49487L,49488L,49489L,49490L,49491L,49492L,49493L,49494L,
49495L,49496L,49497L,49498L,49499L,49500L,49501L,49502L,49503L,49504L,
49505L,49506L,49507L,49508L,49509L,49510L,49511L,49512L,49513L,49514L,
49515L,49516L,49517L,49518L,49519L,49520L,49521L,49522L,49523L,49524L,
49525L,49526L,49527L,49528L,49529L,49530L,49531L,49532L,49533L,49534L,
49535L,49536L,49537L,49538L,49539L,49540L,49541L,49542L,49543L,49544L,
49545L,49546L,49547L,49548L,49549L,49550L,49551L,49552L,49553L,49554L,
49555L,49556L,49557L,49558L,49559L,49560L,49561L,49562L,49563L,49564L,
49565L,49566L,49567L,49568L,49569L,49570L,49571L,49572L,49573L,49574L,
49575L,49576L,49577L,49578L,49579L,49580L,49581L,49582L,49583L,49584L,
49585L,49586L,49587L,49588L,49589L,49590L,49591L,49592L,49593L,49594L,
49595L,49596L,49597L,49598L,49599L,49600L,49601L,49602L,49603L,49604L,
49605L,49606L,49607L,49608L,49609L,49610L,49611L,49612L,49613L,49614L,
49615L,49616L,49617L,49618L,49619L,49620L,49621L,49622L,49623L,49624L,
49625L,49626L,49627L,49628L,49629L,49630L,49631L,49632L,49633L,49634L,
49635L,49636L,49637L,49638L,49639L,49640L,49641L,49642L,49643L,49644L,
49645L,49646L,49647L,49648L,49649L,49650L,49651L,49652L,49653L,49654L,
49655L,49656L,49657L,49658L,49659L,49660L,49661L,49662L,49663L,49664L,
49665L,49666L,49667L,49668L,49669L,49670L,49671L,49672L,49673L,49674L,
49675L,49676L,49677L,49678L,49679L,49680L,49681L,49682L,49683L,49684L,
49685L,49686L,49687L,49688L,49689L,49690L,49691L,49692L,49693L,49694L,
49695L,49696L,49697L,49698L,49699L,49700L,49701L,49702L,49703L,49704L,
49705L,49706L,49707L,49708L,49709L,49710L,49711L,49712L,49713L,49714L,
49715L,49716L,49717L,49718L,49719L,49720L,49721L,49722L,49723L,49724L,
49725L,49726L,49727L,49728L,49729L,49730L,49731L,49732L,49733L,49734L,
49735L,49736L,49737L,49738L,49739L,49740L,49741L,49742L,49743L,49744L,
49745L,49746L,49747L,49748L,49749L,49750L,49751L,49752L,49753L,49754L,
49755L,49756L,49757L,49758L,49759L,49760L,49761L,49762L,49763L,49764L,
49765L,49766L,49767L,49768L,49769L,49770L,49771L,49772L,49773L,49774L,
49775L,49776L,49777L,49778L,49779L,49780L,49781L,49782L,49783L,49784L,
49785L,49786L,49787L,49788L,49789L,49790L,49791L,49792L,49793L,49794L,
49795L,49796L,49797L,49798L,49799L,49800L,49801L,49802L,49803L,49804L,
49805L,49806L,49807L,49808L,49809L,49810L,49811L,49812L,49813L,49814L,
49815L,49816L,49817L,49818L,49819L,49820L,49821L,49822L,49823L,49824L,
49825L,49826L,49827L,49828L,49829L,49830L,49831L,49832L,49833L,49834L,
49835L,49836L,49837L,49838L,49839L,49840L,49841L,49842L,49843L,49844L,
49845L,49846L,49847L,49848L,49849L,49850L,49851L,49852L,49853L,49854L,
49855L,49856L,49857L,49858L,49859L,49860L,49861L,49862L,49863L,49864L,
49865L,49866L,49867L,49868L,49869L,49870L,49871L,49872L,49873L,49874L,
49875L,49876L,49877L,49878L,49879L,49880L,49881L,49882L,49883L,49884L,
49885L,49886L,49887L,49888L,49889L,49890L,49891L,49892L,49893L,49894L,
49895L,49896L,49897L,49898L,49899L,49900L,49901L,49902L,49903L,49904L,
49905L,49906L,49907L,49908L,49909L,49910L,49911L,49912L,49913L,49914L,
49915L,49916L,49917L,49918L,49919L,49920L,49921L,49922L,49923L,49924L,
49925L,49926L,49927L,49928L,49929L,49930L,49931L,49932L,49933L,49934L,
49935L,49936L,49937L,49938L,49939L,49940L,49941L,49942L,49943L,49944L,
49945L,49946L,49947L,49948L,49949L,49950L,49951L,49952L,49953L,49954L,
49955L,49956L,49957L,49958L,49959L,49960L,49961L,49962L,49963L,49964L,
49965L,49966L,49967L,49968L,49969L,49970L,49971L,49972L,49973L,49974L,
49975L,49976L,49977L,49978L,49979L,49980L,49981L,49982L,49983L,49984L,
49985L,49986L,49987L,49988L,49989L,49990L,49991L,49992L,49993L,49994L,
49995L,49996L,49997L,49998L,49999L,50000L,50001L,50002L,50003L,50004L,
50005L,50006L,50007L,50008L,50009L,50010L,50011L,50012L,50013L,50014L,
50015L,50016L,50017L,50018L,50019L,50020L,50021L,50022L,50023L,50024L,
50025L,50026L,50027L,50028L,50029L,50030L,50031L,50032L,50033L,50034L,
50035L,50036L,50037L,50038L,50039L,50040L,50041L,50042L,50043L,50044L,
50045L,50046L,50047L,50048L,50049L,50050L,50051L,50052L,50053L,50054L,
50055L,50056L,50057L,50058L,50059L,50060L,50061L,50062L,50063L,50064L,
50065L,50066L,50067L,50068L,50069L,50070L,50071L,50072L,50073L,50074L,
50075L,50076L,50077L,50078L,50079L,50080L,50081L,50082L,50083L,50084L,
50085L,50086L,50087L,50088L,50089L,50090L,50091L,50092L,50093L,50094L,
50095L,50096L,50097L,50098L,50099L,50100L,50101L,50102L,50103L,50104L,
50105L,50106L,50107L,50108L,50109L,50110L,50111L,50112L,50113L,50114L,
50115L,50116L,50117L,50118L,50119L,50120L,50121L,50122L,50123L,50124L,
50125L,50126L,50127L,50128L,50129L,50130L,50131L,50132L,50133L,50134L,
50135L,50136L,50137L,50138L,50139L,50140L,50141L,50142L,50143L,50144L,
50145L,50146L,50147L,50148L,50149L,50150L,50151L,50152L,50153L,50154L,
50155L,50156L,50157L,50158L,50159L,50160L,50161L,50162L,50163L,50164L,
50165L,50166L,50167L,50168L,50169L,50170L,50171L,50172L,50173L,50174L,
50175L,50176L,50177L,50178L,50179L,50180L,50181L,50182L,50183L,50184L,
50185L,50186L,50187L,50188L,50189L,50190L,50191L,50192L,50193L,50194L,
50195L,50196L,50197L,50198L,50199L,50200L,50201L,50202L,50203L,50204L,
50205L,50206L,50207L,50208L,50209L,50210L,50211L,50212L,50213L,50214L,
50215L,50216L,50217L,50218L,50219L,50220L,50221L,50222L,50223L,50224L,
50225L,50226L,50227L,50228L,50229L,50230L,50231L,50232L,50233L,50234L,
50235L,50236L,50237L,50238L,50239L,50240L,50241L,50242L,50243L,50244L,
50245L,50246L,50247L,50248L,50249L,50250L,50251L,50252L,50253L,50254L,
50255L,50256L,50257L,50258L,50259L,50260L,50261L,50262L,50263L,50264L,
50265L,50266L,50267L,50268L,50269L,50270L,50271L,50272L,50273L,50274L,
50275L,50276L,50277L,50278L,50279L,50280L,50281L,50282L,50283L,50284L,
50285L,50286L,50287L,50288L,50289L,50290L,50291L,50292L,50293L,50294L,
50295L,50296L,50297L,50298L,50299L,50300L,50301L,50302L,50303L,50304L,
50305L,50306L,50307L,50308L,50309L,50310L,50311L,50312L,50313L,50314L,
50315L,50316L,50317L,50318L,50319L,50320L,50321L,50322L,50323L,50324L,
50325L,50326L,50327L,50328L,50329L,50330L,50331L,50332L,50333L,50334L,
50335L,50336L,50337L,50338L,50339L,50340L,50341L,50342L,50343L,50344L,
50345L,50346L,50347L,50348L,50349L,50350L,50351L,50352L,50353L,50354L,
50355L,50356L,50357L,50358L,50359L,50360L,50361L,50362L,50363L,50364L,
50365L,50366L,50367L,50368L,50369L,50370L,50371L,50372L,50373L,50374L,
50375L,50376L,50377L,50378L,50379L,50380L,50381L,50382L,50383L,50384L,
50385L,50386L,50387L,50388L,50389L,50390L,50391L,50392L,50393L,50394L,
50395L,50396L,50397L,50398L,50399L,50400L,50401L,50402L,50403L,50404L,
50405L,50406L,50407L,50408L,50409L,50410L,50411L,50412L,50413L,50414L,
50415L,50416L,50417L,50418L,50419L,50420L,50421L,50422L,50423L,50424L,
50425L,50426L,50427L,50428L,50429L,50430L,50431L,50432L,50433L,50434L,
50435L,50436L,50437L,50438L,50439L,50440L,50441L,50442L,50443L,50444L,
50445L,50446L,50447L,50448L,50449L,50450L,50451L,50452L,50453L,50454L,
50455L,50456L,50457L,50458L,50459L,50460L,50461L,50462L,50463L,50464L,
50465L,50466L,50467L,50468L,50469L,50470L,50471L,50472L,50473L,50474L,
50475L,50476L,50477L,50478L,50479L,50480L,50481L,50482L,50483L,50484L,
50485L,50486L,50487L,50488L,50489L,50490L,50491L,50492L,50493L,50494L,
50495L,50496L,50497L,50498L,50499L,50500L,50501L,50502L,50503L,50504L,
50505L,50506L,50507L,50508L,50509L,50510L,50511L,50512L,50513L,50514L,
50515L,50516L,50517L,50518L,50519L,50520L,50521L,50522L,50523L,50524L,
50525L,50526L,50527L,50528L,50529L,50530L,50531L,50532L,50533L,50534L,
50535L,50536L,50537L,50538L,50539L,50540L,50541L,50542L,50543L,50544L,
50545L,50546L,50547L,50548L,50549L,50550L,50551L,50552L,50553L,50554L,
50555L,50556L,50557L,50558L,50559L,50560L,50561L,50562L,50563L,50564L,
50565L,50566L,50567L,50568L,50569L,50570L,50571L,50572L,50573L,50574L,
50575L,50576L,50577L,50578L,50579L,50580L,50581L,50582L,50583L,50584L,
50585L,50586L,50587L,50588L,50589L,50590L,50591L,50592L,50593L,50594L,
50595L,50596L,50597L,50598L,50599L,50600L,50601L,50602L,50603L,50604L,
50605L,50606L,50607L,50608L,50609L,50610L,50611L,50612L,50613L,50614L,
50615L,50616L,50617L,50618L,50619L,50620L,50621L,50622L,50623L,50624L,
50625L,50626L,50627L,50628L,50629L,50630L,50631L,50632L,50633L,50634L,
50635L,50636L,50637L,50638L,50639L,50640L,50641L,50642L,50643L,50644L,
50645L,50646L,50647L,50648L,50649L,50650L,50651L,50652L,50653L,50654L,
50655L,50656L,50657L,50658L,50659L,50660L,50661L,50662L,50663L,50664L,
50665L,50666L,50667L,50668L,50669L,50670L,50671L,50672L,50673L,50674L,
50675L,50676L,50677L,50678L,50679L,50680L,50681L,50682L,50683L,50684L,
50685L,50686L,50687L,50688L,50689L,50690L,50691L,50692L,50693L,50694L,
50695L,50696L,50697L,50698L,50699L,50700L,50701L,50702L,50703L,50704L,
50705L,50706L,50707L,50708L,50709L,50710L,50711L,50712L,50713L,50714L,
50715L,50716L,50717L,50718L,50719L,50720L,50721L,50722L,50723L,50724L,
50725L,50726L,50727L,50728L,50729L,50730L,50731L,50732L,50733L,50734L,
50735L,50736L,50737L,50738L,50739L,50740L,50741L,50742L,50743L,50744L,
50745L,50746L,50747L,50748L,50749L,50750L,50751L,50752L,50753L,50754L,
50755L,50756L,50757L,50758L,50759L,50760L,50761L,50762L,50763L,50764L,
50765L,50766L,50767L,50768L,50769L,50770L,50771L,50772L,50773L,50774L,
50775L,50776L,50777L,50778L,50779L,50780L,50781L,50782L,50783L,50784L,
50785L,50786L,50787L,50788L,50789L,50790L,50791L,50792L,50793L,50794L,
50795L,50796L,50797L,50798L,50799L,50800L,50801L,50802L,50803L,50804L,
50805L,50806L,50807L,50808L,50809L,50810L,50811L,50812L,50813L,50814L,
50815L,50816L,50817L,50818L,50819L,50820L,50821L,50822L,50823L,50824L,
50825L,50826L,50827L,50828L,50829L,50830L,50831L,50832L,50833L,50834L,
50835L,50836L,50837L,50838L,50839L,50840L,50841L,50842L,50843L,50844L,
50845L,50846L,50847L,50848L,50849L,50850L,50851L,50852L,50853L,50854L,
50855L,50856L,50857L,50858L,50859L,50860L,50861L,50862L,50863L,50864L,
50865L,50866L,50867L,50868L,50869L,50870L,50871L,50872L,50873L,50874L,
50875L,50876L,50877L,50878L,50879L,50880L,50881L,50882L,50883L,50884L,
50885L,50886L,50887L,50888L,50889L,50890L,50891L,50892L,50893L,50894L,
50895L,50896L,50897L,50898L,50899L,50900L,50901L,50902L,50903L,50904L,
50905L,50906L,50907L,50908L,50909L,50910L,50911L,50912L,50913L,50914L,
50915L,50916L,50917L,50918L,50919L,50920L,50921L,50922L,50923L,50924L,
50925L,50926L,50927L,50928L,50929L,50930L,50931L,50932L,50933L,50934L,
50935L,50936L,50937L,50938L,50939L,50940L,50941L,50942L,50943L,50944L,
50945L,50946L,50947L,50948L,50949L,50950L,50951L,50952L,50953L,50954L,
50955L,50956L,50957L,50958L,50959L,50960L,50961L,50962L,50963L,50964L,
50965L,50966L,50967L,50968L,50969L,50970L,50971L,50972L,50973L,50974L,
50975L,50976L,50977L,50978L,50979L,50980L,50981L,50982L,50983L,50984L,
50985L,50986L,50987L,50988L,50989L,50990L,50991L,50992L,50993L,50994L,
50995L,50996L,50997L,50998L,50999L,51000L,51001L,51002L,51003L,51004L,
51005L,51006L,51007L,51008L,51009L,51010L,51011L,51012L,51013L,51014L,
51015L,51016L,51017L,51018L,51019L,51020L,51021L,51022L,51023L,51024L,
51025L,51026L,51027L,51028L,51029L,51030L,51031L,51032L,51033L,51034L,
51035L,51036L,51037L,51038L,51039L,51040L,51041L,51042L,51043L,51044L,
51045L,51046L,51047L,51048L,51049L,51050L,51051L,51052L,51053L,51054L,
51055L,51056L,51057L,51058L,51059L,51060L,51061L,51062L,51063L,51064L,
51065L,51066L,51067L,51068L,51069L,51070L,51071L,51072L,51073L,51074L,
51075L,51076L,51077L,51078L,51079L,51080L,51081L,51082L,51083L,51084L,
51085L,51086L,51087L,51088L,51089L,51090L,51091L,51092L,51093L,51094L,
51095L,51096L,51097L,51098L,51099L,51100L,51101L,51102L,51103L,51104L,
51105L,51106L,51107L,51108L,51109L,51110L,51111L,51112L,51113L,51114L,
51115L,51116L,51117L,51118L,51119L,51120L,51121L,51122L,51123L,51124L,
51125L,51126L,51127L,51128L,51129L,51130L,51131L,51132L,51133L,51134L,
51135L,51136L,51137L,51138L,51139L,51140L,51141L,51142L,51143L,51144L,
51145L,51146L,51147L,51148L,51149L,51150L,51151L,51152L,51153L,51154L,
51155L,51156L,51157L,51158L,51159L,51160L,51161L,51162L,51163L,51164L,
51165L,51166L,51167L,51168L,51169L,51170L,51171L,51172L,51173L,51174L,
51175L,51176L,51177L,51178L,51179L,51180L,51181L,51182L,51183L,51184L,
51185L,51186L,51187L,51188L,51189L,51190L,51191L,51192L,51193L,51194L,
51195L,51196L,51197L,51198L,51199L,51200L,51201L,51202L,51203L,51204L,
51205L,51206L,51207L,51208L,51209L,51210L,51211L,51212L,51213L,51214L,
51215L,51216L,51217L,51218L,51219L,51220L,51221L,51222L,51223L,51224L,
51225L,51226L,51227L,51228L,51229L,51230L,51231L,51232L,51233L,51234L,
51235L,51236L,51237L,51238L,51239L,51240L,51241L,51242L,51243L,51244L,
51245L,51246L,51247L,51248L,51249L,51250L,51251L,51252L,51253L,51254L,
51255L,51256L,51257L,51258L,51259L,51260L,51261L,51262L,51263L,51264L,
51265L,51266L,51267L,51268L,51269L,51270L,51271L,51272L,51273L,51274L,
51275L,51276L,51277L,51278L,51279L,51280L,51281L,51282L,51283L,51284L,
51285L,51286L,51287L,51288L,51289L,51290L,51291L,51292L,51293L,51294L,
51295L,51296L,51297L,51298L,51299L,51300L,51301L,51302L,51303L,51304L,
51305L,51306L,51307L,51308L,51309L,51310L,51311L,51312L,51313L,51314L,
51315L,51316L,51317L,51318L,51319L,51320L,51321L,51322L,51323L,51324L,
51325L,51326L,51327L,51328L,51329L,51330L,51331L,51332L,51333L,51334L,
51335L,51336L,51337L,51338L,51339L,51340L,51341L,51342L,51343L,51344L,
51345L,51346L,51347L,51348L,51349L,51350L,51351L,51352L,51353L,51354L,
51355L,51356L,51357L,51358L,51359L,51360L,51361L,51362L,51363L,51364L,
51365L,51366L,51367L,51368L,51369L,51370L,51371L,51372L,51373L,51374L,
51375L,51376L,51377L,51378L,51379L,51380L,51381L,51382L,51383L,51384L,
51385L,51386L,51387L,51388L,51389L,51390L,51391L,51392L,51393L,51394L,
51395L,51396L,51397L,51398L,51399L,51400L,51401L,51402L,51403L,51404L,
51405L,51406L,51407L,51408L,51409L,51410L,51411L,51412L,51413L,51414L,
51415L,51416L,51417L,51418L,51419L,51420L,51421L,51422L,51423L,51424L,
51425L,51426L,51427L,51428L,51429L,51430L,51431L,51432L,51433L,51434L,
51435L,51436L,51437L,51438L,51439L,51440L,51441L,51442L,51443L,51444L,
51445L,51446L,51447L,51448L,51449L,51450L,51451L,51452L,51453L,51454L,
51455L,51456L,51457L,51458L,51459L,51460L,51461L,51462L,51463L,51464L,
51465L,51466L,51467L,51468L,51469L,51470L,51471L,51472L,51473L,51474L,
51475L,51476L,51477L,51478L,51479L,51480L,51481L,51482L,51483L,51484L,
51485L,51486L,51487L,51488L,51489L,51490L,51491L,51492L,51493L,51494L,
51495L,51496L,51497L,51498L,51499L,51500L,51501L,51502L,51503L,51504L,
51505L,51506L,51507L,51508L,51509L,51510L,51511L,51512L,51513L,51514L,
51515L,51516L,51517L,51518L,51519L,51520L,51521L,51522L,51523L,51524L,
51525L,51526L,51527L,51528L,51529L,51530L,51531L,51532L,51533L,51534L,
51535L,51536L,51537L,51538L,51539L,51540L,51541L,51542L,51543L,51544L,
51545L,51546L,51547L,51548L,51549L,51550L,51551L,51552L,51553L,51554L,
51555L,51556L,51557L,51558L,51559L,51560L,51561L,51562L,51563L,51564L,
51565L,51566L,51567L,51568L,51569L,51570L,51571L,51572L,51573L,51574L,
51575L,51576L,51577L,51578L,51579L,51580L,51581L,51582L,51583L,51584L,
51585L,51586L,51587L,51588L,51589L,51590L,51591L,51592L,51593L,51594L,
51595L,51596L,51597L,51598L,51599L,51600L,51601L,51602L,51603L,51604L,
51605L,51606L,51607L,51608L,51609L,51610L,51611L,51612L,51613L,51614L,
51615L,51616L,51617L,51618L,51619L,51620L,51621L,51622L,51623L,51624L,
51625L,51626L,51627L,51628L,51629L,51630L,51631L,51632L,51633L,51634L,
51635L,51636L,51637L,51638L,51639L,51640L,51641L,51642L,51643L,51644L,
51645L,51646L,51647L,51648L,51649L,51650L,51651L,51652L,51653L,51654L,
51655L,51656L,51657L,51658L,51659L,51660L,51661L,51662L,51663L,51664L,
51665L,51666L,51667L,51668L,51669L,51670L,51671L,51672L,51673L,51674L,
51675L,51676L,51677L,51678L,51679L,51680L,51681L,51682L,51683L,51684L,
51685L,51686L,51687L,51688L,51689L,51690L,51691L,51692L,51693L,51694L,
51695L,51696L,51697L,51698L,51699L,51700L,51701L,51702L,51703L,51704L,
51705L,51706L,51707L,51708L,51709L,51710L,51711L,51712L,51713L,51714L,
51715L,51716L,51717L,51718L,51719L,51720L,51721L,51722L,51723L,51724L,
51725L,51726L,51727L,51728L,51729L,51730L,51731L,51732L,51733L,51734L,
51735L,51736L,51737L,51738L,51739L,51740L,51741L,51742L,51743L,51744L,
51745L,51746L,51747L,51748L,51749L,51750L,51751L,51752L,51753L,51754L,
51755L,51756L,51757L,51758L,51759L,51760L,51761L,51762L,51763L,51764L,
51765L,51766L,51767L,51768L,51769L,51770L,51771L,51772L,51773L,51774L,
51775L,51776L,51777L,51778L,51779L,51780L,51781L,51782L,51783L,51784L,
51785L,51786L,51787L,51788L,51789L,51790L,51791L,51792L,51793L,51794L,
51795L,51796L,51797L,51798L,51799L,51800L,51801L,51802L,51803L,51804L,
51805L,51806L,51807L,51808L,51809L,51810L,51811L,51812L,51813L,51814L,
51815L,51816L,51817L,51818L,51819L,51820L,51821L,51822L,51823L,51824L,
51825L,51826L,51827L,51828L,51829L,51830L,51831L,51832L,51833L,51834L,
51835L,51836L,51837L,51838L,51839L,51840L,51841L,51842L,51843L,51844L,
51845L,51846L,51847L,51848L,51849L,51850L,51851L,51852L,51853L,51854L,
51855L,51856L,51857L,51858L,51859L,51860L,51861L,51862L,51863L,51864L,
51865L,51866L,51867L,51868L,51869L,51870L,51871L,51872L,51873L,51874L,
51875L,51876L,51877L,51878L,51879L,51880L,51881L,51882L,51883L,51884L,
51885L,51886L,51887L,51888L,51889L,51890L,51891L,51892L,51893L,51894L,
51895L,51896L,51897L,51898L,51899L,51900L,51901L,51902L,51903L,51904L,
51905L,51906L,51907L,51908L,51909L,51910L,51911L,51912L,51913L,51914L,
51915L,51916L,51917L,51918L,51919L,51920L,51921L,51922L,51923L,51924L,
51925L,51926L,51927L,51928L,51929L,51930L,51931L,51932L,51933L,51934L,
51935L,51936L,51937L,51938L,51939L,51940L,51941L,51942L,51943L,51944L,
51945L,51946L,51947L,51948L,51949L,51950L,51951L,51952L,51953L,51954L,
51955L,51956L,51957L,51958L,51959L,51960L,51961L,51962L,51963L,51964L,
51965L,51966L,51967L,51968L,51969L,51970L,51971L,51972L,51973L,51974L,
51975L,51976L,51977L,51978L,51979L,51980L,51981L,51982L,51983L,51984L,
51985L,51986L,51987L,51988L,51989L,51990L,51991L,51992L,51993L,51994L,
51995L,51996L,51997L,51998L,51999L,52000L,52001L,52002L,52003L,52004L,
52005L,52006L,52007L,52008L,52009L,52010L,52011L,52012L,52013L,52014L,
52015L,52016L,52017L,52018L,52019L,52020L,52021L,52022L,52023L,52024L,
52025L,52026L,52027L,52028L,52029L,52030L,52031L,52032L,52033L,52034L,
52035L,52036L,52037L,52038L,52039L,52040L,52041L,52042L,52043L,52044L,
52045L,52046L,52047L,52048L,52049L,52050L,52051L,52052L,52053L,52054L,
52055L,52056L,52057L,52058L,52059L,52060L,52061L,52062L,52063L,52064L,
52065L,52066L,52067L,52068L,52069L,52070L,52071L,52072L,52073L,52074L,
52075L,52076L,52077L,52078L,52079L,52080L,52081L,52082L,52083L,52084L,
52085L,52086L,52087L,52088L,52089L,52090L,52091L,52092L,52093L,52094L,
52095L,52096L,52097L,52098L,52099L,52100L,52101L,52102L,52103L,52104L,
52105L,52106L,52107L,52108L,52109L,52110L,52111L,52112L,52113L,52114L,
52115L,52116L,52117L,52118L,52119L,52120L,52121L,52122L,52123L,52124L,
52125L,52126L,52127L,52128L,52129L,52130L,52131L,52132L,52133L,52134L,
52135L,52136L,52137L,52138L,52139L,52140L,52141L,52142L,52143L,52144L,
52145L,52146L,52147L,52148L,52149L,52150L,52151L,52152L,52153L,52154L,
52155L,52156L,52157L,52158L,52159L,52160L,52161L,52162L,52163L,52164L,
52165L,52166L,52167L,52168L,52169L,52170L,52171L,52172L,52173L,52174L,
52175L,52176L,52177L,52178L,52179L,52180L,52181L,52182L,52183L,52184L,
52185L,52186L,52187L,52188L,52189L,52190L,52191L,52192L,52193L,52194L,
52195L,52196L,52197L,52198L,52199L,52200L,52201L,52202L,52203L,52204L,
52205L,52206L,52207L,52208L,52209L,52210L,52211L,52212L,52213L,52214L,
52215L,52216L,52217L,52218L,52219L,52220L,52221L,52222L,52223L,52224L,
52225L,52226L,52227L,52228L,52229L,52230L,52231L,52232L,52233L,52234L,
52235L,52236L,52237L,52238L,52239L,52240L,52241L,52242L,52243L,52244L,
52245L,52246L,52247L,52248L,52249L,52250L,52251L,52252L,52253L,52254L,
52255L,52256L,52257L,52258L,52259L,52260L,52261L,52262L,52263L,52264L,
52265L,52266L,52267L,52268L,52269L,52270L,52271L,52272L,52273L,52274L,
52275L,52276L,52277L,52278L,52279L,52280L,52281L,52282L,52283L,52284L,
52285L,52286L,52287L,52288L,52289L,52290L,52291L,52292L,52293L,52294L,
52295L,52296L,52297L,52298L,52299L,52300L,52301L,52302L,52303L,52304L,
52305L,52306L,52307L,52308L,52309L,52310L,52311L,52312L,52313L,52314L,
52315L,52316L,52317L,52318L,52319L,52320L,52321L,52322L,52323L,52324L,
52325L,52326L,52327L,52328L,52329L,52330L,52331L,52332L,52333L,52334L,
52335L,52336L,52337L,52338L,52339L,52340L,52341L,52342L,52343L,52344L,
52345L,52346L,52347L,52348L,52349L,52350L,52351L,52352L,52353L,52354L,
52355L,52356L,52357L,52358L,52359L,52360L,52361L,52362L,52363L,52364L,
52365L,52366L,52367L,52368L,52369L,52370L,52371L,52372L,52373L,52374L,
52375L,52376L,52377L,52378L,52379L,52380L,52381L,52382L,52383L,52384L,
52385L,52386L,52387L,52388L,52389L,52390L,52391L,52392L,52393L,52394L,
52395L,52396L,52397L,52398L,52399L,52400L,52401L,52402L,52403L,52404L,
52405L,52406L,52407L,52408L,52409L,52410L,52411L,52412L,52413L,52414L,
52415L,52416L,52417L,52418L,52419L,52420L,52421L,52422L,52423L,52424L,
52425L,52426L,52427L,52428L,52429L,52430L,52431L,52432L,52433L,52434L,
52435L,52436L,52437L,52438L,52439L,52440L,52441L,52442L,52443L,52444L,
52445L,52446L,52447L,52448L,52449L,52450L,52451L,52452L,52453L,52454L,
52455L,52456L,52457L,52458L,52459L,52460L,52461L,52462L,52463L,52464L,
52465L,52466L,52467L,52468L,52469L,52470L,52471L,52472L,52473L,52474L,
52475L,52476L,52477L,52478L,52479L,52480L,52481L,52482L,52483L,52484L,
52485L,52486L,52487L,52488L,52489L,52490L,52491L,52492L,52493L,52494L,
52495L,52496L,52497L,52498L,52499L,52500L,52501L,52502L,52503L,52504L,
52505L,52506L,52507L,52508L,52509L,52510L,52511L,52512L,52513L,52514L,
52515L,52516L,52517L,52518L,52519L,52520L,52521L,52522L,52523L,52524L,
52525L,52526L,52527L,52528L,52529L,52530L,52531L,52532L,52533L,52534L,
52535L,52536L,52537L,52538L,52539L,52540L,52541L,52542L,52543L,52544L,
52545L,52546L,52547L,52548L,52549L,52550L,52551L,52552L,52553L,52554L,
52555L,52556L,52557L,52558L,52559L,52560L,52561L,52562L,52563L,52564L,
52565L,52566L,52567L,52568L,52569L,52570L,52571L,52572L,52573L,52574L,
52575L,52576L,52577L,52578L,52579L,52580L,52581L,52582L,52583L,52584L,
52585L,52586L,52587L,52588L,52589L,52590L,52591L,52592L,52593L,52594L,
52595L,52596L,52597L,52598L,52599L,52600L,52601L,52602L,52603L,52604L,
52605L,52606L,52607L,52608L,52609L,52610L,52611L,52612L,52613L,52614L,
52615L,52616L,52617L,52618L,52619L,52620L,52621L,52622L,52623L,52624L,
52625L,52626L,52627L,52628L,52629L,52630L,52631L,52632L,52633L,52634L,
52635L,52636L,52637L,52638L,52639L,52640L,52641L,52642L,52643L,52644L,
52645L,52646L,52647L,52648L,52649L,52650L,52651L,52652L,52653L,52654L,
52655L,52656L,52657L,52658L,52659L,52660L,52661L,52662L,52663L,52664L,
52665L,52666L,52667L,52668L,52669L,52670L,52671L,52672L,52673L,52674L,
52675L,52676L,52677L,52678L,52679L,52680L,52681L,52682L,52683L,52684L,
52685L,52686L,52687L,52688L,52689L,52690L,52691L,52692L,52693L,52694L,
52695L,52696L,52697L,52698L,52699L,52700L,52701L,52702L,52703L,52704L,
52705L,52706L,52707L,52708L,52709L,52710L,52711L,52712L,52713L,52714L,
52715L,52716L,52717L,52718L,52719L,52720L,52721L,52722L,52723L,52724L,
52725L,52726L,52727L,52728L,52729L,52730L,52731L,52732L,52733L,52734L,
52735L,52736L,52737L,52738L,52739L,52740L,52741L,52742L,52743L,52744L,
52745L,52746L,52747L,52748L,52749L,52750L,52751L,52752L,52753L,52754L,
52755L,52756L,52757L,52758L,52759L,52760L,52761L,52762L,52763L,52764L,
52765L,52766L,52767L,52768L,52769L,52770L,52771L,52772L,52773L,52774L,
52775L,52776L,52777L,52778L,52779L,52780L,52781L,52782L,52783L,52784L,
52785L,52786L,52787L,52788L,52789L,52790L,52791L,52792L,52793L,52794L,
52795L,52796L,52797L,52798L,52799L,52800L,52801L,52802L,52803L,52804L,
52805L,52806L,52807L,52808L,52809L,52810L,52811L,52812L,52813L,52814L,
52815L,52816L,52817L,52818L,52819L,52820L,52821L,52822L,52823L,52824L,
52825L,52826L,52827L,52828L,52829L,52830L,52831L,52832L,52833L,52834L,
52835L,52836L,52837L,52838L,52839L,52840L,52841L,52842L,52843L,52844L,
52845L,52846L,52847L,52848L,52849L,52850L,52851L,52852L,52853L,52854L,
52855L,52856L,52857L,52858L,52859L,52860L,52861L,52862L,52863L,52864L,
52865L,52866L,52867L,52868L,52869L,52870L,52871L,52872L,52873L,52874L,
52875L,52876L,52877L,52878L,52879L,52880L,52881L,52882L,52883L,52884L,
52885L,52886L,52887L,52888L,52889L,52890L,52891L,52892L,52893L,52894L,
52895L,52896L,52897L,52898L,52899L,52900L,52901L,52902L,52903L,52904L,
52905L,52906L,52907L,52908L,52909L,52910L,52911L,52912L,52913L,52914L,
52915L,52916L,52917L,52918L,52919L,52920L,52921L,52922L,52923L,52924L,
52925L,52926L,52927L,52928L,52929L,52930L,52931L,52932L,52933L,52934L,
52935L,52936L,52937L,52938L,52939L,52940L,52941L,52942L,52943L,52944L,
52945L,52946L,52947L,52948L,52949L,52950L,52951L,52952L,52953L,52954L,
52955L,52956L,52957L,52958L,52959L,52960L,52961L,52962L,52963L,52964L,
52965L,52966L,52967L,52968L,52969L,52970L,52971L,52972L,52973L,52974L,
52975L,52976L,52977L,52978L,52979L,52980L,52981L,52982L,52983L,52984L,
52985L,52986L,52987L,52988L,52989L,52990L,52991L,52992L,52993L,52994L,
52995L,52996L,52997L,52998L,52999L,53000L,53001L,53002L,53003L,53004L,
53005L,53006L,53007L,53008L,53009L,53010L,53011L,53012L,53013L,53014L,
53015L,53016L,53017L,53018L,53019L,53020L,53021L,53022L,53023L,53024L,
53025L,53026L,53027L,53028L,53029L,53030L,53031L,53032L,53033L,53034L,
53035L,53036L,53037L,53038L,53039L,53040L,53041L,53042L,53043L,53044L,
53045L,53046L,53047L,53048L,53049L,53050L,53051L,53052L,53053L,53054L,
53055L,53056L,53057L,53058L,53059L,53060L,53061L,53062L,53063L,53064L,
53065L,53066L,53067L,53068L,53069L,53070L,53071L,53072L,53073L,53074L,
53075L,53076L,53077L,53078L,53079L,53080L,53081L,53082L,53083L,53084L,
53085L,53086L,53087L,53088L,53089L,53090L,53091L,53092L,53093L,53094L,
53095L,53096L,53097L,53098L,53099L,53100L,53101L,53102L,53103L,53104L,
53105L,53106L,53107L,53108L,53109L,53110L,53111L,53112L,53113L,53114L,
53115L,53116L,53117L,53118L,53119L,53120L,53121L,53122L,53123L,53124L,
53125L,53126L,53127L,53128L,53129L,53130L,53131L,53132L,53133L,53134L,
53135L,53136L,53137L,53138L,53139L,53140L,53141L,53142L,53143L,53144L,
53145L,53146L,53147L,53148L,53149L,53150L,53151L,53152L,53153L,53154L,
53155L,53156L,53157L,53158L,53159L,53160L,53161L,53162L,53163L,53164L,
53165L,53166L,53167L,53168L,53169L,53170L,53171L,53172L,53173L,53174L,
53175L,53176L,53177L,53178L,53179L,53180L,53181L,53182L,53183L,53184L,
53185L,53186L,53187L,53188L,53189L,53190L,53191L,53192L,53193L,53194L,
53195L,53196L,53197L,53198L,53199L,53200L,53201L,53202L,53203L,53204L,
53205L,53206L,53207L,53208L,53209L,53210L,53211L,53212L,53213L,53214L,
53215L,53216L,53217L,53218L,53219L,53220L,53221L,53222L,53223L,53224L,
53225L,53226L,53227L,53228L,53229L,53230L,53231L,53232L,53233L,53234L,
53235L,53236L,53237L,53238L,53239L,53240L,53241L,53242L,53243L,53244L,
53245L,53246L,53247L,53248L,53249L,53250L,53251L,53252L,53253L,53254L,
53255L,53256L,53257L,53258L,53259L,53260L,53261L,53262L,53263L,53264L,
53265L,53266L,53267L,53268L,53269L,53270L,53271L,53272L,53273L,53274L,
53275L,53276L,53277L,53278L,53279L,53280L,53281L,53282L,53283L,53284L,
53285L,53286L,53287L,53288L,53289L,53290L,53291L,53292L,53293L,53294L,
53295L,53296L,53297L,53298L,53299L,53300L,53301L,53302L,53303L,53304L,
53305L,53306L,53307L,53308L,53309L,53310L,53311L,53312L,53313L,53314L,
53315L,53316L,53317L,53318L,53319L,53320L,53321L,53322L,53323L,53324L,
53325L,53326L,53327L,53328L,53329L,53330L,53331L,53332L,53333L,53334L,
53335L,53336L,53337L,53338L,53339L,53340L,53341L,53342L,53343L,53344L,
53345L,53346L,53347L,53348L,53349L,53350L,53351L,53352L,53353L,53354L,
53355L,53356L,53357L,53358L,53359L,53360L,53361L,53362L,53363L,53364L,
53365L,53366L,53367L,53368L,53369L,53370L,53371L,53372L,53373L,53374L,
53375L,53376L,53377L,53378L,53379L,53380L,53381L,53382L,53383L,53384L,
53385L,53386L,53387L,53388L,53389L,53390L,53391L,53392L,53393L,53394L,
53395L,53396L,53397L,53398L,53399L,53400L,53401L,53402L,53403L,53404L,
53405L,53406L,53407L,53408L,53409L,53410L,53411L,53412L,53413L,53414L,
53415L,53416L,53417L,53418L,53419L,53420L,53421L,53422L,53423L,53424L,
53425L,53426L,53427L,53428L,53429L,53430L,53431L,53432L,53433L,53434L,
53435L,53436L,53437L,53438L,53439L,53440L,53441L,53442L,53443L,53444L,
53445L,53446L,53447L,53448L,53449L,53450L,53451L,53452L,53453L,53454L,
53455L,53456L,53457L,53458L,53459L,53460L,53461L,53462L,53463L,53464L,
53465L,53466L,53467L,53468L,53469L,53470L,53471L,53472L,53473L,53474L,
53475L,53476L,53477L,53478L,53479L,53480L,53481L,53482L,53483L,53484L,
53485L,53486L,53487L,53488L,53489L,53490L,53491L,53492L,53493L,53494L,
53495L,53496L,53497L,53498L,53499L,53500L,53501L,53502L,53503L,53504L,
53505L,53506L,53507L,53508L,53509L,53510L,53511L,53512L,53513L,53514L,
53515L,53516L,53517L,53518L,53519L,53520L,53521L,53522L,53523L,53524L,
53525L,53526L,53527L,53528L,53529L,53530L,53531L,53532L,53533L,53534L,
53535L,53536L,53537L,53538L,53539L,53540L,53541L,53542L,53543L,53544L,
53545L,53546L,53547L,53548L,53549L,53550L,53551L,53552L,53553L,53554L,
53555L,53556L,53557L,53558L,53559L,53560L,53561L,53562L,53563L,53564L,
53565L,53566L,53567L,53568L,53569L,53570L,53571L,53572L,53573L,53574L,
53575L,53576L,53577L,53578L,53579L,53580L,53581L,53582L,53583L,53584L,
53585L,53586L,53587L,53588L,53589L,53590L,53591L,53592L,53593L,53594L,
53595L,53596L,53597L,53598L,53599L,53600L,53601L,53602L,53603L,53604L,
53605L,53606L,53607L,53608L,53609L,53610L,53611L,53612L,53613L,53614L,
53615L,53616L,53617L,53618L,53619L,53620L,53621L,53622L,53623L,53624L,
53625L,53626L,53627L,53628L,53629L,53630L,53631L,53632L,53633L,53634L,
53635L,53636L,53637L,53638L,53639L,53640L,53641L,53642L,53643L,53644L,
53645L,53646L,53647L,53648L,53649L,53650L,53651L,53652L,53653L,53654L,
53655L,53656L,53657L,53658L,53659L,53660L,53661L,53662L,53663L,53664L,
53665L,53666L,53667L,53668L,53669L,53670L,53671L,53672L,53673L,53674L,
53675L,53676L,53677L,53678L,53679L,53680L,53681L,53682L,53683L,53684L,
53685L,53686L,53687L,53688L,53689L,53690L,53691L,53692L,53693L,53694L,
53695L,53696L,53697L,53698L,53699L,53700L,53701L,53702L,53703L,53704L,
53705L,53706L,53707L,53708L,53709L,53710L,53711L,53712L,53713L,53714L,
53715L,53716L,53717L,53718L,53719L,53720L,53721L,53722L,53723L,53724L,
53725L,53726L,53727L,53728L,53729L,53730L,53731L,53732L,53733L,53734L,
53735L,53736L,53737L,53738L,53739L,53740L,53741L,53742L,53743L,53744L,
53745L,53746L,53747L,53748L,53749L,53750L,53751L,53752L,53753L,53754L,
53755L,53756L,53757L,53758L,53759L,53760L,53761L,53762L,53763L,53764L,
53765L,53766L,53767L,53768L,53769L,53770L,53771L,53772L,53773L,53774L,
53775L,53776L,53777L,53778L,53779L,53780L,53781L,53782L,53783L,53784L,
53785L,53786L,53787L,53788L,53789L,53790L,53791L,53792L,53793L,53794L,
53795L,53796L,53797L,53798L,53799L,53800L,53801L,53802L,53803L,53804L,
53805L,53806L,53807L,53808L,53809L,53810L,53811L,53812L,53813L,53814L,
53815L,53816L,53817L,53818L,53819L,53820L,53821L,53822L,53823L,53824L,
53825L,53826L,53827L,53828L,53829L,53830L,53831L,53832L,53833L,53834L,
53835L,53836L,53837L,53838L,53839L,53840L,53841L,53842L,53843L,53844L,
53845L,53846L,53847L,53848L,53849L,53850L,53851L,53852L,53853L,53854L,
53855L,53856L,53857L,53858L,53859L,53860L,53861L,53862L,53863L,53864L,
53865L,53866L,53867L,53868L,53869L,53870L,53871L,53872L,53873L,53874L,
53875L,53876L,53877L,53878L,53879L,53880L,53881L,53882L,53883L,53884L,
53885L,53886L,53887L,53888L,53889L,53890L,53891L,53892L,53893L,53894L,
53895L,53896L,53897L,53898L,53899L,53900L,53901L,53902L,53903L,53904L,
53905L,53906L,53907L,53908L,53909L,53910L,53911L,53912L,53913L,53914L,
53915L,53916L,53917L,53918L,53919L,53920L,53921L,53922L,53923L,53924L,
53925L,53926L,53927L,53928L,53929L,53930L,53931L,53932L,53933L,53934L,
53935L,53936L,53937L,53938L,53939L,53940L,53941L,53942L,53943L,53944L,
53945L,53946L,53947L,53948L,53949L,53950L,53951L,53952L,53953L,53954L,
53955L,53956L,53957L,53958L,53959L,53960L,53961L,53962L,53963L,53964L,
53965L,53966L,53967L,53968L,53969L,53970L,53971L,53972L,53973L,53974L,
53975L,53976L,53977L,53978L,53979L,53980L,53981L,53982L,53983L,53984L,
53985L,53986L,53987L,53988L,53989L,53990L,53991L,53992L,53993L,53994L,
53995L,53996L,53997L,53998L,53999L,54000L,54001L,54002L,54003L,54004L,
54005L,54006L,54007L,54008L,54009L,54010L,54011L,54012L,54013L,54014L,
54015L,54016L,54017L,54018L,54019L,54020L,54021L,54022L,54023L,54024L,
54025L,54026L,54027L,54028L,54029L,54030L,54031L,54032L,54033L,54034L,
54035L,54036L,54037L,54038L,54039L,54040L,54041L,54042L,54043L,54044L,
54045L,54046L,54047L,54048L,54049L,54050L,54051L,54052L,54053L,54054L,
54055L,54056L,54057L,54058L,54059L,54060L,54061L,54062L,54063L,54064L,
54065L,54066L,54067L,54068L,54069L,54070L,54071L,54072L,54073L,54074L,
54075L,54076L,54077L,54078L,54079L,54080L,54081L,54082L,54083L,54084L,
54085L,54086L,54087L,54088L,54089L,54090L,54091L,54092L,54093L,54094L,
54095L,54096L,54097L,54098L,54099L,54100L,54101L,54102L,54103L,54104L,
54105L,54106L,54107L,54108L,54109L,54110L,54111L,54112L,54113L,54114L,
54115L,54116L,54117L,54118L,54119L,54120L,54121L,54122L,54123L,54124L,
54125L,54126L,54127L,54128L,54129L,54130L,54131L,54132L,54133L,54134L,
54135L,54136L,54137L,54138L,54139L,54140L,54141L,54142L,54143L,54144L,
54145L,54146L,54147L,54148L,54149L,54150L,54151L,54152L,54153L,54154L,
54155L,54156L,54157L,54158L,54159L,54160L,54161L,54162L,54163L,54164L,
54165L,54166L,54167L,54168L,54169L,54170L,54171L,54172L,54173L,54174L,
54175L,54176L,54177L,54178L,54179L,54180L,54181L,54182L,54183L,54184L,
54185L,54186L,54187L,54188L,54189L,54190L,54191L,54192L,54193L,54194L,
54195L,54196L,54197L,54198L,54199L,54200L,54201L,54202L,54203L,54204L,
54205L,54206L,54207L,54208L,54209L,54210L,54211L,54212L,54213L,54214L,
54215L,54216L,54217L,54218L,54219L,54220L,54221L,54222L,54223L,54224L,
54225L,54226L,54227L,54228L,54229L,54230L,54231L,54232L,54233L,54234L,
54235L,54236L,54237L,54238L,54239L,54240L,54241L,54242L,54243L,54244L,
54245L,54246L,54247L,54248L,54249L,54250L,54251L,54252L,54253L,54254L,
54255L,54256L,54257L,54258L,54259L,54260L,54261L,54262L,54263L,54264L,
54265L,54266L,54267L,54268L,54269L,54270L,54271L,54272L,54273L,54274L,
54275L,54276L,54277L,54278L,54279L,54280L,54281L,54282L,54283L,54284L,
54285L,54286L,54287L,54288L,54289L,54290L,54291L,54292L,54293L,54294L,
54295L,54296L,54297L,54298L,54299L,54300L,54301L,54302L,54303L,54304L,
54305L,54306L,54307L,54308L,54309L,54310L,54311L,54312L,54313L,54314L,
54315L,54316L,54317L,54318L,54319L,54320L,54321L,54322L,54323L,54324L,
54325L,54326L,54327L,54328L,54329L,54330L,54331L,54332L,54333L,54334L,
54335L,54336L,54337L,54338L,54339L,54340L,54341L,54342L,54343L,54344L,
54345L,54346L,54347L,54348L,54349L,54350L,54351L,54352L,54353L,54354L,
54355L,54356L,54357L,54358L,54359L,54360L,54361L,54362L,54363L,54364L,
54365L,54366L,54367L,54368L,54369L,54370L,54371L,54372L,54373L,54374L,
54375L,54376L,54377L,54378L,54379L,54380L,54381L,54382L,54383L,54384L,
54385L,54386L,54387L,54388L,54389L,54390L,54391L,54392L,54393L,54394L,
54395L,54396L,54397L,54398L,54399L,54400L,54401L,54402L,54403L,54404L,
54405L,54406L,54407L,54408L,54409L,54410L,54411L,54412L,54413L,54414L,
54415L,54416L,54417L,54418L,54419L,54420L,54421L,54422L,54423L,54424L,
54425L,54426L,54427L,54428L,54429L,54430L,54431L,54432L,54433L,54434L,
54435L,54436L,54437L,54438L,54439L,54440L,54441L,54442L,54443L,54444L,
54445L,54446L,54447L,54448L,54449L,54450L,54451L,54452L,54453L,54454L,
54455L,54456L,54457L,54458L,54459L,54460L,54461L,54462L,54463L,54464L,
54465L,54466L,54467L,54468L,54469L,54470L,54471L,54472L,54473L,54474L,
54475L,54476L,54477L,54478L,54479L,54480L,54481L,54482L,54483L,54484L,
54485L,54486L,54487L,54488L,54489L,54490L,54491L,54492L,54493L,54494L,
54495L,54496L,54497L,54498L,54499L,54500L,54501L,54502L,54503L,54504L,
54505L,54506L,54507L,54508L,54509L,54510L,54511L,54512L,54513L,54514L,
54515L,54516L,54517L,54518L,54519L,54520L,54521L,54522L,54523L,54524L,
54525L,54526L,54527L,54528L,54529L,54530L,54531L,54532L,54533L,54534L,
54535L,54536L,54537L,54538L,54539L,54540L,54541L,54542L,54543L,54544L,
54545L,54546L,54547L,54548L,54549L,54550L,54551L,54552L,54553L,54554L,
54555L,54556L,54557L,54558L,54559L,54560L,54561L,54562L,54563L,54564L,
54565L,54566L,54567L,54568L,54569L,54570L,54571L,54572L,54573L,54574L,
54575L,54576L,54577L,54578L,54579L,54580L,54581L,54582L,54583L,54584L,
54585L,54586L,54587L,54588L,54589L,54590L,54591L,54592L,54593L,54594L,
54595L,54596L,54597L,54598L,54599L,54600L,54601L,54602L,54603L,54604L,
54605L,54606L,54607L,54608L,54609L,54610L,54611L,54612L,54613L,54614L,
54615L,54616L,54617L,54618L,54619L,54620L,54621L,54622L,54623L,54624L,
54625L,54626L,54627L,54628L,54629L,54630L,54631L,54632L,54633L,54634L,
54635L,54636L,54637L,54638L,54639L,54640L,54641L,54642L,54643L,54644L,
54645L,54646L,54647L,54648L,54649L,54650L,54651L,54652L,54653L,54654L,
54655L,54656L,54657L,54658L,54659L,54660L,54661L,54662L,54663L,54664L,
54665L,54666L,54667L,54668L,54669L,54670L,54671L,54672L,54673L,54674L,
54675L,54676L,54677L,54678L,54679L,54680L,54681L,54682L,54683L,54684L,
54685L,54686L,54687L,54688L,54689L,54690L,54691L,54692L,54693L,54694L,
54695L,54696L,54697L,54698L,54699L,54700L,54701L,54702L,54703L,54704L,
54705L,54706L,54707L,54708L,54709L,54710L,54711L,54712L,54713L,54714L,
54715L,54716L,54717L,54718L,54719L,54720L,54721L,54722L,54723L,54724L,
54725L,54726L,54727L,54728L,54729L,54730L,54731L,54732L,54733L,54734L,
54735L,54736L,54737L,54738L,54739L,54740L,54741L,54742L,54743L,54744L,
54745L,54746L,54747L,54748L,54749L,54750L,54751L,54752L,54753L,54754L,
54755L,54756L,54757L,54758L,54759L,54760L,54761L,54762L,54763L,54764L,
54765L,54766L,54767L,54768L,54769L,54770L,54771L,54772L,54773L,54774L,
54775L,54776L,54777L,54778L,54779L,54780L,54781L,54782L,54783L,54784L,
54785L,54786L,54787L,54788L,54789L,54790L,54791L,54792L,54793L,54794L,
54795L,54796L,54797L,54798L,54799L,54800L,54801L,54802L,54803L,54804L,
54805L,54806L,54807L,54808L,54809L,54810L,54811L,54812L,54813L,54814L,
54815L,54816L,54817L,54818L,54819L,54820L,54821L,54822L,54823L,54824L,
54825L,54826L,54827L,54828L,54829L,54830L,54831L,54832L,54833L,54834L,
54835L,54836L,54837L,54838L,54839L,54840L,54841L,54842L,54843L,54844L,
54845L,54846L,54847L,54848L,54849L,54850L,54851L,54852L,54853L,54854L,
54855L,54856L,54857L,54858L,54859L,54860L,54861L,54862L,54863L,54864L,
54865L,54866L,54867L,54868L,54869L,54870L,54871L,54872L,54873L,54874L,
54875L,54876L,54877L,54878L,54879L,54880L,54881L,54882L,54883L,54884L,
54885L,54886L,54887L,54888L,54889L,54890L,54891L,54892L,54893L,54894L,
54895L,54896L,54897L,54898L,54899L,54900L,54901L,54902L,54903L,54904L,
54905L,54906L,54907L,54908L,54909L,54910L,54911L,54912L,54913L,54914L,
54915L,54916L,54917L,54918L,54919L,54920L,54921L,54922L,54923L,54924L,
54925L,54926L,54927L,54928L,54929L,54930L,54931L,54932L,54933L,54934L,
54935L,54936L,54937L,54938L,54939L,54940L,54941L,54942L,54943L,54944L,
54945L,54946L,54947L,54948L,54949L,54950L,54951L,54952L,54953L,54954L,
54955L,54956L,54957L,54958L,54959L,54960L,54961L,54962L,54963L,54964L,
54965L,54966L,54967L,54968L,54969L,54970L,54971L,54972L,54973L,54974L,
54975L,54976L,54977L,54978L,54979L,54980L,54981L,54982L,54983L,54984L,
54985L,54986L,54987L,54988L,54989L,54990L,54991L,54992L,54993L,54994L,
54995L,54996L,54997L,54998L,54999L,55000L,55001L,55002L,55003L,55004L,
55005L,55006L,55007L,55008L,55009L,55010L,55011L,55012L,55013L,55014L,
55015L,55016L,55017L,55018L,55019L,55020L,55021L,55022L,55023L,55024L,
55025L,55026L,55027L,55028L,55029L,55030L,55031L,55032L,55033L,55034L,
55035L,55036L,55037L,55038L,55039L,55040L,55041L,55042L,55043L,55044L,
55045L,55046L,55047L,55048L,55049L,55050L,55051L,55052L,55053L,55054L,
55055L,55056L,55057L,55058L,55059L,55060L,55061L,55062L,55063L,55064L,
55065L,55066L,55067L,55068L,55069L,55070L,55071L,55072L,55073L,55074L,
55075L,55076L,55077L,55078L,55079L,55080L,55081L,55082L,55083L,55084L,
55085L,55086L,55087L,55088L,55089L,55090L,55091L,55092L,55093L,55094L,
55095L,55096L,55097L,55098L,55099L,55100L,55101L,55102L,55103L,55104L,
55105L,55106L,55107L,55108L,55109L,55110L,55111L,55112L,55113L,55114L,
55115L,55116L,55117L,55118L,55119L,55120L,55121L,55122L,55123L,55124L,
55125L,55126L,55127L,55128L,55129L,55130L,55131L,55132L,55133L,55134L,
55135L,55136L,55137L,55138L,55139L,55140L,55141L,55142L,55143L,55144L,
55145L,55146L,55147L,55148L,55149L,55150L,55151L,55152L,55153L,55154L,
55155L,55156L,55157L,55158L,55159L,55160L,55161L,55162L,55163L,55164L,
55165L,55166L,55167L,55168L,55169L,55170L,55171L,55172L,55173L,55174L,
55175L,55176L,55177L,55178L,55179L,55180L,55181L,55182L,55183L,55184L,
55185L,55186L,55187L,55188L,55189L,55190L,55191L,55192L,55193L,55194L,
55195L,55196L,55197L,55198L,55199L,55200L,55201L,55202L,55203L,55204L,
55205L,55206L,55207L,55208L,55209L,55210L,55211L,55212L,55213L,55214L,
55215L,55216L,55217L,55218L,55219L,55220L,55221L,55222L,55223L,55224L,
55225L,55226L,55227L,55228L,55229L,55230L,55231L,55232L,55233L,55234L,
55235L,55236L,55237L,55238L,55239L,55240L,55241L,55242L,55243L,55244L,
55245L,55246L,55247L,55248L,55249L,55250L,55251L,55252L,55253L,55254L,
55255L,55256L,55257L,55258L,55259L,55260L,55261L,55262L,55263L,55264L,
55265L,55266L,55267L,55268L,55269L,55270L,55271L,55272L,55273L,55274L,
55275L,55276L,55277L,55278L,55279L,55280L,55281L,55282L,55283L,55284L,
55285L,55286L,55287L,55288L,55289L,55290L,55291L,55292L,55293L,55294L,
55295L,55296L,55297L,55298L,55299L,55300L,55301L,55302L,55303L,55304L,
55305L,55306L,55307L,55308L,55309L,55310L,55311L,55312L,55313L,55314L,
55315L,55316L,55317L,55318L,55319L,55320L,55321L,55322L,55323L,55324L,
55325L,55326L,55327L,55328L,55329L,55330L,55331L,55332L,55333L,55334L,
55335L,55336L,55337L,55338L,55339L,55340L,55341L,55342L,55343L,55344L,
55345L,55346L,55347L,55348L,55349L,55350L,55351L,55352L,55353L,55354L,
55355L,55356L,55357L,55358L,55359L,55360L,55361L,55362L,55363L,55364L,
55365L,55366L,55367L,55368L,55369L,55370L,55371L,55372L,55373L,55374L,
55375L,55376L,55377L,55378L,55379L,55380L,55381L,55382L,55383L,55384L,
55385L,55386L,55387L,55388L,55389L,55390L,55391L,55392L,55393L,55394L,
55395L,55396L,55397L,55398L,55399L,55400L,55401L,55402L,55403L,55404L,
55405L,55406L,55407L,55408L,55409L,55410L,55411L,55412L,55413L,55414L,
55415L,55416L,55417L,55418L,55419L,55420L,55421L,55422L,55423L,55424L,
55425L,55426L,55427L,55428L,55429L,55430L,55431L,55432L,55433L,55434L,
55435L,55436L,55437L,55438L,55439L,55440L,55441L,55442L,55443L,55444L,
55445L,55446L,55447L,55448L,55449L,55450L,55451L,55452L,55453L,55454L,
55455L,55456L,55457L,55458L,55459L,55460L,55461L,55462L,55463L,55464L,
55465L,55466L,55467L,55468L,55469L,55470L,55471L,55472L,55473L,55474L,
55475L,55476L,55477L,55478L,55479L,55480L,55481L,55482L,55483L,55484L,
55485L,55486L,55487L,55488L,55489L,55490L,55491L,55492L,55493L,55494L,
55495L,55496L,55497L,55498L,55499L,55500L,55501L,55502L,55503L,55504L,
55505L,55506L,55507L,55508L,55509L,55510L,55511L,55512L,55513L,55514L,
55515L,55516L,55517L,55518L,55519L,55520L,55521L,55522L,55523L,55524L,
55525L,55526L,55527L,55528L,55529L,55530L,55531L,55532L,55533L,55534L,
55535L,55536L,55537L,55538L,55539L,55540L,55541L,55542L,55543L,55544L,
55545L,55546L,55547L,55548L,55549L,55550L,55551L,55552L,55553L,55554L,
55555L,55556L,55557L,55558L,55559L,55560L,55561L,55562L,55563L,55564L,
55565L,55566L,55567L,55568L,55569L,55570L,55571L,55572L,55573L,55574L,
55575L,55576L,55577L,55578L,55579L,55580L,55581L,55582L,55583L,55584L,
55585L,55586L,55587L,55588L,55589L,55590L,55591L,55592L,55593L,55594L,
55595L,55596L,55597L,55598L,55599L,55600L,55601L,55602L,55603L,55604L,
55605L,55606L,55607L,55608L,55609L,55610L,55611L,55612L,55613L,55614L,
55615L,55616L,55617L,55618L,55619L,55620L,55621L,55622L,55623L,55624L,
55625L,55626L,55627L,55628L,55629L,55630L,55631L,55632L,55633L,55634L,
55635L,55636L,55637L,55638L,55639L,55640L,55641L,55642L,55643L,55644L,
55645L,55646L,55647L,55648L,55649L,55650L,55651L,55652L,55653L,55654L,
55655L,55656L,55657L,55658L,55659L,55660L,55661L,55662L,55663L,55664L,
55665L,55666L,55667L,55668L,55669L,55670L,55671L,55672L,55673L,55674L,
55675L,55676L,55677L,55678L,55679L,55680L,55681L,55682L,55683L,55684L,
55685L,55686L,55687L,55688L,55689L,55690L,55691L,55692L,55693L,55694L,
55695L,55696L,55697L,55698L,55699L,55700L,55701L,55702L,55703L,55704L,
55705L,55706L,55707L,55708L,55709L,55710L,55711L,55712L,55713L,55714L,
55715L,55716L,55717L,55718L,55719L,55720L,55721L,55722L,55723L,55724L,
55725L,55726L,55727L,55728L,55729L,55730L,55731L,55732L,55733L,55734L,
55735L,55736L,55737L,55738L,55739L,55740L,55741L,55742L,55743L,55744L,
55745L,55746L,55747L,55748L,55749L,55750L,55751L,55752L,55753L,55754L,
55755L,55756L,55757L,55758L,55759L,55760L,55761L,55762L,55763L,55764L,
55765L,55766L,55767L,55768L,55769L,55770L,55771L,55772L,55773L,55774L,
55775L,55776L,55777L,55778L,55779L,55780L,55781L,55782L,55783L,55784L,
55785L,55786L,55787L,55788L,55789L,55790L,55791L,55792L,55793L,55794L,
55795L,55796L,55797L,55798L,55799L,55800L,55801L,55802L,55803L,55804L,
55805L,55806L,55807L,55808L,55809L,55810L,55811L,55812L,55813L,55814L,
55815L,55816L,55817L,55818L,55819L,55820L,55821L,55822L,55823L,55824L,
55825L,55826L,55827L,55828L,55829L,55830L,55831L,55832L,55833L,55834L,
55835L,55836L,55837L,55838L,55839L,55840L,55841L,55842L,55843L,55844L,
55845L,55846L,55847L,55848L,55849L,55850L,55851L,55852L,55853L,55854L,
55855L,55856L,55857L,55858L,55859L,55860L,55861L,55862L,55863L,55864L,
55865L,55866L,55867L,55868L,55869L,55870L,55871L,55872L,55873L,55874L,
55875L,55876L,55877L,55878L,55879L,55880L,55881L,55882L,55883L,55884L,
55885L,55886L,55887L,55888L,55889L,55890L,55891L,55892L,55893L,55894L,
55895L,55896L,55897L,55898L,55899L,55900L,55901L,55902L,55903L,55904L,
55905L,55906L,55907L,55908L,55909L,55910L,55911L,55912L,55913L,55914L,
55915L,55916L,55917L,55918L,55919L,55920L,55921L,55922L,55923L,55924L,
55925L,55926L,55927L,55928L,55929L,55930L,55931L,55932L,55933L,55934L,
55935L,55936L,55937L,55938L,55939L,55940L,55941L,55942L,55943L,55944L,
55945L,55946L,55947L,55948L,55949L,55950L,55951L,55952L,55953L,55954L,
55955L,55956L,55957L,55958L,55959L,55960L,55961L,55962L,55963L,55964L,
55965L,55966L,55967L,55968L,55969L,55970L,55971L,55972L,55973L,55974L,
55975L,55976L,55977L,55978L,55979L,55980L,55981L,55982L,55983L,55984L,
55985L,55986L,55987L,55988L,55989L,55990L,55991L,55992L,55993L,55994L,
55995L,55996L,55997L,55998L,55999L,56000L,56001L,56002L,56003L,56004L,
56005L,56006L,56007L,56008L,56009L,56010L,56011L,56012L,56013L,56014L,
56015L,56016L,56017L,56018L,56019L,56020L,56021L,56022L,56023L,56024L,
56025L,56026L,56027L,56028L,56029L,56030L,56031L,56032L,56033L,56034L,
56035L,56036L,56037L,56038L,56039L,56040L,56041L,56042L,56043L,56044L,
56045L,56046L,56047L,56048L,56049L,56050L,56051L,56052L,56053L,56054L,
56055L,56056L,56057L,56058L,56059L,56060L,56061L,56062L,56063L,56064L,
56065L,56066L,56067L,56068L,56069L,56070L,56071L,56072L,56073L,56074L,
56075L,56076L,56077L,56078L,56079L,56080L,56081L,56082L,56083L,56084L,
56085L,56086L,56087L,56088L,56089L,56090L,56091L,56092L,56093L,56094L,
56095L,56096L,56097L,56098L,56099L,56100L,56101L,56102L,56103L,56104L,
56105L,56106L,56107L,56108L,56109L,56110L,56111L,56112L,56113L,56114L,
56115L,56116L,56117L,56118L,56119L,56120L,56121L,56122L,56123L,56124L,
56125L,56126L,56127L,56128L,56129L,56130L,56131L,56132L,56133L,56134L,
56135L,56136L,56137L,56138L,56139L,56140L,56141L,56142L,56143L,56144L,
56145L,56146L,56147L,56148L,56149L,56150L,56151L,56152L,56153L,56154L,
56155L,56156L,56157L,56158L,56159L,56160L,56161L,56162L,56163L,56164L,
56165L,56166L,56167L,56168L,56169L,56170L,56171L,56172L,56173L,56174L,
56175L,56176L,56177L,56178L,56179L,56180L,56181L,56182L,56183L,56184L,
56185L,56186L,56187L,56188L,56189L,56190L,56191L,56192L,56193L,56194L,
56195L,56196L,56197L,56198L,56199L,56200L,56201L,56202L,56203L,56204L,
56205L,56206L,56207L,56208L,56209L,56210L,56211L,56212L,56213L,56214L,
56215L,56216L,56217L,56218L,56219L,56220L,56221L,56222L,56223L,56224L,
56225L,56226L,56227L,56228L,56229L,56230L,56231L,56232L,56233L,56234L,
56235L,56236L,56237L,56238L,56239L,56240L,56241L,56242L,56243L,56244L,
56245L,56246L,56247L,56248L,56249L,56250L,56251L,56252L,56253L,56254L,
56255L,56256L,56257L,56258L,56259L,56260L,56261L,56262L,56263L,56264L,
56265L,56266L,56267L,56268L,56269L,56270L,56271L,56272L,56273L,56274L,
56275L,56276L,56277L,56278L,56279L,56280L,56281L,56282L,56283L,56284L,
56285L,56286L,56287L,56288L,56289L,56290L,56291L,56292L,56293L,56294L,
56295L,56296L,56297L,56298L,56299L,56300L,56301L,56302L,56303L,56304L,
56305L,56306L,56307L,56308L,56309L,56310L,56311L,56312L,56313L,56314L,
56315L,56316L,56317L,56318L,56319L,56320L,56321L,56322L,56323L,56324L,
56325L,56326L,56327L,56328L,56329L,56330L,56331L,56332L,56333L,56334L,
56335L,56336L,56337L,56338L,56339L,56340L,56341L,56342L,56343L,56344L,
56345L,56346L,56347L,56348L,56349L,56350L,56351L,56352L,56353L,56354L,
56355L,56356L,56357L,56358L,56359L,56360L,56361L,56362L,56363L,56364L,
56365L,56366L,56367L,56368L,56369L,56370L,56371L,56372L,56373L,56374L,
56375L,56376L,56377L,56378L,56379L,56380L,56381L,56382L,56383L,56384L,
56385L,56386L,56387L,56388L,56389L,56390L,56391L,56392L,56393L,56394L,
56395L,56396L,56397L,56398L,56399L,56400L,56401L,56402L,56403L,56404L,
56405L,56406L,56407L,56408L,56409L,56410L,56411L,56412L,56413L,56414L,
56415L,56416L,56417L,56418L,56419L,56420L,56421L,56422L,56423L,56424L,
56425L,56426L,56427L,56428L,56429L,56430L,56431L,56432L,56433L,56434L,
56435L,56436L,56437L,56438L,56439L,56440L,56441L,56442L,56443L,56444L,
56445L,56446L,56447L,56448L,56449L,56450L,56451L,56452L,56453L,56454L,
56455L,56456L,56457L,56458L,56459L,56460L,56461L,56462L,56463L,56464L,
56465L,56466L,56467L,56468L,56469L,56470L,56471L,56472L,56473L,56474L,
56475L,56476L,56477L,56478L,56479L,56480L,56481L,56482L,56483L,56484L,
56485L,56486L,56487L,56488L,56489L,56490L,56491L,56492L,56493L,56494L,
56495L,56496L,56497L,56498L,56499L,56500L,56501L,56502L,56503L,56504L,
56505L,56506L,56507L,56508L,56509L,56510L,56511L,56512L,56513L,56514L,
56515L,56516L,56517L,56518L,56519L,56520L,56521L,56522L,56523L,56524L,
56525L,56526L,56527L,56528L,56529L,56530L,56531L,56532L,56533L,56534L,
56535L,56536L,56537L,56538L,56539L,56540L,56541L,56542L,56543L,56544L,
56545L,56546L,56547L,56548L,56549L,56550L,56551L,56552L,56553L,56554L,
56555L,56556L,56557L,56558L,56559L,56560L,56561L,56562L,56563L,56564L,
56565L,56566L,56567L,56568L,56569L,56570L,56571L,56572L,56573L,56574L,
56575L,56576L,56577L,56578L,56579L,56580L,56581L,56582L,56583L,56584L,
56585L,56586L,56587L,56588L,56589L,56590L,56591L,56592L,56593L,56594L,
56595L,56596L,56597L,56598L,56599L,56600L,56601L,56602L,56603L,56604L,
56605L,56606L,56607L,56608L,56609L,56610L,56611L,56612L,56613L,56614L,
56615L,56616L,56617L,56618L,56619L,56620L,56621L,56622L,56623L,56624L,
56625L,56626L,56627L,56628L,56629L,56630L,56631L,56632L,56633L,56634L,
56635L,56636L,56637L,56638L,56639L,56640L,56641L,56642L,56643L,56644L,
56645L,56646L,56647L,56648L,56649L,56650L,56651L,56652L,56653L,56654L,
56655L,56656L,56657L,56658L,56659L,56660L,56661L,56662L,56663L,56664L,
56665L,56666L,56667L,56668L,56669L,56670L,56671L,56672L,56673L,56674L,
56675L,56676L,56677L,56678L,56679L,56680L,56681L,56682L,56683L,56684L,
56685L,56686L,56687L,56688L,56689L,56690L,56691L,56692L,56693L,56694L,
56695L,56696L,56697L,56698L,56699L,56700L,56701L,56702L,56703L,56704L,
56705L,56706L,56707L,56708L,56709L,56710L,56711L,56712L,56713L,56714L,
56715L,56716L,56717L,56718L,56719L,56720L,56721L,56722L,56723L,56724L,
56725L,56726L,56727L,56728L,56729L,56730L,56731L,56732L,56733L,56734L,
56735L,56736L,56737L,56738L,56739L,56740L,56741L,56742L,56743L,56744L,
56745L,56746L,56747L,56748L,56749L,56750L,56751L,56752L,56753L,56754L,
56755L,56756L,56757L,56758L,56759L,56760L,56761L,56762L,56763L,56764L,
56765L,56766L,56767L,56768L,56769L,56770L,56771L,56772L,56773L,56774L,
56775L,56776L,56777L,56778L,56779L,56780L,56781L,56782L,56783L,56784L,
56785L,56786L,56787L,56788L,56789L,56790L,56791L,56792L,56793L,56794L,
56795L,56796L,56797L,56798L,56799L,56800L,56801L,56802L,56803L,56804L,
56805L,56806L,56807L,56808L,56809L,56810L,56811L,56812L,56813L,56814L,
56815L,56816L,56817L,56818L,56819L,56820L,56821L,56822L,56823L,56824L,
56825L,56826L,56827L,56828L,56829L,56830L,56831L,56832L,56833L,56834L,
56835L,56836L,56837L,56838L,56839L,56840L,56841L,56842L,56843L,56844L,
56845L,56846L,56847L,56848L,56849L,56850L,56851L,56852L,56853L,56854L,
56855L,56856L,56857L,56858L,56859L,56860L,56861L,56862L,56863L,56864L,
56865L,56866L,56867L,56868L,56869L,56870L,56871L,56872L,56873L,56874L,
56875L,56876L,56877L,56878L,56879L,56880L,56881L,56882L,56883L,56884L,
56885L,56886L,56887L,56888L,56889L,56890L,56891L,56892L,56893L,56894L,
56895L,56896L,56897L,56898L,56899L,56900L,56901L,56902L,56903L,56904L,
56905L,56906L,56907L,56908L,56909L,56910L,56911L,56912L,56913L,56914L,
56915L,56916L,56917L,56918L,56919L,56920L,56921L,56922L,56923L,56924L,
56925L,56926L,56927L,56928L,56929L,56930L,56931L,56932L,56933L,56934L,
56935L,56936L,56937L,56938L,56939L,56940L,56941L,56942L,56943L,56944L,
56945L,56946L,56947L,56948L,56949L,56950L,56951L,56952L,56953L,56954L,
56955L,56956L,56957L,56958L,56959L,56960L,56961L,56962L,56963L,56964L,
56965L,56966L,56967L,56968L,56969L,56970L,56971L,56972L,56973L,56974L,
56975L,56976L,56977L,56978L,56979L,56980L,56981L,56982L,56983L,56984L,
56985L,56986L,56987L,56988L,56989L,56990L,56991L,56992L,56993L,56994L,
56995L,56996L,56997L,56998L,56999L,57000L,57001L,57002L,57003L,57004L,
57005L,57006L,57007L,57008L,57009L,57010L,57011L,57012L,57013L,57014L,
57015L,57016L,57017L,57018L,57019L,57020L,57021L,57022L,57023L,57024L,
57025L,57026L,57027L,57028L,57029L,57030L,57031L,57032L,57033L,57034L,
57035L,57036L,57037L,57038L,57039L,57040L,57041L,57042L,57043L,57044L,
57045L,57046L,57047L,57048L,57049L,57050L,57051L,57052L,57053L,57054L,
57055L,57056L,57057L,57058L,57059L,57060L,57061L,57062L,57063L,57064L,
57065L,57066L,57067L,57068L,57069L,57070L,57071L,57072L,57073L,57074L,
57075L,57076L,57077L,57078L,57079L,57080L,57081L,57082L,57083L,57084L,
57085L,57086L,57087L,57088L,57089L,57090L,57091L,57092L,57093L,57094L,
57095L,57096L,57097L,57098L,57099L,57100L,57101L,57102L,57103L,57104L,
57105L,57106L,57107L,57108L,57109L,57110L,57111L,57112L,57113L,57114L,
57115L,57116L,57117L,57118L,57119L,57120L,57121L,57122L,57123L,57124L,
57125L,57126L,57127L,57128L,57129L,57130L,57131L,57132L,57133L,57134L,
57135L,57136L,57137L,57138L,57139L,57140L,57141L,57142L,57143L,57144L,
57145L,57146L,57147L,57148L,57149L,57150L,57151L,57152L,57153L,57154L,
57155L,57156L,57157L,57158L,57159L,57160L,57161L,57162L,57163L,57164L,
57165L,57166L,57167L,57168L,57169L,57170L,57171L,57172L,57173L,57174L,
57175L,57176L,57177L,57178L,57179L,57180L,57181L,57182L,57183L,57184L,
57185L,57186L,57187L,57188L,57189L,57190L,57191L,57192L,57193L,57194L,
57195L,57196L,57197L,57198L,57199L,57200L,57201L,57202L,57203L,57204L,
57205L,57206L,57207L,57208L,57209L,57210L,57211L,57212L,57213L,57214L,
57215L,57216L,57217L,57218L,57219L,57220L,57221L,57222L,57223L,57224L,
57225L,57226L,57227L,57228L,57229L,57230L,57231L,57232L,57233L,57234L,
57235L,57236L,57237L,57238L,57239L,57240L,57241L,57242L,57243L,57244L,
57245L,57246L,57247L,57248L,57249L,57250L,57251L,57252L,57253L,57254L,
57255L,57256L,57257L,57258L,57259L,57260L,57261L,57262L,57263L,57264L,
57265L,57266L,57267L,57268L,57269L,57270L,57271L,57272L,57273L,57274L,
57275L,57276L,57277L,57278L,57279L,57280L,57281L,57282L,57283L,57284L,
57285L,57286L,57287L,57288L,57289L,57290L,57291L,57292L,57293L,57294L,
57295L,57296L,57297L,57298L,57299L,57300L,57301L,57302L,57303L,57304L,
57305L,57306L,57307L,57308L,57309L,57310L,57311L,57312L,57313L,57314L,
57315L,57316L,57317L,57318L,57319L,57320L,57321L,57322L,57323L,57324L,
57325L,57326L,57327L,57328L,57329L,57330L,57331L,57332L,57333L,57334L,
57335L,57336L,57337L,57338L,57339L,57340L,57341L,57342L,57343L,57344L,
57345L,57346L,57347L,57348L,57349L,57350L,57351L,57352L,57353L,57354L,
57355L,57356L,57357L,57358L,57359L,57360L,57361L,57362L,57363L,57364L,
57365L,57366L,57367L,57368L,57369L,57370L,57371L,57372L,57373L,57374L,
57375L,57376L,57377L,57378L,57379L,57380L,57381L,57382L,57383L,57384L,
57385L,57386L,57387L,57388L,57389L,57390L,57391L,57392L,57393L,57394L,
57395L,57396L,57397L,57398L,57399L,57400L,57401L,57402L,57403L,57404L,
57405L,57406L,57407L,57408L,57409L,57410L,57411L,57412L,57413L,57414L,
57415L,57416L,57417L,57418L,57419L,57420L,57421L,57422L,57423L,57424L,
57425L,57426L,57427L,57428L,57429L,57430L,57431L,57432L,57433L,57434L,
57435L,57436L,57437L,57438L,57439L,57440L,57441L,57442L,57443L,57444L,
57445L,57446L,57447L,57448L,57449L,57450L,57451L,57452L,57453L,57454L,
57455L,57456L,57457L,57458L,57459L,57460L,57461L,57462L,57463L,57464L,
57465L,57466L,57467L,57468L,57469L,57470L,57471L,57472L,57473L,57474L,
57475L,57476L,57477L,57478L,57479L,57480L,57481L,57482L,57483L,57484L,
57485L,57486L,57487L,57488L,57489L,57490L,57491L,57492L,57493L,57494L,
57495L,57496L,57497L,57498L,57499L,57500L,57501L,57502L,57503L,57504L,
57505L,57506L,57507L,57508L,57509L,57510L,57511L,57512L,57513L,57514L,
57515L,57516L,57517L,57518L,57519L,57520L,57521L,57522L,57523L,57524L,
57525L,57526L,57527L,57528L,57529L,57530L,57531L,57532L,57533L,57534L,
57535L,57536L,57537L,57538L,57539L,57540L,57541L,57542L,57543L,57544L,
57545L,57546L,57547L,57548L,57549L,57550L,57551L,57552L,57553L,57554L,
57555L,57556L,57557L,57558L,57559L,57560L,57561L,57562L,57563L,57564L,
57565L,57566L,57567L,57568L,57569L,57570L,57571L,57572L,57573L,57574L,
57575L,57576L,57577L,57578L,57579L,57580L,57581L,57582L,57583L,57584L,
57585L,57586L,57587L,57588L,57589L,57590L,57591L,57592L,57593L,57594L,
57595L,57596L,57597L,57598L,57599L,57600L,57601L,57602L,57603L,57604L,
57605L,57606L,57607L,57608L,57609L,57610L,57611L,57612L,57613L,57614L,
57615L,57616L,57617L,57618L,57619L,57620L,57621L,57622L,57623L,57624L,
57625L,57626L,57627L,57628L,57629L,57630L,57631L,57632L,57633L,57634L,
57635L,57636L,57637L,57638L,57639L,57640L,57641L,57642L,57643L,57644L,
57645L,57646L,57647L,57648L,57649L,57650L,57651L,57652L,57653L,57654L,
57655L,57656L,57657L,57658L,57659L,57660L,57661L,57662L,57663L,57664L,
57665L,57666L,57667L,57668L,57669L,57670L,57671L,57672L,57673L,57674L,
57675L,57676L,57677L,57678L,57679L,57680L,57681L,57682L,57683L,57684L,
57685L,57686L,57687L,57688L,57689L,57690L,57691L,57692L,57693L,57694L,
57695L,57696L,57697L,57698L,57699L,57700L,57701L,57702L,57703L,57704L,
57705L,57706L,57707L,57708L,57709L,57710L,57711L,57712L,57713L,57714L,
57715L,57716L,57717L,57718L,57719L,57720L,57721L,57722L,57723L,57724L,
57725L,57726L,57727L,57728L,57729L,57730L,57731L,57732L,57733L,57734L,
57735L,57736L,57737L,57738L,57739L,57740L,57741L,57742L,57743L,57744L,
57745L,57746L,57747L,57748L,57749L,57750L,57751L,57752L,57753L,57754L,
57755L,57756L,57757L,57758L,57759L,57760L,57761L,57762L,57763L,57764L,
57765L,57766L,57767L,57768L,57769L,57770L,57771L,57772L,57773L,57774L,
57775L,57776L,57777L,57778L,57779L,57780L,57781L,57782L,57783L,57784L,
57785L,57786L,57787L,57788L,57789L,57790L,57791L,57792L,57793L,57794L,
57795L,57796L,57797L,57798L,57799L,57800L,57801L,57802L,57803L,57804L,
57805L,57806L,57807L,57808L,57809L,57810L,57811L,57812L,57813L,57814L,
57815L,57816L,57817L,57818L,57819L,57820L,57821L,57822L,57823L,57824L,
57825L,57826L,57827L,57828L,57829L,57830L,57831L,57832L,57833L,57834L,
57835L,57836L,57837L,57838L,57839L,57840L,57841L,57842L,57843L,57844L,
57845L,57846L,57847L,57848L,57849L,57850L,57851L,57852L,57853L,57854L,
57855L,57856L,57857L,57858L,57859L,57860L,57861L,57862L,57863L,57864L,
57865L,57866L,57867L,57868L,57869L,57870L,57871L,57872L,57873L,57874L,
57875L,57876L,57877L,57878L,57879L,57880L,57881L,57882L,57883L,57884L,
57885L,57886L,57887L,57888L,57889L,57890L,57891L,57892L,57893L,57894L,
57895L,57896L,57897L,57898L,57899L,57900L,57901L,57902L,57903L,57904L,
57905L,57906L,57907L,57908L,57909L,57910L,57911L,57912L,57913L,57914L,
57915L,57916L,57917L,57918L,57919L,57920L,57921L,57922L,57923L,57924L,
57925L,57926L,57927L,57928L,57929L,57930L,57931L,57932L,57933L,57934L,
57935L,57936L,57937L,57938L,57939L,57940L,57941L,57942L,57943L,57944L,
57945L,57946L,57947L,57948L,57949L,57950L,57951L,57952L,57953L,57954L,
57955L,57956L,57957L,57958L,57959L,57960L,57961L,57962L,57963L,57964L,
57965L,57966L,57967L,57968L,57969L,57970L,57971L,57972L,57973L,57974L,
57975L,57976L,57977L,57978L,57979L,57980L,57981L,57982L,57983L,57984L,
57985L,57986L,57987L,57988L,57989L,57990L,57991L,57992L,57993L,57994L,
57995L,57996L,57997L,57998L,57999L,58000L,58001L,58002L,58003L,58004L,
58005L,58006L,58007L,58008L,58009L,58010L,58011L,58012L,58013L,58014L,
58015L,58016L,58017L,58018L,58019L,58020L,58021L,58022L,58023L,58024L,
58025L,58026L,58027L,58028L,58029L,58030L,58031L,58032L,58033L,58034L,
58035L,58036L,58037L,58038L,58039L,58040L,58041L,58042L,58043L,58044L,
58045L,58046L,58047L,58048L,58049L,58050L,58051L,58052L,58053L,58054L,
58055L,58056L,58057L,58058L,58059L,58060L,58061L,58062L,58063L,58064L,
58065L,58066L,58067L,58068L,58069L,58070L,58071L,58072L,58073L,58074L,
58075L,58076L,58077L,58078L,58079L,58080L,58081L,58082L,58083L,58084L,
58085L,58086L,58087L,58088L,58089L,58090L,58091L,58092L,58093L,58094L,
58095L,58096L,58097L,58098L,58099L,58100L,58101L,58102L,58103L,58104L,
58105L,58106L,58107L,58108L,58109L,58110L,58111L,58112L,58113L,58114L,
58115L,58116L,58117L,58118L,58119L,58120L,58121L,58122L,58123L,58124L,
58125L,58126L,58127L,58128L,58129L,58130L,58131L,58132L,58133L,58134L,
58135L,58136L,58137L,58138L,58139L,58140L,58141L,58142L,58143L,58144L,
58145L,58146L,58147L,58148L,58149L,58150L,58151L,58152L,58153L,58154L,
58155L,58156L,58157L,58158L,58159L,58160L,58161L,58162L,58163L,58164L,
58165L,58166L,58167L,58168L,58169L,58170L,58171L,58172L,58173L,58174L,
58175L,58176L,58177L,58178L,58179L,58180L,58181L,58182L,58183L,58184L,
58185L,58186L,58187L,58188L,58189L,58190L,58191L,58192L,58193L,58194L,
58195L,58196L,58197L,58198L,58199L,58200L,58201L,58202L,58203L,58204L,
58205L,58206L,58207L,58208L,58209L,58210L,58211L,58212L,58213L,58214L,
58215L,58216L,58217L,58218L,58219L,58220L,58221L,58222L,58223L,58224L,
58225L,58226L,58227L,58228L,58229L,58230L,58231L,58232L,58233L,58234L,
58235L,58236L,58237L,58238L,58239L,58240L,58241L,58242L,58243L,58244L,
58245L,58246L,58247L,58248L,58249L,58250L,58251L,58252L,58253L,58254L,
58255L,58256L,58257L,58258L,58259L,58260L,58261L,58262L,58263L,58264L,
58265L,58266L,58267L,58268L,58269L,58270L,58271L,58272L,58273L,58274L,
58275L,58276L,58277L,58278L,58279L,58280L,58281L,58282L,58283L,58284L,
58285L,58286L,58287L,58288L,58289L,58290L,58291L,58292L,58293L,58294L,
58295L,58296L,58297L,58298L,58299L,58300L,58301L,58302L,58303L,58304L,
58305L,58306L,58307L,58308L,58309L,58310L,58311L,58312L,58313L,58314L,
58315L,58316L,58317L,58318L,58319L,58320L,58321L,58322L,58323L,58324L,
58325L,58326L,58327L,58328L,58329L,58330L,58331L,58332L,58333L,58334L,
58335L,58336L,58337L,58338L,58339L,58340L,58341L,58342L,58343L,58344L,
58345L,58346L,58347L,58348L,58349L,58350L,58351L,58352L,58353L,58354L,
58355L,58356L,58357L,58358L,58359L,58360L,58361L,58362L,58363L,58364L,
58365L,58366L,58367L,58368L,58369L,58370L,58371L,58372L,58373L,58374L,
58375L,58376L,58377L,58378L,58379L,58380L,58381L,58382L,58383L,58384L,
58385L,58386L,58387L,58388L,58389L,58390L,58391L,58392L,58393L,58394L,
58395L,58396L,58397L,58398L,58399L,58400L,58401L,58402L,58403L,58404L,
58405L,58406L,58407L,58408L,58409L,58410L,58411L,58412L,58413L,58414L,
58415L,58416L,58417L,58418L,58419L,58420L,58421L,58422L,58423L,58424L,
58425L,58426L,58427L,58428L,58429L,58430L,58431L,58432L,58433L,58434L,
58435L,58436L,58437L,58438L,58439L,58440L,58441L,58442L,58443L,58444L,
58445L,58446L,58447L,58448L,58449L,58450L,58451L,58452L,58453L,58454L,
58455L,58456L,58457L,58458L,58459L,58460L,58461L,58462L,58463L,58464L,
58465L,58466L,58467L,58468L,58469L,58470L,58471L,58472L,58473L,58474L,
58475L,58476L,58477L,58478L,58479L,58480L,58481L,58482L,58483L,58484L,
58485L,58486L,58487L,58488L,58489L,58490L,58491L,58492L,58493L,58494L,
58495L,58496L,58497L,58498L,58499L,58500L,58501L,58502L,58503L,58504L,
58505L,58506L,58507L,58508L,58509L,58510L,58511L,58512L,58513L,58514L,
58515L,58516L,58517L,58518L,58519L,58520L,58521L,58522L,58523L,58524L,
58525L,58526L,58527L,58528L,58529L,58530L,58531L,58532L,58533L,58534L,
58535L,58536L,58537L,58538L,58539L,58540L,58541L,58542L,58543L,58544L,
58545L,58546L,58547L,58548L,58549L,58550L,58551L,58552L,58553L,58554L,
58555L,58556L,58557L,58558L,58559L,58560L,58561L,58562L,58563L,58564L,
58565L,58566L,58567L,58568L,58569L,58570L,58571L,58572L,58573L,58574L,
58575L,58576L,58577L,58578L,58579L,58580L,58581L,58582L,58583L,58584L,
58585L,58586L,58587L,58588L,58589L,58590L,58591L,58592L,58593L,58594L,
58595L,58596L,58597L,58598L,58599L,58600L,58601L,58602L,58603L,58604L,
58605L,58606L,58607L,58608L,58609L,58610L,58611L,58612L,58613L,58614L,
58615L,58616L,58617L,58618L,58619L,58620L,58621L,58622L,58623L,58624L,
58625L,58626L,58627L,58628L,58629L,58630L,58631L,58632L,58633L,58634L,
58635L,58636L,58637L,58638L,58639L,58640L,58641L,58642L,58643L,58644L,
58645L,58646L,58647L,58648L,58649L,58650L,58651L,58652L,58653L,58654L,
58655L,58656L,58657L,58658L,58659L,58660L,58661L,58662L,58663L,58664L,
58665L,58666L,58667L,58668L,58669L,58670L,58671L,58672L,58673L,58674L,
58675L,58676L,58677L,58678L,58679L,58680L,58681L,58682L,58683L,58684L,
58685L,58686L,58687L,58688L,58689L,58690L,58691L,58692L,58693L,58694L,
58695L,58696L,58697L,58698L,58699L,58700L,58701L,58702L,58703L,58704L,
58705L,58706L,58707L,58708L,58709L,58710L,58711L,58712L,58713L,58714L,
58715L,58716L,58717L,58718L,58719L,58720L,58721L,58722L,58723L,58724L,
58725L,58726L,58727L,58728L,58729L,58730L,58731L,58732L,58733L,58734L,
58735L,58736L,58737L,58738L,58739L,58740L,58741L,58742L,58743L,58744L,
58745L,58746L,58747L,58748L,58749L,58750L,58751L,58752L,58753L,58754L,
58755L,58756L,58757L,58758L,58759L,58760L,58761L,58762L,58763L,58764L,
58765L,58766L,58767L,58768L,58769L,58770L,58771L,58772L,58773L,58774L,
58775L,58776L,58777L,58778L,58779L,58780L,58781L,58782L,58783L,58784L,
58785L,58786L,58787L,58788L,58789L,58790L,58791L,58792L,58793L,58794L,
58795L,58796L,58797L,58798L,58799L,58800L,58801L,58802L,58803L,58804L,
58805L,58806L,58807L,58808L,58809L,58810L,58811L,58812L,58813L,58814L,
58815L,58816L,58817L,58818L,58819L,58820L,58821L,58822L,58823L,58824L,
58825L,58826L,58827L,58828L,58829L,58830L,58831L,58832L,58833L,58834L,
58835L,58836L,58837L,58838L,58839L,58840L,58841L,58842L,58843L,58844L,
58845L,58846L,58847L,58848L,58849L,58850L,58851L,58852L,58853L,58854L,
58855L,58856L,58857L,58858L,58859L,58860L,58861L,58862L,58863L,58864L,
58865L,58866L,58867L,58868L,58869L,58870L,58871L,58872L,58873L,58874L,
58875L,58876L,58877L,58878L,58879L,58880L,58881L,58882L,58883L,58884L,
58885L,58886L,58887L,58888L,58889L,58890L,58891L,58892L,58893L,58894L,
58895L,58896L,58897L,58898L,58899L,58900L,58901L,58902L,58903L,58904L,
58905L,58906L,58907L,58908L,58909L,58910L,58911L,58912L,58913L,58914L,
58915L,58916L,58917L,58918L,58919L,58920L,58921L,58922L,58923L,58924L,
58925L,58926L,58927L,58928L,58929L,58930L,58931L,58932L,58933L,58934L,
58935L,58936L,58937L,58938L,58939L,58940L,58941L,58942L,58943L,58944L,
58945L,58946L,58947L,58948L,58949L,58950L,58951L,58952L,58953L,58954L,
58955L,58956L,58957L,58958L,58959L,58960L,58961L,58962L,58963L,58964L,
58965L,58966L,58967L,58968L,58969L,58970L,58971L,58972L,58973L,58974L,
58975L,58976L,58977L,58978L,58979L,58980L,58981L,58982L,58983L,58984L,
58985L,58986L,58987L,58988L,58989L,58990L,58991L,58992L,58993L,58994L,
58995L,58996L,58997L,58998L,58999L,59000L,59001L,59002L,59003L,59004L,
59005L,59006L,59007L,59008L,59009L,59010L,59011L,59012L,59013L,59014L,
59015L,59016L,59017L,59018L,59019L,59020L,59021L,59022L,59023L,59024L,
59025L,59026L,59027L,59028L,59029L,59030L,59031L,59032L,59033L,59034L,
59035L,59036L,59037L,59038L,59039L,59040L,59041L,59042L,59043L,59044L,
59045L,59046L,59047L,59048L,59049L,59050L,59051L,59052L,59053L,59054L,
59055L,59056L,59057L,59058L,59059L,59060L,59061L,59062L,59063L,59064L,
59065L,59066L,59067L,59068L,59069L,59070L,59071L,59072L,59073L,59074L,
59075L,59076L,59077L,59078L,59079L,59080L,59081L,59082L,59083L,59084L,
59085L,59086L,59087L,59088L,59089L,59090L,59091L,59092L,59093L,59094L,
59095L,59096L,59097L,59098L,59099L,59100L,59101L,59102L,59103L,59104L,
59105L,59106L,59107L,59108L,59109L,59110L,59111L,59112L,59113L,59114L,
59115L,59116L,59117L,59118L,59119L,59120L,59121L,59122L,59123L,59124L,
59125L,59126L,59127L,59128L,59129L,59130L,59131L,59132L,59133L,59134L,
59135L,59136L,59137L,59138L,59139L,59140L,59141L,59142L,59143L,59144L,
59145L,59146L,59147L,59148L,59149L,59150L,59151L,59152L,59153L,59154L,
59155L,59156L,59157L,59158L,59159L,59160L,59161L,59162L,59163L,59164L,
59165L,59166L,59167L,59168L,59169L,59170L,59171L,59172L,59173L,59174L,
59175L,59176L,59177L,59178L,59179L,59180L,59181L,59182L,59183L,59184L,
59185L,59186L,59187L,59188L,59189L,59190L,59191L,59192L,59193L,59194L,
59195L,59196L,59197L,59198L,59199L,59200L,59201L,59202L,59203L,59204L,
59205L,59206L,59207L,59208L,59209L,59210L,59211L,59212L,59213L,59214L,
59215L,59216L,59217L,59218L,59219L,59220L,59221L,59222L,59223L,59224L,
59225L,59226L,59227L,59228L,59229L,59230L,59231L,59232L,59233L,59234L,
59235L,59236L,59237L,59238L,59239L,59240L,59241L,59242L,59243L,59244L,
59245L,59246L,59247L,59248L,59249L,59250L,59251L,59252L,59253L,59254L,
59255L,59256L,59257L,59258L,59259L,59260L,59261L,59262L,59263L,59264L,
59265L,59266L,59267L,59268L,59269L,59270L,59271L,59272L,59273L,59274L,
59275L,59276L,59277L,59278L,59279L,59280L,59281L,59282L,59283L,59284L,
59285L,59286L,59287L,59288L,59289L,59290L,59291L,59292L,59293L,59294L,
59295L,59296L,59297L,59298L,59299L,59300L,59301L,59302L,59303L,59304L,
59305L,59306L,59307L,59308L,59309L,59310L,59311L,59312L,59313L,59314L,
59315L,59316L,59317L,59318L,59319L,59320L,59321L,59322L,59323L,59324L,
59325L,59326L,59327L,59328L,59329L,59330L,59331L,59332L,59333L,59334L,
59335L,59336L,59337L,59338L,59339L,59340L,59341L,59342L,59343L,59344L,
59345L,59346L,59347L,59348L,59349L,59350L,59351L,59352L,59353L,59354L,
59355L,59356L,59357L,59358L,59359L,59360L,59361L,59362L,59363L,59364L,
59365L,59366L,59367L,59368L,59369L,59370L,59371L,59372L,59373L,59374L,
59375L,59376L,59377L,59378L,59379L,59380L,59381L,59382L,59383L,59384L,
59385L,59386L,59387L,59388L,59389L,59390L,59391L,59392L,59393L,59394L,
59395L,59396L,59397L,59398L,59399L,59400L,59401L,59402L,59403L,59404L,
59405L,59406L,59407L,59408L,59409L,59410L,59411L,59412L,59413L,59414L,
59415L,59416L,59417L,59418L,59419L,59420L,59421L,59422L,59423L,59424L,
59425L,59426L,59427L,59428L,59429L,59430L,59431L,59432L,59433L,59434L,
59435L,59436L,59437L,59438L,59439L,59440L,59441L,59442L,59443L,59444L,
59445L,59446L,59447L,59448L,59449L,59450L,59451L,59452L,59453L,59454L,
59455L,59456L,59457L,59458L,59459L,59460L,59461L,59462L,59463L,59464L,
59465L,59466L,59467L,59468L,59469L,59470L,59471L,59472L,59473L,59474L,
59475L,59476L,59477L,59478L,59479L,59480L,59481L,59482L,59483L,59484L,
59485L,59486L,59487L,59488L,59489L,59490L,59491L,59492L,59493L,59494L,
59495L,59496L,59497L,59498L,59499L,59500L,59501L,59502L,59503L,59504L,
59505L,59506L,59507L,59508L,59509L,59510L,59511L,59512L,59513L,59514L,
59515L,59516L,59517L,59518L,59519L,59520L,59521L,59522L,59523L,59524L,
59525L,59526L,59527L,59528L,59529L,59530L,59531L,59532L,59533L,59534L,
59535L,59536L,59537L,59538L,59539L,59540L,59541L,59542L,59543L,59544L,
59545L,59546L,59547L,59548L,59549L,59550L,59551L,59552L,59553L,59554L,
59555L,59556L,59557L,59558L,59559L,59560L,59561L,59562L,59563L,59564L,
59565L,59566L,59567L,59568L,59569L,59570L,59571L,59572L,59573L,59574L,
59575L,59576L,59577L,59578L,59579L,59580L,59581L,59582L,59583L,59584L,
59585L,59586L,59587L,59588L,59589L,59590L,59591L,59592L,59593L,59594L,
59595L,59596L,59597L,59598L,59599L,59600L,59601L,59602L,59603L,59604L,
59605L,59606L,59607L,59608L,59609L,59610L,59611L,59612L,59613L,59614L,
59615L,59616L,59617L,59618L,59619L,59620L,59621L,59622L,59623L,59624L,
59625L,59626L,59627L,59628L,59629L,59630L,59631L,59632L,59633L,59634L,
59635L,59636L,59637L,59638L,59639L,59640L,59641L,59642L,59643L,59644L,
59645L,59646L,59647L,59648L,59649L,59650L,59651L,59652L,59653L,59654L,
59655L,59656L,59657L,59658L,59659L,59660L,59661L,59662L,59663L,59664L,
59665L,59666L,59667L,59668L,59669L,59670L,59671L,59672L,59673L,59674L,
59675L,59676L,59677L,59678L,59679L,59680L,59681L,59682L,59683L,59684L,
59685L,59686L,59687L,59688L,59689L,59690L,59691L,59692L,59693L,59694L,
59695L,59696L,59697L,59698L,59699L,59700L,59701L,59702L,59703L,59704L,
59705L,59706L,59707L,59708L,59709L,59710L,59711L,59712L,59713L,59714L,
59715L,59716L,59717L,59718L,59719L,59720L,59721L,59722L,59723L,59724L,
59725L,59726L,59727L,59728L,59729L,59730L,59731L,59732L,59733L,59734L,
59735L,59736L,59737L,59738L,59739L,59740L,59741L,59742L,59743L,59744L,
59745L,59746L,59747L,59748L,59749L,59750L,59751L,59752L,59753L,59754L,
59755L,59756L,59757L,59758L,59759L,59760L,59761L,59762L,59763L,59764L,
59765L,59766L,59767L,59768L,59769L,59770L,59771L,59772L,59773L,59774L,
59775L,59776L,59777L,59778L,59779L,59780L,59781L,59782L,59783L,59784L,
59785L,59786L,59787L,59788L,59789L,59790L,59791L,59792L,59793L,59794L,
59795L,59796L,59797L,59798L,59799L,59800L,59801L,59802L,59803L,59804L,
59805L,59806L,59807L,59808L,59809L,59810L,59811L,59812L,59813L,59814L,
59815L,59816L,59817L,59818L,59819L,59820L,59821L,59822L,59823L,59824L,
59825L,59826L,59827L,59828L,59829L,59830L,59831L,59832L,59833L,59834L,
59835L,59836L,59837L,59838L,59839L,59840L,59841L,59842L,59843L,59844L,
59845L,59846L,59847L,59848L,59849L,59850L,59851L,59852L,59853L,59854L,
59855L,59856L,59857L,59858L,59859L,59860L,59861L,59862L,59863L,59864L,
59865L,59866L,59867L,59868L,59869L,59870L,59871L,59872L,59873L,59874L,
59875L,59876L,59877L,59878L,59879L,59880L,59881L,59882L,59883L,59884L,
59885L,59886L,59887L,59888L,59889L,59890L,59891L,59892L,59893L,59894L,
59895L,59896L,59897L,59898L,59899L,59900L,59901L,59902L,59903L,59904L,
59905L,59906L,59907L,59908L,59909L,59910L,59911L,59912L,59913L,59914L,
59915L,59916L,59917L,59918L,59919L,59920L,59921L,59922L,59923L,59924L,
59925L,59926L,59927L,59928L,59929L,59930L,59931L,59932L,59933L,59934L,
59935L,59936L,59937L,59938L,59939L,59940L,59941L,59942L,59943L,59944L,
59945L,59946L,59947L,59948L,59949L,59950L,59951L,59952L,59953L,59954L,
59955L,59956L,59957L,59958L,59959L,59960L,59961L,59962L,59963L,59964L,
59965L,59966L,59967L,59968L,59969L,59970L,59971L,59972L,59973L,59974L,
59975L,59976L,59977L,59978L,59979L,59980L,59981L,59982L,59983L,59984L,
59985L,59986L,59987L,59988L,59989L,59990L,59991L,59992L,59993L,59994L,
59995L,59996L,59997L,59998L,59999L,60000L,60001L,60002L,60003L,60004L,
60005L,60006L,60007L,60008L,60009L,60010L,60011L,60012L,60013L,60014L,
60015L,60016L,60017L,60018L,60019L,60020L,60021L,60022L,60023L,60024L,
60025L,60026L,60027L,60028L,60029L,60030L,60031L,60032L,60033L,60034L,
60035L,60036L,60037L,60038L,60039L,60040L,60041L,60042L,60043L,60044L,
60045L,60046L,60047L,60048L,60049L,60050L,60051L,60052L,60053L,60054L,
60055L,60056L,60057L,60058L,60059L,60060L,60061L,60062L,60063L,60064L,
60065L,60066L,60067L,60068L,60069L,60070L,60071L,60072L,60073L,60074L,
60075L,60076L,60077L,60078L,60079L,60080L,60081L,60082L,60083L,60084L,
60085L,60086L,60087L,60088L,60089L,60090L,60091L,60092L,60093L,60094L,
60095L,60096L,60097L,60098L,60099L,60100L,60101L,60102L,60103L,60104L,
60105L,60106L,60107L,60108L,60109L,60110L,60111L,60112L,60113L,60114L,
60115L,60116L,60117L,60118L,60119L,60120L,60121L,60122L,60123L,60124L,
60125L,60126L,60127L,60128L,60129L,60130L,60131L,60132L,60133L,60134L,
60135L,60136L,60137L,60138L,60139L,60140L,60141L,60142L,60143L,60144L,
60145L,60146L,60147L,60148L,60149L,60150L,60151L,60152L,60153L,60154L,
60155L,60156L,60157L,60158L,60159L,60160L,60161L,60162L,60163L,60164L,
60165L,60166L,60167L,60168L,60169L,60170L,60171L,60172L,60173L,60174L,
60175L,60176L,60177L,60178L,60179L,60180L,60181L,60182L,60183L,60184L,
60185L,60186L,60187L,60188L,60189L,60190L,60191L,60192L,60193L,60194L,
60195L,60196L,60197L,60198L,60199L,60200L,60201L,60202L,60203L,60204L,
60205L,60206L,60207L,60208L,60209L,60210L,60211L,60212L,60213L,60214L,
60215L,60216L,60217L,60218L,60219L,60220L,60221L,60222L,60223L,60224L,
60225L,60226L,60227L,60228L,60229L,60230L,60231L,60232L,60233L,60234L,
60235L,60236L,60237L,60238L,60239L,60240L,60241L,60242L,60243L,60244L,
60245L,60246L,60247L,60248L,60249L,60250L,60251L,60252L,60253L,60254L,
60255L,60256L,60257L,60258L,60259L,60260L,60261L,60262L,60263L,60264L,
60265L,60266L,60267L,60268L,60269L,60270L,60271L,60272L,60273L,60274L,
60275L,60276L,60277L,60278L,60279L,60280L,60281L,60282L,60283L,60284L,
60285L,60286L,60287L,60288L,60289L,60290L,60291L,60292L,60293L,60294L,
60295L,60296L,60297L,60298L,60299L,60300L,60301L,60302L,60303L,60304L,
60305L,60306L,60307L,60308L,60309L,60310L,60311L,60312L,60313L,60314L,
60315L,60316L,60317L,60318L,60319L,60320L,60321L,60322L,60323L,60324L,
60325L,60326L,60327L,60328L,60329L,60330L,60331L,60332L,60333L,60334L,
60335L,60336L,60337L,60338L,60339L,60340L,60341L,60342L,60343L,60344L,
60345L,60346L,60347L,60348L,60349L,60350L,60351L,60352L,60353L,60354L,
60355L,60356L,60357L,60358L,60359L,60360L,60361L,60362L,60363L,60364L,
60365L,60366L,60367L,60368L,60369L,60370L,60371L,60372L,60373L,60374L,
60375L,60376L,60377L,60378L,60379L,60380L,60381L,60382L,60383L,60384L,
60385L,60386L,60387L,60388L,60389L,60390L,60391L,60392L,60393L,60394L,
60395L,60396L,60397L,60398L,60399L,60400L,60401L,60402L,60403L,60404L,
60405L,60406L,60407L,60408L,60409L,60410L,60411L,60412L,60413L,60414L,
60415L,60416L,60417L,60418L,60419L,60420L,60421L,60422L,60423L,60424L,
60425L,60426L,60427L,60428L,60429L,60430L,60431L,60432L,60433L,60434L,
60435L,60436L,60437L,60438L,60439L,60440L,60441L,60442L,60443L,60444L,
60445L,60446L,60447L,60448L,60449L,60450L,60451L,60452L,60453L,60454L,
60455L,60456L,60457L,60458L,60459L,60460L,60461L,60462L,60463L,60464L,
60465L,60466L,60467L,60468L,60469L,60470L,60471L,60472L,60473L,60474L,
60475L,60476L,60477L,60478L,60479L,60480L,60481L,60482L,60483L,60484L,
60485L,60486L,60487L,60488L,60489L,60490L,60491L,60492L,60493L,60494L,
60495L,60496L,60497L,60498L,60499L,60500L,60501L,60502L,60503L,60504L,
60505L,60506L,60507L,60508L,60509L,60510L,60511L,60512L,60513L,60514L,
60515L,60516L,60517L,60518L,60519L,60520L,60521L,60522L,60523L,60524L,
60525L,60526L,60527L,60528L,60529L,60530L,60531L,60532L,60533L,60534L,
60535L,60536L,60537L,60538L,60539L,60540L,60541L,60542L,60543L,60544L,
60545L,60546L,60547L,60548L,60549L,60550L,60551L,60552L,60553L,60554L,
60555L,60556L,60557L,60558L,60559L,60560L,60561L,60562L,60563L,60564L,
60565L,60566L,60567L,60568L,60569L,60570L,60571L,60572L,60573L,60574L,
60575L,60576L,60577L,60578L,60579L,60580L,60581L,60582L,60583L,60584L,
60585L,60586L,60587L,60588L,60589L,60590L,60591L,60592L,60593L,60594L,
60595L,60596L,60597L,60598L,60599L,60600L,60601L,60602L,60603L,60604L,
60605L,60606L,60607L,60608L,60609L,60610L,60611L,60612L,60613L,60614L,
60615L,60616L,60617L,60618L,60619L,60620L,60621L,60622L,60623L,60624L,
60625L,60626L,60627L,60628L,60629L,60630L,60631L,60632L,60633L,60634L,
60635L,60636L,60637L,60638L,60639L,60640L,60641L,60642L,60643L,60644L,
60645L,60646L,60647L,60648L,60649L,60650L,60651L,60652L,60653L,60654L,
60655L,60656L,60657L,60658L,60659L,60660L,60661L,60662L,60663L,60664L,
60665L,60666L,60667L,60668L,60669L,60670L,60671L,60672L,60673L,60674L,
60675L,60676L,60677L,60678L,60679L,60680L,60681L,60682L,60683L,60684L,
60685L,60686L,60687L,60688L,60689L,60690L,60691L,60692L,60693L,60694L,
60695L,60696L,60697L,60698L,60699L,60700L,60701L,60702L,60703L,60704L,
60705L,60706L,60707L,60708L,60709L,60710L,60711L,60712L,60713L,60714L,
60715L,60716L,60717L,60718L,60719L,60720L,60721L,60722L,60723L,60724L,
60725L,60726L,60727L,60728L,60729L,60730L,60731L,60732L,60733L,60734L,
60735L,60736L,60737L,60738L,60739L,60740L,60741L,60742L,60743L,60744L,
60745L,60746L,60747L,60748L,60749L,60750L,60751L,60752L,60753L,60754L,
60755L,60756L,60757L,60758L,60759L,60760L,60761L,60762L,60763L,60764L,
60765L,60766L,60767L,60768L,60769L,60770L,60771L,60772L,60773L,60774L,
60775L,60776L,60777L,60778L,60779L,60780L,60781L,60782L,60783L,60784L,
60785L,60786L,60787L,60788L,60789L,60790L,60791L,60792L,60793L,60794L,
60795L,60796L,60797L,60798L,60799L,60800L,60801L,60802L,60803L,60804L,
60805L,60806L,60807L,60808L,60809L,60810L,60811L,60812L,60813L,60814L,
60815L,60816L,60817L,60818L,60819L,60820L,60821L,60822L,60823L,60824L,
60825L,60826L,60827L,60828L,60829L,60830L,60831L,60832L,60833L,60834L,
60835L,60836L,60837L,60838L,60839L,60840L,60841L,60842L,60843L,60844L,
60845L,60846L,60847L,60848L,60849L,60850L,60851L,60852L,60853L,60854L,
60855L,60856L,60857L,60858L,60859L,60860L,60861L,60862L,60863L,60864L,
60865L,60866L,60867L,60868L,60869L,60870L,60871L,60872L,60873L,60874L,
60875L,60876L,60877L,60878L,60879L,60880L,60881L,60882L,60883L,60884L,
60885L,60886L,60887L,60888L,60889L,60890L,60891L,60892L,60893L,60894L,
60895L,60896L,60897L,60898L,60899L,60900L,60901L,60902L,60903L,60904L,
60905L,60906L,60907L,60908L,60909L,60910L,60911L,60912L,60913L,60914L,
60915L,60916L,60917L,60918L,60919L,60920L,60921L,60922L,60923L,60924L,
60925L,60926L,60927L,60928L,60929L,60930L,60931L,60932L,60933L,60934L,
60935L,60936L,60937L,60938L,60939L,60940L,60941L,60942L,60943L,60944L,
60945L,60946L,60947L,60948L,60949L,60950L,60951L,60952L,60953L,60954L,
60955L,60956L,60957L,60958L,60959L,60960L,60961L,60962L,60963L,60964L,
60965L,60966L,60967L,60968L,60969L,60970L,60971L,60972L,60973L,60974L,
60975L,60976L,60977L,60978L,60979L,60980L,60981L,60982L,60983L,60984L,
60985L,60986L,60987L,60988L,60989L,60990L,60991L,60992L,60993L,60994L,
60995L,60996L,60997L,60998L,60999L,61000L,61001L,61002L,61003L,61004L,
61005L,61006L,61007L,61008L,61009L,61010L,61011L,61012L,61013L,61014L,
61015L,61016L,61017L,61018L,61019L,61020L,61021L,61022L,61023L,61024L,
61025L,61026L,61027L,61028L,61029L,61030L,61031L,61032L,61033L,61034L,
61035L,61036L,61037L,61038L,61039L,61040L,61041L,61042L,61043L,61044L,
61045L,61046L,61047L,61048L,61049L,61050L,61051L,61052L,61053L,61054L,
61055L,61056L,61057L,61058L,61059L,61060L,61061L,61062L,61063L,61064L,
61065L,61066L,61067L,61068L,61069L,61070L,61071L,61072L,61073L,61074L,
61075L,61076L,61077L,61078L,61079L,61080L,61081L,61082L,61083L,61084L,
61085L,61086L,61087L,61088L,61089L,61090L,61091L,61092L,61093L,61094L,
61095L,61096L,61097L,61098L,61099L,61100L,61101L,61102L,61103L,61104L,
61105L,61106L,61107L,61108L,61109L,61110L,61111L,61112L,61113L,61114L,
61115L,61116L,61117L,61118L,61119L,61120L,61121L,61122L,61123L,61124L,
61125L,61126L,61127L,61128L,61129L,61130L,61131L,61132L,61133L,61134L,
61135L,61136L,61137L,61138L,61139L,61140L,61141L,61142L,61143L,61144L,
61145L,61146L,61147L,61148L,61149L,61150L,61151L,61152L,61153L,61154L,
61155L,61156L,61157L,61158L,61159L,61160L,61161L,61162L,61163L,61164L,
61165L,61166L,61167L,61168L,61169L,61170L,61171L,61172L,61173L,61174L,
61175L,61176L,61177L,61178L,61179L,61180L,61181L,61182L,61183L,61184L,
61185L,61186L,61187L,61188L,61189L,61190L,61191L,61192L,61193L,61194L,
61195L,61196L,61197L,61198L,61199L,61200L,61201L,61202L,61203L,61204L,
61205L,61206L,61207L,61208L,61209L,61210L,61211L,61212L,61213L,61214L,
61215L,61216L,61217L,61218L,61219L,61220L,61221L,61222L,61223L,61224L,
61225L,61226L,61227L,61228L,61229L,61230L,61231L,61232L,61233L,61234L,
61235L,61236L,61237L,61238L,61239L,61240L,61241L,61242L,61243L,61244L,
61245L,61246L,61247L,61248L,61249L,61250L,61251L,61252L,61253L,61254L,
61255L,61256L,61257L,61258L,61259L,61260L,61261L,61262L,61263L,61264L,
61265L,61266L,61267L,61268L,61269L,61270L,61271L,61272L,61273L,61274L,
61275L,61276L,61277L,61278L,61279L,61280L,61281L,61282L,61283L,61284L,
61285L,61286L,61287L,61288L,61289L,61290L,61291L,61292L,61293L,61294L,
61295L,61296L,61297L,61298L,61299L,61300L,61301L,61302L,61303L,61304L,
61305L,61306L,61307L,61308L,61309L,61310L,61311L,61312L,61313L,61314L,
61315L,61316L,61317L,61318L,61319L,61320L,61321L,61322L,61323L,61324L,
61325L,61326L,61327L,61328L,61329L,61330L,61331L,61332L,61333L,61334L,
61335L,61336L,61337L,61338L,61339L,61340L,61341L,61342L,61343L,61344L,
61345L,61346L,61347L,61348L,61349L,61350L,61351L,61352L,61353L,61354L,
61355L,61356L,61357L,61358L,61359L,61360L,61361L,61362L,61363L,61364L,
61365L,61366L,61367L,61368L,61369L,61370L,61371L,61372L,61373L,61374L,
61375L,61376L,61377L,61378L,61379L,61380L,61381L,61382L,61383L,61384L,
61385L,61386L,61387L,61388L,61389L,61390L,61391L,61392L,61393L,61394L,
61395L,61396L,61397L,61398L,61399L,61400L,61401L,61402L,61403L,61404L,
61405L,61406L,61407L,61408L,61409L,61410L,61411L,61412L,61413L,61414L,
61415L,61416L,61417L,61418L,61419L,61420L,61421L,61422L,61423L,61424L,
61425L,61426L,61427L,61428L,61429L,61430L,61431L,61432L,61433L,61434L,
61435L,61436L,61437L,61438L,61439L,61440L,61441L,61442L,61443L,61444L,
61445L,61446L,61447L,61448L,61449L,61450L,61451L,61452L,61453L,61454L,
61455L,61456L,61457L,61458L,61459L,61460L,61461L,61462L,61463L,61464L,
61465L,61466L,61467L,61468L,61469L,61470L,61471L,61472L,61473L,61474L,
61475L,61476L,61477L,61478L,61479L,61480L,61481L,61482L,61483L,61484L,
61485L,61486L,61487L,61488L,61489L,61490L,61491L,61492L,61493L,61494L,
61495L,61496L,61497L,61498L,61499L,61500L,61501L,61502L,61503L,61504L,
61505L,61506L,61507L,61508L,61509L,61510L,61511L,61512L,61513L,61514L,
61515L,61516L,61517L,61518L,61519L,61520L,61521L,61522L,61523L,61524L,
61525L,61526L,61527L,61528L,61529L,61530L,61531L,61532L,61533L,61534L,
61535L,61536L,61537L,61538L,61539L,61540L,61541L,61542L,61543L,61544L,
61545L,61546L,61547L,61548L,61549L,61550L,61551L,61552L,61553L,61554L,
61555L,61556L,61557L,61558L,61559L,61560L,61561L,61562L,61563L,61564L,
61565L,61566L,61567L,61568L,61569L,61570L,61571L,61572L,61573L,61574L,
61575L,61576L,61577L,61578L,61579L,61580L,61581L,61582L,61583L,61584L,
61585L,61586L,61587L,61588L,61589L,61590L,61591L,61592L,61593L,61594L,
61595L,61596L,61597L,61598L,61599L,61600L,61601L,61602L,61603L,61604L,
61605L,61606L,61607L,61608L,61609L,61610L,61611L,61612L,61613L,61614L,
61615L,61616L,61617L,61618L,61619L,61620L,61621L,61622L,61623L,61624L,
61625L,61626L,61627L,61628L,61629L,61630L,61631L,61632L,61633L,61634L,
61635L,61636L,61637L,61638L,61639L,61640L,61641L,61642L,61643L,61644L,
61645L,61646L,61647L,61648L,61649L,61650L,61651L,61652L,61653L,61654L,
61655L,61656L,61657L,61658L,61659L,61660L,61661L,61662L,61663L,61664L,
61665L,61666L,61667L,61668L,61669L,61670L,61671L,61672L,61673L,61674L,
61675L,61676L,61677L,61678L,61679L,61680L,61681L,61682L,61683L,61684L,
61685L,61686L,61687L,61688L,61689L,61690L,61691L,61692L,61693L,61694L,
61695L,61696L,61697L,61698L,61699L,61700L,61701L,61702L,61703L,61704L,
61705L,61706L,61707L,61708L,61709L,61710L,61711L,61712L,61713L,61714L,
61715L,61716L,61717L,61718L,61719L,61720L,61721L,61722L,61723L,61724L,
61725L,61726L,61727L,61728L,61729L,61730L,61731L,61732L,61733L,61734L,
61735L,61736L,61737L,61738L,61739L,61740L,61741L,61742L,61743L,61744L,
61745L,61746L,61747L,61748L,61749L,61750L,61751L,61752L,61753L,61754L,
61755L,61756L,61757L,61758L,61759L,61760L,61761L,61762L,61763L,61764L,
61765L,61766L,61767L,61768L,61769L,61770L,61771L,61772L,61773L,61774L,
61775L,61776L,61777L,61778L,61779L,61780L,61781L,61782L,61783L,61784L,
61785L,61786L,61787L,61788L,61789L,61790L,61791L,61792L,61793L,61794L,
61795L,61796L,61797L,61798L,61799L,61800L,61801L,61802L,61803L,61804L,
61805L,61806L,61807L,61808L,61809L,61810L,61811L,61812L,61813L,61814L,
61815L,61816L,61817L,61818L,61819L,61820L,61821L,61822L,61823L,61824L,
61825L,61826L,61827L,61828L,61829L,61830L,61831L,61832L,61833L,61834L,
61835L,61836L,61837L,61838L,61839L,61840L,61841L,61842L,61843L,61844L,
61845L,61846L,61847L,61848L,61849L,61850L,61851L,61852L,61853L,61854L,
61855L,61856L,61857L,61858L,61859L,61860L,61861L,61862L,61863L,61864L,
61865L,61866L,61867L,61868L,61869L,61870L,61871L,61872L,61873L,61874L,
61875L,61876L,61877L,61878L,61879L,61880L,61881L,61882L,61883L,61884L,
61885L,61886L,61887L,61888L,61889L,61890L,61891L,61892L,61893L,61894L,
61895L,61896L,61897L,61898L,61899L,61900L,61901L,61902L,61903L,61904L,
61905L,61906L,61907L,61908L,61909L,61910L,61911L,61912L,61913L,61914L,
61915L,61916L,61917L,61918L,61919L,61920L,61921L,61922L,61923L,61924L,
61925L,61926L,61927L,61928L,61929L,61930L,61931L,61932L,61933L,61934L,
61935L,61936L,61937L,61938L,61939L,61940L,61941L,61942L,61943L,61944L,
61945L,61946L,61947L,61948L,61949L,61950L,61951L,61952L,61953L,61954L,
61955L,61956L,61957L,61958L,61959L,61960L,61961L,61962L,61963L,61964L,
61965L,61966L,61967L,61968L,61969L,61970L,61971L,61972L,61973L,61974L,
61975L,61976L,61977L,61978L,61979L,61980L,61981L,61982L,61983L,61984L,
61985L,61986L,61987L,61988L,61989L,61990L,61991L,61992L,61993L,61994L,
61995L,61996L,61997L,61998L,61999L,62000L,62001L,62002L,62003L,62004L,
62005L,62006L,62007L,62008L,62009L,62010L,62011L,62012L,62013L,62014L,
62015L,62016L,62017L,62018L,62019L,62020L,62021L,62022L,62023L,62024L,
62025L,62026L,62027L,62028L,62029L,62030L,62031L,62032L,62033L,62034L,
62035L,62036L,62037L,62038L,62039L,62040L,62041L,62042L,62043L,62044L,
62045L,62046L,62047L,62048L,62049L,62050L,62051L,62052L,62053L,62054L,
62055L,62056L,62057L,62058L,62059L,62060L,62061L,62062L,62063L,62064L,
62065L,62066L,62067L,62068L,62069L,62070L,62071L,62072L,62073L,62074L,
62075L,62076L,62077L,62078L,62079L,62080L,62081L,62082L,62083L,62084L,
62085L,62086L,62087L,62088L,62089L,62090L,62091L,62092L,62093L,62094L,
62095L,62096L,62097L,62098L,62099L,62100L,62101L,62102L,62103L,62104L,
62105L,62106L,62107L,62108L,62109L,62110L,62111L,62112L,62113L,62114L,
62115L,62116L,62117L,62118L,62119L,62120L,62121L,62122L,62123L,62124L,
62125L,62126L,62127L,62128L,62129L,62130L,62131L,62132L,62133L,62134L,
62135L,62136L,62137L,62138L,62139L,62140L,62141L,62142L,62143L,62144L,
62145L,62146L,62147L,62148L,62149L,62150L,62151L,62152L,62153L,62154L,
62155L,62156L,62157L,62158L,62159L,62160L,62161L,62162L,62163L,62164L,
62165L,62166L,62167L,62168L,62169L,62170L,62171L,62172L,62173L,62174L,
62175L,62176L,62177L,62178L,62179L,62180L,62181L,62182L,62183L,62184L,
62185L,62186L,62187L,62188L,62189L,62190L,62191L,62192L,62193L,62194L,
62195L,62196L,62197L,62198L,62199L,62200L,62201L,62202L,62203L,62204L,
62205L,62206L,62207L,62208L,62209L,62210L,62211L,62212L,62213L,62214L,
62215L,62216L,62217L,62218L,62219L,62220L,62221L,62222L,62223L,62224L,
62225L,62226L,62227L,62228L,62229L,62230L,62231L,62232L,62233L,62234L,
62235L,62236L,62237L,62238L,62239L,62240L,62241L,62242L,62243L,62244L,
62245L,62246L,62247L,62248L,62249L,62250L,62251L,62252L,62253L,62254L,
62255L,62256L,62257L,62258L,62259L,62260L,62261L,62262L,62263L,62264L,
62265L,62266L,62267L,62268L,62269L,62270L,62271L,62272L,62273L,62274L,
62275L,62276L,62277L,62278L,62279L,62280L,62281L,62282L,62283L,62284L,
62285L,62286L,62287L,62288L,62289L,62290L,62291L,62292L,62293L,62294L,
62295L,62296L,62297L,62298L,62299L,62300L,62301L,62302L,62303L,62304L,
62305L,62306L,62307L,62308L,62309L,62310L,62311L,62312L,62313L,62314L,
62315L,62316L,62317L,62318L,62319L,62320L,62321L,62322L,62323L,62324L,
62325L,62326L,62327L,62328L,62329L,62330L,62331L,62332L,62333L,62334L,
62335L,62336L,62337L,62338L,62339L,62340L,62341L,62342L,62343L,62344L,
62345L,62346L,62347L,62348L,62349L,62350L,62351L,62352L,62353L,62354L,
62355L,62356L,62357L,62358L,62359L,62360L,62361L,62362L,62363L,62364L,
62365L,62366L,62367L,62368L,62369L,62370L,62371L,62372L,62373L,62374L,
62375L,62376L,62377L,62378L,62379L,62380L,62381L,62382L,62383L,62384L,
62385L,62386L,62387L,62388L,62389L,62390L,62391L,62392L,62393L,62394L,
62395L,62396L,62397L,62398L,62399L,62400L,62401L,62402L,62403L,62404L,
62405L,62406L,62407L,62408L,62409L,62410L,62411L,62412L,62413L,62414L,
62415L,62416L,62417L,62418L,62419L,62420L,62421L,62422L,62423L,62424L,
62425L,62426L,62427L,62428L,62429L,62430L,62431L,62432L,62433L,62434L,
62435L,62436L,62437L,62438L,62439L,62440L,62441L,62442L,62443L,62444L,
62445L,62446L,62447L,62448L,62449L,62450L,62451L,62452L,62453L,62454L,
62455L,62456L,62457L,62458L,62459L,62460L,62461L,62462L,62463L,62464L,
62465L,62466L,62467L,62468L,62469L,62470L,62471L,62472L,62473L,62474L,
62475L,62476L,62477L,62478L,62479L,62480L,62481L,62482L,62483L,62484L,
62485L,62486L,62487L,62488L,62489L,62490L,62491L,62492L,62493L,62494L,
62495L,62496L,62497L,62498L,62499L,62500L,62501L,62502L,62503L,62504L,
62505L,62506L,62507L,62508L,62509L,62510L,62511L,62512L,62513L,62514L,
62515L,62516L,62517L,62518L,62519L,62520L,62521L,62522L,62523L,62524L,
62525L,62526L,62527L,62528L,62529L,62530L,62531L,62532L,62533L,62534L,
62535L,62536L,62537L,62538L,62539L,62540L,62541L,62542L,62543L,62544L,
62545L,62546L,62547L,62548L,62549L,62550L,62551L,62552L,62553L,62554L,
62555L,62556L,62557L,62558L,62559L,62560L,62561L,62562L,62563L,62564L,
62565L,62566L,62567L,62568L,62569L,62570L,62571L,62572L,62573L,62574L,
62575L,62576L,62577L,62578L,62579L,62580L,62581L,62582L,62583L,62584L,
62585L,62586L,62587L,62588L,62589L,62590L,62591L,62592L,62593L,62594L,
62595L,62596L,62597L,62598L,62599L,62600L,62601L,62602L,62603L,62604L,
62605L,62606L,62607L,62608L,62609L,62610L,62611L,62612L,62613L,62614L,
62615L,62616L,62617L,62618L,62619L,62620L,62621L,62622L,62623L,62624L,
62625L,62626L,62627L,62628L,62629L,62630L,62631L,62632L,62633L,62634L,
62635L,62636L,62637L,62638L,62639L,62640L,62641L,62642L,62643L,62644L,
62645L,62646L,62647L,62648L,62649L,62650L,62651L,62652L,62653L,62654L,
62655L,62656L,62657L,62658L,62659L,62660L,62661L,62662L,62663L,62664L,
62665L,62666L,62667L,62668L,62669L,62670L,62671L,62672L,62673L,62674L,
62675L,62676L,62677L,62678L,62679L,62680L,62681L,62682L,62683L,62684L,
62685L,62686L,62687L,62688L,62689L,62690L,62691L,62692L,62693L,62694L,
62695L,62696L,62697L,62698L,62699L,62700L,62701L,62702L,62703L,62704L,
62705L,62706L,62707L,62708L,62709L,62710L,62711L,62712L,62713L,62714L,
62715L,62716L,62717L,62718L,62719L,62720L,62721L,62722L,62723L,62724L,
62725L,62726L,62727L,62728L,62729L,62730L,62731L,62732L,62733L,62734L,
62735L,62736L,62737L,62738L,62739L,62740L,62741L,62742L,62743L,62744L,
62745L,62746L,62747L,62748L,62749L,62750L,62751L,62752L,62753L,62754L,
62755L,62756L,62757L,62758L,62759L,62760L,62761L,62762L,62763L,62764L,
62765L,62766L,62767L,62768L,62769L,62770L,62771L,62772L,62773L,62774L,
62775L,62776L,62777L,62778L,62779L,62780L,62781L,62782L,62783L,62784L,
62785L,62786L,62787L,62788L,62789L,62790L,62791L,62792L,62793L,62794L,
62795L,62796L,62797L,62798L,62799L,62800L,62801L,62802L,62803L,62804L,
62805L,62806L,62807L,62808L,62809L,62810L,62811L,62812L,62813L,62814L,
62815L,62816L,62817L,62818L,62819L,62820L,62821L,62822L,62823L,62824L,
62825L,62826L,62827L,62828L,62829L,62830L,62831L,62832L,62833L,62834L,
62835L,62836L,62837L,62838L,62839L,62840L,62841L,62842L,62843L,62844L,
62845L,62846L,62847L,62848L,62849L,62850L,62851L,62852L,62853L,62854L,
62855L,62856L,62857L,62858L,62859L,62860L,62861L,62862L,62863L,62864L,
62865L,62866L,62867L,62868L,62869L,62870L,62871L,62872L,62873L,62874L,
62875L,62876L,62877L,62878L,62879L,62880L,62881L,62882L,62883L,62884L,
62885L,62886L,62887L,62888L,62889L,62890L,62891L,62892L,62893L,62894L,
62895L,62896L,62897L,62898L,62899L,62900L,62901L,62902L,62903L,62904L,
62905L,62906L,62907L,62908L,62909L,62910L,62911L,62912L,62913L,62914L,
62915L,62916L,62917L,62918L,62919L,62920L,62921L,62922L,62923L,62924L,
62925L,62926L,62927L,62928L,62929L,62930L,62931L,62932L,62933L,62934L,
62935L,62936L,62937L,62938L,62939L,62940L,62941L,62942L,62943L,62944L,
62945L,62946L,62947L,62948L,62949L,62950L,62951L,62952L,62953L,62954L,
62955L,62956L,62957L,62958L,62959L,62960L,62961L,62962L,62963L,62964L,
62965L,62966L,62967L,62968L,62969L,62970L,62971L,62972L,62973L,62974L,
62975L,62976L,62977L,62978L,62979L,62980L,62981L,62982L,62983L,62984L,
62985L,62986L,62987L,62988L,62989L,62990L,62991L,62992L,62993L,62994L,
62995L,62996L,62997L,62998L,62999L,63000L,63001L,63002L,63003L,63004L,
63005L,63006L,63007L,63008L,63009L,63010L,63011L,63012L,63013L,63014L,
63015L,63016L,63017L,63018L,63019L,63020L,63021L,63022L,63023L,63024L,
63025L,63026L,63027L,63028L,63029L,63030L,63031L,63032L,63033L,63034L,
63035L,63036L,63037L,63038L,63039L,63040L,63041L,63042L,63043L,63044L,
63045L,63046L,63047L,63048L,63049L,63050L,63051L,63052L,63053L,63054L,
63055L,63056L,63057L,63058L,63059L,63060L,63061L,63062L,63063L,63064L,
63065L,63066L,63067L,63068L,63069L,63070L,63071L,63072L,63073L,63074L,
63075L,63076L,63077L,63078L,63079L,63080L,63081L,63082L,63083L,63084L,
63085L,63086L,63087L,63088L,63089L,63090L,63091L,63092L,63093L,63094L,
63095L,63096L,63097L,63098L,63099L,63100L,63101L,63102L,63103L,63104L,
63105L,63106L,63107L,63108L,63109L,63110L,63111L,63112L,63113L,63114L,
63115L,63116L,63117L,63118L,63119L,63120L,63121L,63122L,63123L,63124L,
63125L,63126L,63127L,63128L,63129L,63130L,63131L,63132L,63133L,63134L,
63135L,63136L,63137L,63138L,63139L,63140L,63141L,63142L,63143L,63144L,
63145L,63146L,63147L,63148L,63149L,63150L,63151L,63152L,63153L,63154L,
63155L,63156L,63157L,63158L,63159L,63160L,63161L,63162L,63163L,63164L,
63165L,63166L,63167L,63168L,63169L,63170L,63171L,63172L,63173L,63174L,
63175L,63176L,63177L,63178L,63179L,63180L,63181L,63182L,63183L,63184L,
63185L,63186L,63187L,63188L,63189L,63190L,63191L,63192L,63193L,63194L,
63195L,63196L,63197L,63198L,63199L,63200L,63201L,63202L,63203L,63204L,
63205L,63206L,63207L,63208L,63209L,63210L,63211L,63212L,63213L,63214L,
63215L,63216L,63217L,63218L,63219L,63220L,63221L,63222L,63223L,63224L,
63225L,63226L,63227L,63228L,63229L,63230L,63231L,63232L,63233L,63234L,
63235L,63236L,63237L,63238L,63239L,63240L,63241L,63242L,63243L,63244L,
63245L,63246L,63247L,63248L,63249L,63250L,63251L,63252L,63253L,63254L,
63255L,63256L,63257L,63258L,63259L,63260L,63261L,63262L,63263L,63264L,
63265L,63266L,63267L,63268L,63269L,63270L,63271L,63272L,63273L,63274L,
63275L,63276L,63277L,63278L,63279L,63280L,63281L,63282L,63283L,63284L,
63285L,63286L,63287L,63288L,63289L,63290L,63291L,63292L,63293L,63294L,
63295L,63296L,63297L,63298L,63299L,63300L,63301L,63302L,63303L,63304L,
63305L,63306L,63307L,63308L,63309L,63310L,63311L,63312L,63313L,63314L,
63315L,63316L,63317L,63318L,63319L,63320L,63321L,63322L,63323L,63324L,
63325L,63326L,63327L,63328L,63329L,63330L,63331L,63332L,63333L,63334L,
63335L,63336L,63337L,63338L,63339L,63340L,63341L,63342L,63343L,63344L,
63345L,63346L,63347L,63348L,63349L,63350L,63351L,63352L,63353L,63354L,
63355L,63356L,63357L,63358L,63359L,63360L,63361L,63362L,63363L,63364L,
63365L,63366L,63367L,63368L,63369L,63370L,63371L,63372L,63373L,63374L,
63375L,63376L,63377L,63378L,63379L,63380L,63381L,63382L,63383L,63384L,
63385L,63386L,63387L,63388L,63389L,63390L,63391L,63392L,63393L,63394L,
63395L,63396L,63397L,63398L,63399L,63400L,63401L,63402L,63403L,63404L,
63405L,63406L,63407L,63408L,63409L,63410L,63411L,63412L,63413L,63414L,
63415L,63416L,63417L,63418L,63419L,63420L,63421L,63422L,63423L,63424L,
63425L,63426L,63427L,63428L,63429L,63430L,63431L,63432L,63433L,63434L,
63435L,63436L,63437L,63438L,63439L,63440L,63441L,63442L,63443L,63444L,
63445L,63446L,63447L,63448L,63449L,63450L,63451L,63452L,63453L,63454L,
63455L,63456L,63457L,63458L,63459L,63460L,63461L,63462L,63463L,63464L,
63465L,63466L,63467L,63468L,63469L,63470L,63471L,63472L,63473L,63474L,
63475L,63476L,63477L,63478L,63479L,63480L,63481L,63482L,63483L,63484L,
63485L,63486L,63487L,63488L,63489L,63490L,63491L,63492L,63493L,63494L,
63495L,63496L,63497L,63498L,63499L,63500L,63501L,63502L,63503L,63504L,
63505L,63506L,63507L,63508L,63509L,63510L,63511L,63512L,63513L,63514L,
63515L,63516L,63517L,63518L,63519L,63520L,63521L,63522L,63523L,63524L,
63525L,63526L,63527L,63528L,63529L,63530L,63531L,63532L,63533L,63534L,
63535L,63536L,63537L,63538L,63539L,63540L,63541L,63542L,63543L,63544L,
63545L,63546L,63547L,63548L,63549L,63550L,63551L,63552L,63553L,63554L,
63555L,63556L,63557L,63558L,63559L,63560L,63561L,63562L,63563L,63564L,
63565L,63566L,63567L,63568L,63569L,63570L,63571L,63572L,63573L,63574L,
63575L,63576L,63577L,63578L,63579L,63580L,63581L,63582L,63583L,63584L,
63585L,63586L,63587L,63588L,63589L,63590L,63591L,63592L,63593L,63594L,
63595L,63596L,63597L,63598L,63599L,63600L,63601L,63602L,63603L,63604L,
63605L,63606L,63607L,63608L,63609L,63610L,63611L,63612L,63613L,63614L,
63615L,63616L,63617L,63618L,63619L,63620L,63621L,63622L,63623L,63624L,
63625L,63626L,63627L,63628L,63629L,63630L,63631L,63632L,63633L,63634L,
63635L,63636L,63637L,63638L,63639L,63640L,63641L,63642L,63643L,63644L,
63645L,63646L,63647L,63648L,63649L,63650L,63651L,63652L,63653L,63654L,
63655L,63656L,63657L,63658L,63659L,63660L,63661L,63662L,63663L,63664L,
63665L,63666L,63667L,63668L,63669L,63670L,63671L,63672L,63673L,63674L,
63675L,63676L,63677L,63678L,63679L,63680L,63681L,63682L,63683L,63684L,
63685L,63686L,63687L,63688L,63689L,63690L,63691L,63692L,63693L,63694L,
63695L,63696L,63697L,63698L,63699L,63700L,63701L,63702L,63703L,63704L,
63705L,63706L,63707L,63708L,63709L,63710L,63711L,63712L,63713L,63714L,
63715L,63716L,63717L,63718L,63719L,63720L,63721L,63722L,63723L,63724L,
63725L,63726L,63727L,63728L,63729L,63730L,63731L,63732L,63733L,63734L,
63735L,63736L,63737L,63738L,63739L,63740L,63741L,63742L,63743L,63744L,
63745L,63746L,63747L,63748L,63749L,63750L,63751L,63752L,63753L,63754L,
63755L,63756L,63757L,63758L,63759L,63760L,63761L,63762L,63763L,63764L,
63765L,63766L,63767L,63768L,63769L,63770L,63771L,63772L,63773L,63774L,
63775L,63776L,63777L,63778L,63779L,63780L,63781L,63782L,63783L,63784L,
63785L,63786L,63787L,63788L,63789L,63790L,63791L,63792L,63793L,63794L,
63795L,63796L,63797L,63798L,63799L,63800L,63801L,63802L,63803L,63804L,
63805L,63806L,63807L,63808L,63809L,63810L,63811L,63812L,63813L,63814L,
63815L,63816L,63817L,63818L,63819L,63820L,63821L,63822L,63823L,63824L,
63825L,63826L,63827L,63828L,63829L,63830L,63831L,63832L,63833L,63834L,
63835L,63836L,63837L,63838L,63839L,63840L,63841L,63842L,63843L,63844L,
63845L,63846L,63847L,63848L,63849L,63850L,63851L,63852L,63853L,63854L,
63855L,63856L,63857L,63858L,63859L,63860L,63861L,63862L,63863L,63864L,
63865L,63866L,63867L,63868L,63869L,63870L,63871L,63872L,63873L,63874L,
63875L,63876L,63877L,63878L,63879L,63880L,63881L,63882L,63883L,63884L,
63885L,63886L,63887L,63888L,63889L,63890L,63891L,63892L,63893L,63894L,
63895L,63896L,63897L,63898L,63899L,63900L,63901L,63902L,63903L,63904L,
63905L,63906L,63907L,63908L,63909L,63910L,63911L,63912L,63913L,63914L,
63915L,63916L,63917L,63918L,63919L,63920L,63921L,63922L,63923L,63924L,
63925L,63926L,63927L,63928L,63929L,63930L,63931L,63932L,63933L,63934L,
63935L,63936L,63937L,63938L,63939L,63940L,63941L,63942L,63943L,63944L,
63945L,63946L,63947L,63948L,63949L,63950L,63951L,63952L,63953L,63954L,
63955L,63956L,63957L,63958L,63959L,63960L,63961L,63962L,63963L,63964L,
63965L,63966L,63967L,63968L,63969L,63970L,63971L,63972L,63973L,63974L,
63975L,63976L,63977L,63978L,63979L,63980L,63981L,63982L,63983L,63984L,
63985L,63986L,63987L,63988L,63989L,63990L,63991L,63992L,63993L,63994L,
63995L,63996L,63997L,63998L,63999L,64000L,64001L,64002L,64003L,64004L,
64005L,64006L,64007L,64008L,64009L,64010L,64011L,64012L,64013L,64014L,
64015L,64016L,64017L,64018L,64019L,64020L,64021L,64022L,64023L,64024L,
64025L,64026L,64027L,64028L,64029L,64030L,64031L,64032L,64033L,64034L,
64035L,64036L,64037L,64038L,64039L,64040L,64041L,64042L,64043L,64044L,
64045L,64046L,64047L,64048L,64049L,64050L,64051L,64052L,64053L,64054L,
64055L,64056L,64057L,64058L,64059L,64060L,64061L,64062L,64063L,64064L,
64065L,64066L,64067L,64068L,64069L,64070L,64071L,64072L,64073L,64074L,
64075L,64076L,64077L,64078L,64079L,64080L,64081L,64082L,64083L,64084L,
64085L,64086L,64087L,64088L,64089L,64090L,64091L,64092L,64093L,64094L,
64095L,64096L,64097L,64098L,64099L,64100L,64101L,64102L,64103L,64104L,
64105L,64106L,64107L,64108L,64109L,64110L,64111L,64112L,64113L,64114L,
64115L,64116L,64117L,64118L,64119L,64120L,64121L,64122L,64123L,64124L,
64125L,64126L,64127L,64128L,64129L,64130L,64131L,64132L,64133L,64134L,
64135L,64136L,64137L,64138L,64139L,64140L,64141L,64142L,64143L,64144L,
64145L,64146L,64147L,64148L,64149L,64150L,64151L,64152L,64153L,64154L,
64155L,64156L,64157L,64158L,64159L,64160L,64161L,64162L,64163L,64164L,
64165L,64166L,64167L,64168L,64169L,64170L,64171L,64172L,64173L,64174L,
64175L,64176L,64177L,64178L,64179L,64180L,64181L,64182L,64183L,64184L,
64185L,64186L,64187L,64188L,64189L,64190L,64191L,64192L,64193L,64194L,
64195L,64196L,64197L,64198L,64199L,64200L,64201L,64202L,64203L,64204L,
64205L,64206L,64207L,64208L,64209L,64210L,64211L,64212L,64213L,64214L,
64215L,64216L,64217L,64218L,64219L,64220L,64221L,64222L,64223L,64224L,
64225L,64226L,64227L,64228L,64229L,64230L,64231L,64232L,64233L,64234L,
64235L,64236L,64237L,64238L,64239L,64240L,64241L,64242L,64243L,64244L,
64245L,64246L,64247L,64248L,64249L,64250L,64251L,64252L,64253L,64254L,
64255L,64256L,64257L,64258L,64259L,64260L,64261L,64262L,64263L,64264L,
64265L,64266L,64267L,64268L,64269L,64270L,64271L,64272L,64273L,64274L,
64275L,64276L,64277L,64278L,64279L,64280L,64281L,64282L,64283L,64284L,
64285L,64286L,64287L,64288L,64289L,64290L,64291L,64292L,64293L,64294L,
64295L,64296L,64297L,64298L,64299L,64300L,64301L,64302L,64303L,64304L,
64305L,64306L,64307L,64308L,64309L,64310L,64311L,64312L,64313L,64314L,
64315L,64316L,64317L,64318L,64319L,64320L,64321L,64322L,64323L,64324L,
64325L,64326L,64327L,64328L,64329L,64330L,64331L,64332L,64333L,64334L,
64335L,64336L,64337L,64338L,64339L,64340L,64341L,64342L,64343L,64344L,
64345L,64346L,64347L,64348L,64349L,64350L,64351L,64352L,64353L,64354L,
64355L,64356L,64357L,64358L,64359L,64360L,64361L,64362L,64363L,64364L,
64365L,64366L,64367L,64368L,64369L,64370L,64371L,64372L,64373L,64374L,
64375L,64376L,64377L,64378L,64379L,64380L,64381L,64382L,64383L,64384L,
64385L,64386L,64387L,64388L,64389L,64390L,64391L,64392L,64393L,64394L,
64395L,64396L,64397L,64398L,64399L,64400L,64401L,64402L,64403L,64404L,
64405L,64406L,64407L,64408L,64409L,64410L,64411L,64412L,64413L,64414L,
64415L,64416L,64417L,64418L,64419L,64420L,64421L,64422L,64423L,64424L,
64425L,64426L,64427L,64428L,64429L,64430L,64431L,64432L,64433L,64434L,
64435L,64436L,64437L,64438L,64439L,64440L,64441L,64442L,64443L,64444L,
64445L,64446L,64447L,64448L,64449L,64450L,64451L,64452L,64453L,64454L,
64455L,64456L,64457L,64458L,64459L,64460L,64461L,64462L,64463L,64464L,
64465L,64466L,64467L,64468L,64469L,64470L,64471L,64472L,64473L,64474L,
64475L,64476L,64477L,64478L,64479L,64480L,64481L,64482L,64483L,64484L,
64485L,64486L,64487L,64488L,64489L,64490L,64491L,64492L,64493L,64494L,
64495L,64496L,64497L,64498L,64499L,64500L,64501L,64502L,64503L,64504L,
64505L,64506L,64507L,64508L,64509L,64510L,64511L,64512L,64513L,64514L,
64515L,64516L,64517L,64518L,64519L,64520L,64521L,64522L,64523L,64524L,
64525L,64526L,64527L,64528L,64529L,64530L,64531L,64532L,64533L,64534L,
64535L,64536L,64537L,64538L,64539L,64540L,64541L,64542L,64543L,64544L,
64545L,64546L,64547L,64548L,64549L,64550L,64551L,64552L,64553L,64554L,
64555L,64556L,64557L,64558L,64559L,64560L,64561L,64562L,64563L,64564L,
64565L,64566L,64567L,64568L,64569L,64570L,64571L,64572L,64573L,64574L,
64575L,64576L,64577L,64578L,64579L,64580L,64581L,64582L,64583L,64584L,
64585L,64586L,64587L,64588L,64589L,64590L,64591L,64592L,64593L,64594L,
64595L,64596L,64597L,64598L,64599L,64600L,64601L,64602L,64603L,64604L,
64605L,64606L,64607L,64608L,64609L,64610L,64611L,64612L,64613L,64614L,
64615L,64616L,64617L,64618L,64619L,64620L,64621L,64622L,64623L,64624L,
64625L,64626L,64627L,64628L,64629L,64630L,64631L,64632L,64633L,64634L,
64635L,64636L,64637L,64638L,64639L,64640L,64641L,64642L,64643L,64644L,
64645L,64646L,64647L,64648L,64649L,64650L,64651L,64652L,64653L,64654L,
64655L,64656L,64657L,64658L,64659L,64660L,64661L,64662L,64663L,64664L,
64665L,64666L,64667L,64668L,64669L,64670L,64671L,64672L,64673L,64674L,
64675L,64676L,64677L,64678L,64679L,64680L,64681L,64682L,64683L,64684L,
64685L,64686L,64687L,64688L,64689L,64690L,64691L,64692L,64693L,64694L,
64695L,64696L,64697L,64698L,64699L,64700L,64701L,64702L,64703L,64704L,
64705L,64706L,64707L,64708L,64709L,64710L,64711L,64712L,64713L,64714L,
64715L,64716L,64717L,64718L,64719L,64720L,64721L,64722L,64723L,64724L,
64725L,64726L,64727L,64728L,64729L,64730L,64731L,64732L,64733L,64734L,
64735L,64736L,64737L,64738L,64739L,64740L,64741L,64742L,64743L,64744L,
64745L,64746L,64747L,64748L,64749L,64750L,64751L,64752L,64753L,64754L,
64755L,64756L,64757L,64758L,64759L,64760L,64761L,64762L,64763L,64764L,
64765L,64766L,64767L,64768L,64769L,64770L,64771L,64772L,64773L,64774L,
64775L,64776L,64777L,64778L,64779L,64780L,64781L,64782L,64783L,64784L,
64785L,64786L,64787L,64788L,64789L,64790L,64791L,64792L,64793L,64794L,
64795L,64796L,64797L,64798L,64799L,64800L,64801L,64802L,64803L,64804L,
64805L,64806L,64807L,64808L,64809L,64810L,64811L,64812L,64813L,64814L,
64815L,64816L,64817L,64818L,64819L,64820L,64821L,64822L,64823L,64824L,
64825L,64826L,64827L,64828L,64829L,64830L,64831L,64832L,64833L,64834L,
64835L,64836L,64837L,64838L,64839L,64840L,64841L,64842L,64843L,64844L,
64845L,64846L,64847L,64848L,64849L,64850L,64851L,64852L,64853L,64854L,
64855L,64856L,64857L,64858L,64859L,64860L,64861L,64862L,64863L,64864L,
64865L,64866L,64867L,64868L,64869L,64870L,64871L,64872L,64873L,64874L,
64875L,64876L,64877L,64878L,64879L,64880L,64881L,64882L,64883L,64884L,
64885L,64886L,64887L,64888L,64889L,64890L,64891L,64892L,64893L,64894L,
64895L,64896L,64897L,64898L,64899L,64900L,64901L,64902L,64903L,64904L,
64905L,64906L,64907L,64908L,64909L,64910L,64911L,64912L,64913L,64914L,
64915L,64916L,64917L,64918L,64919L,64920L,64921L,64922L,64923L,64924L,
64925L,64926L,64927L,64928L,64929L,64930L,64931L,64932L,64933L,64934L,
64935L,64936L,64937L,64938L,64939L,64940L,64941L,64942L,64943L,64944L,
64945L,64946L,64947L,64948L,64949L,64950L,64951L,64952L,64953L,64954L,
64955L,64956L,64957L,64958L,64959L,64960L,64961L,64962L,64963L,64964L,
64965L,64966L,64967L,64968L,64969L,64970L,64971L,64972L,64973L,64974L,
64975L,64976L,64977L,64978L,64979L,64980L,64981L,64982L,64983L,64984L,
64985L,64986L,64987L,64988L,64989L,64990L,64991L,64992L,64993L,64994L,
64995L,64996L,64997L,64998L,64999L,65000L,65001L,65002L,65003L,65004L,
65005L,65006L,65007L,65008L,65009L,65010L,65011L,65012L,65013L,65014L,
65015L,65016L,65017L,65018L,65019L,65020L,65021L,65022L,65023L,65024L,
65025L,65026L,65027L,65028L,65029L,65030L,65031L,65032L,65033L,65034L,
65035L,65036L,65037L,65038L,65039L,65040L,65041L,65042L,65043L,65044L,
65045L,65046L,65047L,65048L,65049L,65050L,65051L,65052L,65053L,65054L,
65055L,65056L,65057L,65058L,65059L,65060L,65061L,65062L,65063L,65064L,
65065L,65066L,65067L,65068L,65069L,65070L,65071L,65072L,65073L,65074L,
65075L,65076L,65077L,65078L,65079L,65080L,65081L,65082L,65083L,65084L,
65085L,65086L,65087L,65088L,65089L,65090L,65091L,65092L,65093L,65094L,
65095L,65096L,65097L,65098L,65099L,65100L,65101L,65102L,65103L,65104L,
65105L,65106L,65107L,65108L,65109L,65110L,65111L,65112L,65113L,65114L,
65115L,65116L,65117L,65118L,65119L,65120L,65121L,65122L,65123L,65124L,
65125L,65126L,65127L,65128L,65129L,65130L,65131L,65132L,65133L,65134L,
65135L,65136L,65137L,65138L,65139L,65140L,65141L,65142L,65143L,65144L,
65145L,65146L,65147L,65148L,65149L,65150L,65151L,65152L,65153L,65154L,
65155L,65156L,65157L,65158L,65159L,65160L,65161L,65162L,65163L,65164L,
65165L,65166L,65167L,65168L,65169L,65170L,65171L,65172L,65173L,65174L,
65175L,65176L,65177L,65178L,65179L,65180L,65181L,65182L,65183L,65184L,
65185L,65186L,65187L,65188L,65189L,65190L,65191L,65192L,65193L,65194L,
65195L,65196L,65197L,65198L,65199L,65200L,65201L,65202L,65203L,65204L,
65205L,65206L,65207L,65208L,65209L,65210L,65211L,65212L,65213L,65214L,
65215L,65216L,65217L,65218L,65219L,65220L,65221L,65222L,65223L,65224L,
65225L,65226L,65227L,65228L,65229L,65230L,65231L,65232L,65233L,65234L,
65235L,65236L,65237L,65238L,65239L,65240L,65241L,65242L,65243L,65244L,
65245L,65246L,65247L,65248L,65249L,65250L,65251L,65252L,65253L,65254L,
65255L,65256L,65257L,65258L,65259L,65260L,65261L,65262L,65263L,65264L,
65265L,65266L,65267L,65268L,65269L,65270L,65271L,65272L,65273L,65274L,
65275L,65276L,65277L,65278L,65279L,65280L,65281L,65282L,65283L,65284L,
65285L,65286L,65287L,65288L,65289L,65290L,65291L,65292L,65293L,65294L,
65295L,65296L,65297L,65298L,65299L,65300L,65301L,65302L,65303L,65304L,
65305L,65306L,65307L,65308L,65309L,65310L,65311L,65312L,65313L,65314L,
65315L,65316L,65317L,65318L,65319L,65320L,65321L,65322L,65323L,65324L,
65325L,65326L,65327L,65328L,65329L,65330L,65331L,65332L,65333L,65334L,
65335L,65336L,65337L,65338L,65339L,65340L,65341L,65342L,65343L,65344L,
65313L,65314L,65315L,65316L,65317L,65318L,65319L,65320L,65321L,65322L,
65323L,65324L,65325L,65326L,65327L,65328L,65329L,65330L,65331L,65332L,
65333L,65334L,65335L,65336L,65337L,65338L,65371L,65372L,65373L,65374L,
65375L,65376L,65377L,65378L,65379L,65380L,65381L,65382L,65383L,65384L,
65385L,65386L,65387L,65388L,65389L,65390L,65391L,65392L,65393L,65394L,
65395L,65396L,65397L,65398L,65399L,65400L,65401L,65402L,65403L,65404L,
65405L,65406L,65407L,65408L,65409L,65410L,65411L,65412L,65413L,65414L,
65415L,65416L,65417L,65418L,65419L,65420L,65421L,65422L,65423L,65424L,
65425L,65426L,65427L,65428L,65429L,65430L,65431L,65432L,65433L,65434L,
65435L,65436L,65437L,65438L,65439L,65440L,65441L,65442L,65443L,65444L,
65445L,65446L,65447L,65448L,65449L,65450L,65451L,65452L,65453L,65454L,
65455L,65456L,65457L,65458L,65459L,65460L,65461L,65462L,65463L,65464L,
65465L,65466L,65467L,65468L,65469L,65470L,65471L,65472L,65473L,65474L,
65475L,65476L,65477L,65478L,65479L,65480L,65481L,65482L,65483L,65484L,
65485L,65486L,65487L,65488L,65489L,65490L,65491L,65492L,65493L,65494L,
65495L,65496L,65497L,65498L,65499L,65500L,65501L,65502L,65503L,65504L,
65505L,65506L,65507L,65508L,65509L,65510L,65511L,65512L,65513L,65514L,
65515L,65516L,65517L,65518L,65519L,65520L,65521L,65522L,65523L,65524L,
65525L,65526L,65527L,65528L,65529L,65530L,65531L,65532L,65533L,65534L,
65535L,
};
#endif

#if defined(DUK_USE_REGEXP_CANON_BITMAP)
/*
 *  Automatically generated by extract_caseconv.py, do not edit!
 */

const duk_uint8_t duk_unicode_re_canon_bitmap[256] = {
23,0,224,19,1,228,255,255,255,255,255,255,255,255,255,255,63,254,255,127,
255,255,255,255,255,255,255,255,231,231,0,16,255,227,255,255,63,255,255,
255,255,255,255,255,1,252,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
227,129,255,255,255,147,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,251,
};
#endif
//#line 1 "duk_util_bitdecoder.c"
/*
 *  Bitstream decoder.
 */

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

/* Decode 'bits' bits from the input stream (bits must be 1...24).
 * When reading past bitstream end, zeroes are shifted in.  The result
 * is signed to match duk_bd_decode_flagged.
 */
DUK_INTERNAL duk_uint32_t duk_bd_decode(duk_bitdecoder_ctx *ctx, duk_small_int_t bits) {
    duk_small_int_t shift;
    duk_uint32_t mask;
    duk_uint32_t tmp;

    /* Note: cannot read more than 24 bits without possibly shifting top bits out.
     * Fixable, but adds complexity.
     */
    DUK_ASSERT(bits >= 1 && bits <= 24);

    while (ctx->currbits < bits) {
#if 0
        DUK_DDD(DUK_DDDPRINT("decode_bits: shift more data (bits=%ld, currbits=%ld)",
                             (long) bits, (long) ctx->currbits));
#endif
        ctx->currval <<= 8;
        if (ctx->offset < ctx->length) {
            /* If ctx->offset >= ctx->length, we "shift zeroes in"
             * instead of croaking.
             */
            ctx->currval |= ctx->data[ctx->offset++];
        }
        ctx->currbits += 8;
    }
#if 0
    DUK_DDD(DUK_DDDPRINT("decode_bits: bits=%ld, currbits=%ld, currval=0x%08lx",
                         (long) bits, (long) ctx->currbits, (unsigned long) ctx->currval));
#endif

    /* Extract 'top' bits of currval; note that the extracted bits do not need
     * to be cleared, we just ignore them on next round.
     */
    shift = ctx->currbits - bits;
    mask = (((duk_uint32_t) 1U) << bits) - 1U;
    tmp = (ctx->currval >> shift) & mask;
    ctx->currbits = shift;  /* remaining */

#if 0
    DUK_DDD(DUK_DDDPRINT("decode_bits: %ld bits -> 0x%08lx (%ld), currbits=%ld, currval=0x%08lx",
                         (long) bits, (unsigned long) tmp, (long) tmp, (long) ctx->currbits, (unsigned long) ctx->currval));
#endif

    return tmp;
}

DUK_INTERNAL duk_small_uint_t duk_bd_decode_flag(duk_bitdecoder_ctx *ctx) {
    return (duk_small_uint_t) duk_bd_decode(ctx, 1);
}

/* Decode a one-bit flag, and if set, decode a value of 'bits', otherwise return
 * default value.
 */
DUK_INTERNAL duk_uint32_t duk_bd_decode_flagged(duk_bitdecoder_ctx *ctx, duk_small_int_t bits, duk_uint32_t def_value) {
    if (duk_bd_decode_flag(ctx)) {
        return duk_bd_decode(ctx, bits);
    } else {
        return def_value;
    }
}

/* Signed variant, allows negative marker value. */
DUK_INTERNAL duk_int32_t duk_bd_decode_flagged_signed(duk_bitdecoder_ctx *ctx, duk_small_int_t bits, duk_int32_t def_value) {
    return (duk_int32_t) duk_bd_decode_flagged(ctx, bits, (duk_uint32_t) def_value);
}

/* Shared varint encoding.  Match dukutil.py BitEncode.varuint(). */
DUK_INTERNAL duk_uint32_t duk_bd_decode_varuint(duk_bitdecoder_ctx *ctx) {
    duk_small_uint_t t;

    /* The bit encoding choices here are based on manual testing against
     * the actual varuints generated by genbuiltins.py.
     */
    switch (duk_bd_decode(ctx, 2)) {
    case 0:
        return 0;  /* [0,0] */
    case 1:
        return duk_bd_decode(ctx, 2) + 1;  /* [1,4] */
    case 2:
        return duk_bd_decode(ctx, 5) + 5;  /* [5,36] */
    default:
        t = duk_bd_decode(ctx, 7);
        if (t == 0) {
            return duk_bd_decode(ctx, 20);
        }
        return (t - 1) + 37;  /* [37,163] */
    }
}

/* Decode a bit packed string from a custom format used by genbuiltins.py.
 * This function is here because it's used for both heap and thread inits.
 * Caller must supply the output buffer whose size is NOT checked!
 */

#define DUK__BITPACK_LETTER_LIMIT  26
#define DUK__BITPACK_LOOKUP1       26
#define DUK__BITPACK_LOOKUP2       27
#define DUK__BITPACK_SWITCH1       28
#define DUK__BITPACK_SWITCH        29
#define DUK__BITPACK_UNUSED1       30
#define DUK__BITPACK_EIGHTBIT      31

DUK_LOCAL const duk_uint8_t duk__bitpacked_lookup[16] = {
    DUK_ASC_0, DUK_ASC_1, DUK_ASC_2, DUK_ASC_3,
    DUK_ASC_4, DUK_ASC_5, DUK_ASC_6, DUK_ASC_7,
    DUK_ASC_8, DUK_ASC_9, DUK_ASC_UNDERSCORE, DUK_ASC_SPACE,
    0x82, 0x80, DUK_ASC_DOUBLEQUOTE, DUK_ASC_LCURLY
};

DUK_INTERNAL duk_small_uint_t duk_bd_decode_bitpacked_string(duk_bitdecoder_ctx *bd, duk_uint8_t *out) {
    duk_small_uint_t len;
    duk_small_uint_t mode;
    duk_small_uint_t t;
    duk_small_uint_t i;

    len = duk_bd_decode(bd, 5);
    if (len == 31) {
        len = duk_bd_decode(bd, 8);  /* Support up to 256 bytes; rare. */
    }

    mode = 32;  /* 0 = uppercase, 32 = lowercase (= 'a' - 'A') */
    for (i = 0; i < len; i++) {
        t = duk_bd_decode(bd, 5);
        if (t < DUK__BITPACK_LETTER_LIMIT) {
            t = t + DUK_ASC_UC_A + mode;
        } else if (t == DUK__BITPACK_LOOKUP1) {
            t = duk__bitpacked_lookup[duk_bd_decode(bd, 3)];
        } else if (t == DUK__BITPACK_LOOKUP2) {
            t = duk__bitpacked_lookup[8 + duk_bd_decode(bd, 3)];
        } else if (t == DUK__BITPACK_SWITCH1) {
            t = duk_bd_decode(bd, 5);
            DUK_ASSERT_DISABLE(t >= 0);  /* unsigned */
            DUK_ASSERT(t <= 25);
            t = t + DUK_ASC_UC_A + (mode ^ 32);
        } else if (t == DUK__BITPACK_SWITCH) {
            mode = mode ^ 32;
            t = duk_bd_decode(bd, 5);
            DUK_ASSERT_DISABLE(t >= 0);
            DUK_ASSERT(t <= 25);
            t = t + DUK_ASC_UC_A + mode;
        } else if (t == DUK__BITPACK_EIGHTBIT) {
            t = duk_bd_decode(bd, 8);
        }
        out[i] = (duk_uint8_t) t;
    }

    return len;
}

/* automatic undefs */
#undef DUK__BITPACK_EIGHTBIT
#undef DUK__BITPACK_LETTER_LIMIT
#undef DUK__BITPACK_LOOKUP1
#undef DUK__BITPACK_LOOKUP2
#undef DUK__BITPACK_SWITCH
#undef DUK__BITPACK_SWITCH1
#undef DUK__BITPACK_UNUSED1
//#line 1 "duk_util_bitencoder.c"
/*
 *  Bitstream encoder.
 */

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

DUK_INTERNAL void duk_be_encode(duk_bitencoder_ctx *ctx, duk_uint32_t data, duk_small_int_t bits) {
    duk_uint8_t tmp;

    DUK_ASSERT(ctx != NULL);
    DUK_ASSERT(ctx->currbits < 8);

    /* This limitation would be fixable but adds unnecessary complexity. */
    DUK_ASSERT(bits >= 1 && bits <= 24);

    ctx->currval = (ctx->currval << bits) | data;
    ctx->currbits += bits;

    while (ctx->currbits >= 8) {
        if (ctx->offset < ctx->length) {
            tmp = (duk_uint8_t) ((ctx->currval >> (ctx->currbits - 8)) & 0xff);
            ctx->data[ctx->offset++] = tmp;
        } else {
            /* If buffer has been exhausted, truncate bitstream */
            ctx->truncated = 1;
        }

        ctx->currbits -= 8;
    }
}

DUK_INTERNAL void duk_be_finish(duk_bitencoder_ctx *ctx) {
    duk_small_int_t npad;

    DUK_ASSERT(ctx != NULL);
    DUK_ASSERT(ctx->currbits < 8);

    npad = (duk_small_int_t) (8 - ctx->currbits);
    if (npad > 0) {
        duk_be_encode(ctx, 0, npad);
    }
    DUK_ASSERT(ctx->currbits == 0);
}
//#line 1 "duk_util_bufwriter.c"
/*
 *  Fast buffer writer with slack management.
 */

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

/* XXX: Avoid duk_{memcmp,memmove}_unsafe() by imposing a minimum length of
 * >0 for the underlying dynamic buffer.
 */

/*
 *  Macro support functions (use only macros in calling code)
 */

DUK_LOCAL void duk__bw_update_ptrs(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_size_t curr_offset, duk_size_t new_length) {
    duk_uint8_t *p;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(bw_ctx != NULL);
    DUK_UNREF(thr);

    /* 'p' might be NULL when the underlying buffer is zero size.  If so,
     * the resulting pointers are not used unsafely.
     */
    p = (duk_uint8_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, bw_ctx->buf);
    DUK_ASSERT(p != NULL || (DUK_HBUFFER_DYNAMIC_GET_SIZE(bw_ctx->buf) == 0 && curr_offset == 0 && new_length == 0));
    bw_ctx->p = p + curr_offset;
    bw_ctx->p_base = p;
    bw_ctx->p_limit = p + new_length;
}

DUK_INTERNAL void duk_bw_init(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_hbuffer_dynamic *h_buf) {
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(bw_ctx != NULL);
    DUK_ASSERT(h_buf != NULL);

    bw_ctx->buf = h_buf;
    duk__bw_update_ptrs(thr, bw_ctx, 0, DUK_HBUFFER_DYNAMIC_GET_SIZE(h_buf));
}

DUK_INTERNAL void duk_bw_init_pushbuf(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_size_t buf_size) {
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(bw_ctx != NULL);

    (void) duk_push_dynamic_buffer(thr, buf_size);
    bw_ctx->buf = (duk_hbuffer_dynamic *) duk_known_hbuffer(thr, -1);
    DUK_ASSERT(bw_ctx->buf != NULL);
    duk__bw_update_ptrs(thr, bw_ctx, 0, buf_size);
}

/* Resize target buffer for requested size.  Called by the macro only when the
 * fast path test (= there is space) fails.
 */
DUK_INTERNAL duk_uint8_t *duk_bw_resize(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_size_t sz) {
    duk_size_t curr_off;
    duk_size_t add_sz;
    duk_size_t new_sz;

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

    /* We could do this operation without caller updating bw_ctx->ptr,
     * but by writing it back here we can share code better.
     */

    curr_off = (duk_size_t) (bw_ctx->p - bw_ctx->p_base);
    add_sz = (curr_off >> DUK_BW_SLACK_SHIFT) + DUK_BW_SLACK_ADD;
    new_sz = curr_off + sz + add_sz;
    if (DUK_UNLIKELY(new_sz < curr_off)) {
        /* overflow */
        DUK_ERROR_RANGE(thr, DUK_STR_BUFFER_TOO_LONG);
        DUK_WO_NORETURN(return NULL;);
    }
#if 0  /* for manual torture testing: tight allocation, useful with valgrind */
    new_sz = curr_off + sz;
#endif

    /* This is important to ensure dynamic buffer data pointer is not
     * NULL (which is possible if buffer size is zero), which in turn
     * causes portability issues with e.g. memmove() and memcpy().
     */
    DUK_ASSERT(new_sz >= 1);

    DUK_DD(DUK_DDPRINT("resize bufferwriter from %ld to %ld (add_sz=%ld)", (long) curr_off, (long) new_sz, (long) add_sz));

    duk_hbuffer_resize(thr, bw_ctx->buf, new_sz);
    duk__bw_update_ptrs(thr, bw_ctx, curr_off, new_sz);
    return bw_ctx->p;
}

/* Make buffer compact, matching current written size. */
DUK_INTERNAL void duk_bw_compact(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx) {
    duk_size_t len;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(bw_ctx != NULL);
    DUK_UNREF(thr);

    len = (duk_size_t) (bw_ctx->p - bw_ctx->p_base);
    duk_hbuffer_resize(thr, bw_ctx->buf, len);
    duk__bw_update_ptrs(thr, bw_ctx, len, len);
}

DUK_INTERNAL void duk_bw_write_raw_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t src_off, duk_size_t len) {
    duk_uint8_t *p_base;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(bw != NULL);
    DUK_ASSERT(src_off <= DUK_BW_GET_SIZE(thr, bw));
    DUK_ASSERT(len <= DUK_BW_GET_SIZE(thr, bw));
    DUK_ASSERT(src_off + len <= DUK_BW_GET_SIZE(thr, bw));
    DUK_UNREF(thr);

    p_base = bw->p_base;
    duk_memcpy_unsafe((void *) bw->p,
                      (const void *) (p_base + src_off),
                      (size_t) len);
    bw->p += len;
}

DUK_INTERNAL void duk_bw_write_ensure_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t src_off, duk_size_t len) {
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(bw != NULL);
    DUK_ASSERT(src_off <= DUK_BW_GET_SIZE(thr, bw));
    DUK_ASSERT(len <= DUK_BW_GET_SIZE(thr, bw));
    DUK_ASSERT(src_off + len <= DUK_BW_GET_SIZE(thr, bw));

    DUK_BW_ENSURE(thr, bw, len);
    duk_bw_write_raw_slice(thr, bw, src_off, len);
}

DUK_INTERNAL void duk_bw_insert_raw_bytes(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t dst_off, const duk_uint8_t *buf, duk_size_t len) {
    duk_uint8_t *p_base;
    duk_size_t buf_sz, move_sz;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(bw != NULL);
    DUK_ASSERT(dst_off <= DUK_BW_GET_SIZE(thr, bw));
    DUK_ASSERT(buf != NULL);
    DUK_UNREF(thr);

    p_base = bw->p_base;
    buf_sz = (duk_size_t) (bw->p - p_base);  /* constrained by maximum buffer size */
    move_sz = buf_sz - dst_off;

    DUK_ASSERT(p_base != NULL);  /* buffer size is >= 1 */
    duk_memmove_unsafe((void *) (p_base + dst_off + len),
                       (const void *) (p_base + dst_off),
                       (size_t) move_sz);
    duk_memcpy_unsafe((void *) (p_base + dst_off),
                      (const void *) buf,
                      (size_t) len);
    bw->p += len;
}

DUK_INTERNAL void duk_bw_insert_ensure_bytes(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t dst_off, const duk_uint8_t *buf, duk_size_t len) {
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(bw != NULL);
    DUK_ASSERT(dst_off <= DUK_BW_GET_SIZE(thr, bw));
    DUK_ASSERT(buf != NULL);

    DUK_BW_ENSURE(thr, bw, len);
    duk_bw_insert_raw_bytes(thr, bw, dst_off, buf, len);
}

DUK_INTERNAL void duk_bw_insert_raw_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t dst_off, duk_size_t src_off, duk_size_t len) {
    duk_uint8_t *p_base;
    duk_size_t buf_sz, move_sz;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(bw != NULL);
    DUK_ASSERT(dst_off <= DUK_BW_GET_SIZE(thr, bw));
    DUK_ASSERT(src_off <= DUK_BW_GET_SIZE(thr, bw));
    DUK_ASSERT(len <= DUK_BW_GET_SIZE(thr, bw));
    DUK_ASSERT(src_off + len <= DUK_BW_GET_SIZE(thr, bw));
    DUK_UNREF(thr);

    p_base = bw->p_base;

    /* Don't support "straddled" source now. */
    DUK_ASSERT(dst_off <= src_off || dst_off >= src_off + len);

    if (dst_off <= src_off) {
        /* Target is before source.  Source offset is expressed as
         * a "before change" offset.  Account for the memmove.
         */
        src_off += len;
    }

    buf_sz = (duk_size_t) (bw->p - p_base);
    move_sz = buf_sz - dst_off;

    DUK_ASSERT(p_base != NULL);  /* buffer size is >= 1 */
    duk_memmove_unsafe((void *) (p_base + dst_off + len),
                       (const void *) (p_base + dst_off),
                       (size_t) move_sz);
    duk_memcpy_unsafe((void *) (p_base + dst_off),
                      (const void *) (p_base + src_off),
                      (size_t) len);
    bw->p += len;
}

DUK_INTERNAL void duk_bw_insert_ensure_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t dst_off, duk_size_t src_off, duk_size_t len) {
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(bw != NULL);
    DUK_ASSERT(dst_off <= DUK_BW_GET_SIZE(thr, bw));
    DUK_ASSERT(src_off <= DUK_BW_GET_SIZE(thr, bw));
    DUK_ASSERT(len <= DUK_BW_GET_SIZE(thr, bw));
    DUK_ASSERT(src_off + len <= DUK_BW_GET_SIZE(thr, bw));

    /* Don't support "straddled" source now. */
    DUK_ASSERT(dst_off <= src_off || dst_off >= src_off + len);

    DUK_BW_ENSURE(thr, bw, len);
    duk_bw_insert_raw_slice(thr, bw, dst_off, src_off, len);
}

DUK_INTERNAL duk_uint8_t *duk_bw_insert_raw_area(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t off, duk_size_t len) {
    duk_uint8_t *p_base, *p_dst, *p_src;
    duk_size_t buf_sz, move_sz;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(bw != NULL);
    DUK_ASSERT(off <= DUK_BW_GET_SIZE(thr, bw));
    DUK_UNREF(thr);

    p_base = bw->p_base;
    buf_sz = (duk_size_t) (bw->p - p_base);
    move_sz = buf_sz - off;
    p_dst = p_base + off + len;
    p_src = p_base + off;
    duk_memmove_unsafe((void *) p_dst, (const void *) p_src, (size_t) move_sz);
    return p_src;  /* point to start of 'reserved area' */
}

DUK_INTERNAL duk_uint8_t *duk_bw_insert_ensure_area(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t off, duk_size_t len) {
    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(bw != NULL);
    DUK_ASSERT(off <= DUK_BW_GET_SIZE(thr, bw));

    DUK_BW_ENSURE(thr, bw, len);
    return duk_bw_insert_raw_area(thr, bw, off, len);
}

DUK_INTERNAL void duk_bw_remove_raw_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t off, duk_size_t len) {
    duk_size_t move_sz;

    duk_uint8_t *p_base;
    duk_uint8_t *p_src;
    duk_uint8_t *p_dst;

    DUK_ASSERT(thr != NULL);
    DUK_ASSERT(bw != NULL);
    DUK_ASSERT(off <= DUK_BW_GET_SIZE(thr, bw));
    DUK_ASSERT(len <= DUK_BW_GET_SIZE(thr, bw));
    DUK_ASSERT(off + len <= DUK_BW_GET_SIZE(thr, bw));
    DUK_UNREF(thr);

    p_base = bw->p_base;
    p_dst = p_base + off;
    p_src = p_dst + len;
    move_sz = (duk_size_t) (bw->p - p_src);
    duk_memmove_unsafe((void *) p_dst,
                       (const void *) p_src,
                       (size_t) move_sz);
    bw->p -= len;
}

/*
 *  Assertion helpers
 */

#if defined(DUK_USE_ASSERTIONS)
DUK_INTERNAL void duk_bw_assert_valid(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx) {
    DUK_UNREF(thr);
    DUK_ASSERT(bw_ctx != NULL);
    DUK_ASSERT(bw_ctx->buf != NULL);
    DUK_ASSERT((DUK_HBUFFER_DYNAMIC_GET_SIZE(bw_ctx->buf) == 0) ||
               (bw_ctx->p != NULL &&
                bw_ctx->p_base != NULL &&
                bw_ctx->p_limit != NULL &&
                bw_ctx->p_limit >= bw_ctx->p_base &&
                bw_ctx->p >= bw_ctx->p_base &&
                bw_ctx->p <= bw_ctx->p_limit));
}
#endif
//#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_double_equals(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_equals((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_equals((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(duk_double_equals(y, 0.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;
}

/* Double and float byteorder changes. */

DUK_INTERNAL DUK_INLINE void duk_dblunion_host_to_little(duk_double_union *u) {
#if defined(DUK_USE_DOUBLE_LE)
    /* HGFEDCBA -> HGFEDCBA */
    DUK_UNREF(u);
#elif defined(DUK_USE_DOUBLE_ME)
    duk_uint32_t a, b;

    /* DCBAHGFE -> HGFEDCBA */
    a = u->ui[0];
    b = u->ui[1];
    u->ui[0] = b;
    u->ui[1] = a;
#elif defined(DUK_USE_DOUBLE_BE)
    /* ABCDEFGH -> HGFEDCBA */
#if defined(DUK_USE_64BIT_OPS)
    u->ull[0] = DUK_BSWAP64(u->ull[0]);
#else
    duk_uint32_t a, b;

    a = u->ui[0];
    b = u->ui[1];
    u->ui[0] = DUK_BSWAP32(b);
    u->ui[1] = DUK_BSWAP32(a);
#endif
#else
#error internal error
#endif
}

DUK_INTERNAL DUK_INLINE void duk_dblunion_little_to_host(duk_double_union *u) {
    duk_dblunion_host_to_little(u);
}

DUK_INTERNAL DUK_INLINE void duk_dblunion_host_to_big(duk_double_union *u) {
#if defined(DUK_USE_DOUBLE_LE)
    /* HGFEDCBA -> ABCDEFGH */
#if defined(DUK_USE_64BIT_OPS)
    u->ull[0] = DUK_BSWAP64(u->ull[0]);
#else
    duk_uint32_t a, b;

    a = u->ui[0];
    b = u->ui[1];
    u->ui[0] = DUK_BSWAP32(b);
    u->ui[1] = DUK_BSWAP32(a);
#endif
#elif defined(DUK_USE_DOUBLE_ME)
    duk_uint32_t a, b;

    /* DCBAHGFE -> ABCDEFGH */
    a = u->ui[0];
    b = u->ui[1];
    u->ui[0] = DUK_BSWAP32(a);
    u->ui[1] = DUK_BSWAP32(b);
#elif defined(DUK_USE_DOUBLE_BE)
    /* ABCDEFGH -> ABCDEFGH */
    DUK_UNREF(u);
#else
#error internal error
#endif
}

DUK_INTERNAL DUK_INLINE void duk_dblunion_big_to_host(duk_double_union *u) {
    duk_dblunion_host_to_big(u);
}

DUK_INTERNAL DUK_INLINE void duk_fltunion_host_to_big(duk_float_union *u) {
#if defined(DUK_USE_DOUBLE_LE) || defined(DUK_USE_DOUBLE_ME)
    /* DCBA -> ABCD */
    u->ui[0] = DUK_BSWAP32(u->ui[0]);
#elif defined(DUK_USE_DOUBLE_BE)
    /* ABCD -> ABCD */
    DUK_UNREF(u);
#else
#error internal error
#endif
}

DUK_INTERNAL DUK_INLINE void duk_fltunion_big_to_host(duk_float_union *u) {
    duk_fltunion_host_to_big(u);
}

/* Comparison: ensures comparison operates on exactly correct types, avoiding
 * some floating point comparison pitfalls (e.g. atan2() assertions failed on
 * -m32 with direct comparison, even with explicit casts).
 */
#if defined(DUK_USE_GCC_PRAGMAS)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"
#elif defined(DUK_USE_CLANG_PRAGMAS)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wfloat-equal"
#endif

DUK_INTERNAL DUK_ALWAYS_INLINE duk_bool_t duk_double_equals(duk_double_t x, duk_double_t y) {
    return x == y;
}

DUK_INTERNAL DUK_ALWAYS_INLINE duk_bool_t duk_float_equals(duk_float_t x, duk_float_t y) {
    return x == y;
}
#if defined(DUK_USE_GCC_PRAGMAS)
#pragma GCC diagnostic pop
#elif defined(DUK_USE_CLANG_PRAGMAS)
#pragma clang diagnostic pop
#endif
//#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;
    h ^= h >> 15;

    return h;
}
#endif  /* DUK_USE_STRHASH_DENSE */

/* automatic undefs */
#undef DUK__MAGIC_M
#undef DUK__MAGIC_R
//#line 1 "duk_util_memory.c"
/*
 *  Memory utils.
 */

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

#if defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR)
DUK_INTERNAL DUK_INLINE duk_small_int_t duk_memcmp_unsafe(const void *s1, const void *s2, duk_size_t len) {
    DUK_ASSERT(s1 != NULL || len == 0U);
    DUK_ASSERT(s2 != NULL || len == 0U);
    return DUK_MEMCMP(s1, s2, (size_t) len);
}

DUK_INTERNAL DUK_INLINE duk_small_int_t duk_memcmp(const void *s1, const void *s2, duk_size_t len) {
    DUK_ASSERT(s1 != NULL);
    DUK_ASSERT(s2 != NULL);
    return DUK_MEMCMP(s1, s2, (size_t) len);
}
#else  /* DUK_USE_ALLOW_UNDEFINED_BEHAVIOR */
DUK_INTERNAL DUK_INLINE duk_small_int_t duk_memcmp_unsafe(const void *s1, const void *s2, duk_size_t len) {
    DUK_ASSERT(s1 != NULL || len == 0U);
    DUK_ASSERT(s2 != NULL || len == 0U);
    if (DUK_UNLIKELY(len == 0U)) {
        return 0;
    }
    DUK_ASSERT(s1 != NULL);
    DUK_ASSERT(s2 != NULL);
    return duk_memcmp(s1, s2, len);
}

DUK_INTERNAL DUK_INLINE duk_small_int_t duk_memcmp(const void *s1, const void *s2, duk_size_t len) {
    DUK_ASSERT(s1 != NULL);
    DUK_ASSERT(s2 != NULL);
    return DUK_MEMCMP(s1, s2, (size_t) len);
}
#endif  /* DUK_USE_ALLOW_UNDEFINED_BEHAVIOR */
//#line 1 "duk_util_tinyrandom.c"
/*
 *  A tiny random number generator used for Math.random() and other internals.
 *
 *  Default algorithm is xoroshiro128+: http://xoroshiro.di.unimi.it/xoroshiro128plus.c
 *  with SplitMix64 seed preparation: http://xorshift.di.unimi.it/splitmix64.c.
 *
 *  Low memory targets and targets without 64-bit types use a slightly smaller
 *  (but slower) algorithm by Adi Shamir:
 *  http://www.woodmann.com/forum/archive/index.php/t-3100.html.
 *
 */

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

#if !defined(DUK_USE_GET_RANDOM_DOUBLE)

#if defined(DUK_USE_PREFER_SIZE) || !defined(DUK_USE_64BIT_OPS)
#define DUK__RANDOM_SHAMIR3OP
#else
#define DUK__RANDOM_XOROSHIRO128PLUS
#endif

#if defined(DUK__RANDOM_SHAMIR3OP)
#define DUK__UPDATE_RND(rnd) do { \
        (rnd) += ((rnd) * (rnd)) | 0x05UL; \
        (rnd) = ((rnd) & 0xffffffffUL);       /* if duk_uint32_t is exactly 32 bits, this is a NOP */ \
    } while (0)

#define DUK__RND_BIT(rnd)  ((rnd) >> 31)  /* only use the highest bit */

DUK_INTERNAL void duk_util_tinyrandom_prepare_seed(duk_hthread *thr) {
    DUK_UNREF(thr);  /* Nothing now. */
}

DUK_INTERNAL duk_double_t duk_util_tinyrandom_get_double(duk_hthread *thr) {
    duk_double_t t;
    duk_small_int_t n;
    duk_uint32_t rnd;

    rnd = thr->heap->rnd_state;

    n = 53;  /* enough to cover the whole mantissa */
    t = 0.0;

    do {
        DUK__UPDATE_RND(rnd);
        t += DUK__RND_BIT(rnd);
        t /= 2.0;
    } while (--n);

    thr->heap->rnd_state = rnd;

    DUK_ASSERT(t >= (duk_double_t) 0.0);
    DUK_ASSERT(t < (duk_double_t) 1.0);

    return t;
}
#endif  /* DUK__RANDOM_SHAMIR3OP */

#if defined(DUK__RANDOM_XOROSHIRO128PLUS)
DUK_LOCAL DUK_ALWAYS_INLINE duk_uint64_t duk__rnd_splitmix64(duk_uint64_t *x) {
    duk_uint64_t z;
    z = (*x += DUK_U64_CONSTANT(0x9E3779B97F4A7C15));
    z = (z ^ (z >> 30U)) * DUK_U64_CONSTANT(0xBF58476D1CE4E5B9);
    z = (z ^ (z >> 27U)) * DUK_U64_CONSTANT(0x94D049BB133111EB);
    return z ^ (z >> 31U);
}

DUK_LOCAL DUK_ALWAYS_INLINE duk_uint64_t duk__rnd_rotl(const duk_uint64_t x, duk_small_uint_t k) {
    return (x << k) | (x >> (64U - k));
}

DUK_LOCAL DUK_ALWAYS_INLINE duk_uint64_t duk__xoroshiro128plus(duk_uint64_t *s) {
    duk_uint64_t s0;
    duk_uint64_t s1;
    duk_uint64_t res;

    s0 = s[0];
    s1 = s[1];
    res = s0 + s1;
    s1 ^= s0;
    s[0] = duk__rnd_rotl(s0, 55) ^ s1 ^ (s1 << 14U);
    s[1] = duk__rnd_rotl(s1, 36);

    return res;
}

DUK_INTERNAL void duk_util_tinyrandom_prepare_seed(duk_hthread *thr) {
    duk_small_uint_t i;
    duk_uint64_t x;

    /* Mix both halves of the initial seed with SplitMix64.  The intent
     * is to ensure that very similar raw seeds (which is usually the case
     * because current seed is Date.now()) result in different xoroshiro128+
     * seeds.
     */
    x = thr->heap->rnd_state[0];  /* Only [0] is used as input here. */
    for (i = 0; i < 64; i++) {
        thr->heap->rnd_state[i & 0x01] = duk__rnd_splitmix64(&x);  /* Keep last 2 values. */
    }
}

DUK_INTERNAL duk_double_t duk_util_tinyrandom_get_double(duk_hthread *thr) {
    duk_uint64_t v;
    duk_double_union du;

    /* For big and little endian the integer and IEEE double byte order
     * is the same so a direct assignment works.  For mixed endian the
     * 32-bit parts must be swapped.
     */
    v = (DUK_U64_CONSTANT(0x3ff) << 52U) | (duk__xoroshiro128plus((duk_uint64_t *) thr->heap->rnd_state) >> 12U);
    du.ull[0] = v;
#if defined(DUK_USE_DOUBLE_ME)
    do {
        duk_uint32_t tmp;
        tmp = du.ui[0];
        du.ui[0] = du.ui[1];
        du.ui[1] = tmp;
    } while (0);
#endif
    return du.d - 1.0;
}
#endif  /* DUK__RANDOM_XOROSHIRO128PLUS */

#endif  /* !DUK_USE_GET_RANDOM_DOUBLE */

/* automatic undefs */
#undef DUK__RANDOM_SHAMIR3OP
#undef DUK__RANDOM_XOROSHIRO128PLUS
#undef DUK__RND_BIT
#undef DUK__UPDATE_RND

#include "../platform/choc_ReenableAllWarnings.h"

//==============================================================================
struct DuktapeContext  : public Context::Pimpl
{
    DuktapeContext() : context (duktape::duk_create_heap (nullptr, nullptr, nullptr, nullptr, fatalError))
    {
        CHOC_ASSERT (context != nullptr);
    }

    ~DuktapeContext() override
    {
        duk_destroy_heap (context);
    }

    void resetStack()
    {
        while (duk_get_top (context))
            duk_remove (context, duk_get_top_index (context));
    }

    static void fatalError (void*, const char* message)    { throw Error (message); }

    void throwError()
    {
        std::string message = duk_safe_to_string (context, -1);
        resetStack();
        throw Error (message);
    }

    void evalString (std::string_view code)
    {
        if (duk_peval_lstring (context, code.data(), code.length()) != DUK_EXEC_SUCCESS)
            throwError();
    }

    choc::value::Value evaluate (const std::string& code, Context::ReadModuleContentFn*) override
    {
        evalString (code);
        auto result = readValue (context, -1);
        duk_pop (context);
        return result;
    }

    void prepareForCall (std::string_view functionName, uint32_t numArgs) override
    {
        evalString (functionName);

        if (! duk_is_function (context, -1))
            throw Error ("No such function");

        numFunctionArgs = (duktape::duk_idx_t) numArgs;
        duk_require_stack_top (context, numFunctionArgs);
    }

    choc::value::Value performCall() override
    {
        if (duk_pcall (context, numFunctionArgs) != DUK_EXEC_SUCCESS)
            throwError();

        auto returnValue = readValue (context, -1);
        duk_pop (context);
        return returnValue;
    }

    void registerFunction (const std::string& name, Context::NativeFunction fn) override
    {
        duk_push_global_object (context);

        registeredFunctions.push_back (std::make_unique<RegisteredFunction> (std::move (fn), *this));
        registeredFunctions.back()->pushToStack (context);

        duk_put_prop_lstring (context, -2, name.data(), name.length());
        duk_pop (context);
    }

    //==============================================================================
    void pushObjectOrArray (const choc::value::ValueView& v) override { pushValue (v); }
    void pushArg (int32_t v) override                                 { duk_push_number (context, static_cast<double> (v)); }
    void pushArg (int64_t v) override                                 { duk_push_number (context, static_cast<double> (v)); }
    void pushArg (uint32_t v) override                                { duk_push_number (context, static_cast<double> (v)); }
    void pushArg (double v) override                                  { duk_push_number (context, v); }
    void pushArg (bool v) override                                    { duk_push_boolean (context, v); }

    void pushArg (std::string_view v) override
    {
        if (choc::text::isValidCESU8 (v))
            return (void) duk_push_lstring (context, v.data(), v.length());

        auto cesu8 = choc::text::convertUTF8ToCESU8 (choc::text::UTF8Pointer (std::string (v).data()));
        duk_push_lstring (context, cesu8.data(), cesu8.length());
    }

    void pushValue (const choc::value::ValueView& v)
    {
        if (v.isInt() || v.isFloat())   return duk_push_number (context, v.get<double>());
        if (v.isBool())                 return duk_push_boolean (context, v.getBool());
        if (v.isString())               return pushArg (v.getString());

        if (v.isArray() || v.isVector())
        {
            auto arrayIndex = duk_push_array (context);
            duktape::duk_uarridx_t elementIndex = 0;

            for (const auto& element : v)
            {
                pushValue (element);
                duk_put_prop_index (context, arrayIndex, elementIndex++);
            }

            return;
        }

        if (v.isObject())
        {
            auto objectIndex = duk_push_object (context);

            auto className = v.getObjectClassName();

            if (! className.empty())
            {
                duk_push_lstring (context, className.data(), className.length());
                duk_put_prop_string (context, objectIndex, objectNameAttribute);
            }

            v.visitObjectMembers ([&] (std::string_view name, const choc::value::ValueView& value)
            {
                pushValue (value);
                duk_put_prop_lstring (context, objectIndex, name.data(), name.length());
            });

            return;
        }

        CHOC_ASSERT (v.isVoid()); // types like vectors aren't currently supported
        return duk_push_undefined (context);
    }

    static choc::value::Value readValue (duktape::duk_context* ctx, duktape::duk_idx_t index)
    {
        switch (duk_get_type (ctx, index))
        {
            case DUK_TYPE_NULL:      return {};
            case DUK_TYPE_UNDEFINED: return {};
            case DUK_TYPE_BOOLEAN:   return choc::value::createBool (static_cast<bool> (duk_get_boolean (ctx, index)));
            case DUK_TYPE_NUMBER:    return choc::value::createFloat64 (duk_get_number (ctx, index));

            case DUK_TYPE_STRING:
            {
                if (auto s = duk_get_string (ctx, index))
                {
                    choc::text::UTF8Pointer cesu8 (s);

                    if (choc::text::containsSurrogatePairs (cesu8))
                        return choc::value::createString (choc::text::convertSurrogatePairsToUTF8 (cesu8));

                    return choc::value::createString (std::string_view (s));
                }

                return choc::value::createString ({});
            }

            case DUK_TYPE_OBJECT:
            case DUK_TYPE_LIGHTFUNC:
            {
                if (duk_is_array (ctx, index))
                {
                    return choc::value::createArray (static_cast<uint32_t> (duk_get_length (ctx, index)),
                                                     [&] (uint32_t i) -> choc::value::Value
                    {
                        duk_get_prop_index (ctx, index, static_cast<duktape::duk_uarridx_t> (i));
                        auto element = readValue (ctx, -1);
                        duk_pop (ctx);
                        return element;
                    });
                }

                if (duk_is_function (ctx, index) || duk_is_lightfunc (ctx, index))
                    throw Error ("Cannot handle 'function' object type");

                // Handle an object
                duk_enum (ctx, index, DUK_ENUM_OWN_PROPERTIES_ONLY);

                bool anyRemaining = duk_next (ctx, -1, 1);
                std::string_view name;

                // look for an object name attribute as the first field
                if (anyRemaining
                     && duk_get_type (ctx, -2) == static_cast<duktape::duk_int_t> (DUK_TYPE_STRING)
                     && duk_to_string (ctx, -2) == std::string_view (objectNameAttribute))
                {
                    name = duk_to_string (ctx, -1);
                    duk_pop_2 (ctx);
                    anyRemaining = duk_next (ctx, -1, 1);
                }

                auto object = choc::value::createObject (name);

                while (anyRemaining)
                {
                    object.addMember (duk_to_string (ctx, -2), readValue (ctx, -1));
                    duk_pop_2 (ctx);
                    anyRemaining = duk_next (ctx, -1, 1);
                }

                duk_pop (ctx);
                return object;
            }

            case DUK_TYPE_NONE:
            default:
                CHOC_ASSERT (false);
                return {};
        }
    }

    //==============================================================================
    struct RegisteredFunction
    {
        RegisteredFunction (Context::NativeFunction&& f, DuktapeContext& p) : function (std::move (f)), owner (p) {}

        Context::NativeFunction function;
        DuktapeContext& owner;

        static duktape::duk_ret_t invoke (duktape::duk_context* ctx)
        {
            duk_push_current_function (ctx);
            auto& fn = RegisteredFunction::get (ctx, -1);
            duk_pop (ctx);

            auto numArgs = duk_get_top (ctx);

            if (numArgs <= 4)
            {
                choc::value::Value args[4];

                for (decltype (numArgs) i = 0; i < numArgs; ++i)
                    args[i] = readValue (ctx, i);

                return fn.invokeWithArgs (args, (size_t) numArgs);
            }

            std::vector<choc::value::Value> args;
            args.reserve ((size_t) numArgs);

            for (decltype (numArgs) i = 0; i < numArgs; ++i)
                args.push_back (readValue (ctx, i));

            return fn.invokeWithArgs (args.data(), (size_t) numArgs);
        }

        duktape::duk_ret_t invokeWithArgs (const choc::value::Value* args, size_t numArgs)
        {
            auto result = std::invoke (function, ArgumentList { args, numArgs });

            if (result.isVoid())
                return 0;

            owner.pushValue (result);
            return 1;
        }

        static duktape::duk_ret_t destroy (duktape::duk_context* ctx)
        {
            duk_require_function (ctx, 0);
            RegisteredFunction::get (ctx, 0).deregister();
            duk_pop (ctx);
            return 0;
        }

        void attachToObject (duktape::duk_context* ctx)
        {
            duk_push_pointer (ctx, (void*) this);
            duk_put_prop_string (ctx, -2, DUK_HIDDEN_SYMBOL("registeredFn"));
        }

        void pushToStack (duktape::duk_context* ctx)
        {
            duk_push_c_function (ctx, RegisteredFunction::invoke, (duktape::duk_int_t) (-1));
            attachToObject (ctx);
            duk_push_c_function (ctx, RegisteredFunction::destroy, 1);
            attachToObject (ctx);
            duk_set_finalizer (ctx, -2);
        }

        void deregister()
        {
            auto oldEnd = owner.registeredFunctions.end();
            auto newEnd = std::remove_if (owner.registeredFunctions.begin(), oldEnd,
                                          [this] (auto& f) { return f.get() == this; });

            if (newEnd != oldEnd)
                owner.registeredFunctions.erase (newEnd, oldEnd);
        }

        static RegisteredFunction& get (duktape::duk_context* ctx, duktape::duk_idx_t index)
        {
            duk_get_prop_string (ctx, index, DUK_HIDDEN_SYMBOL("registeredFn"));
            auto r = static_cast<RegisteredFunction*> (duk_get_pointer (ctx, -1));
            CHOC_ASSERT (r != nullptr);
            duk_pop (ctx);
            return *r;
        }
    };

    duktape::duk_context* context = nullptr;
    std::vector<std::unique_ptr<RegisteredFunction>> registeredFunctions;
    duktape::duk_idx_t numFunctionArgs = 0;

    static constexpr const char* objectNameAttribute = "_objectName";
};

} // namespace duktape

Context createDuktapeContext()
{
    return Context (std::make_unique<duktape::DuktapeContext>());
}

} // namespace choc::javascript

#endif // CHOC_JAVASCRIPT_DUKTAPE_HEADER_INCLUDED
