#include "v7.h"
#ifdef V7_MODULE_LINES
#line 1 "v7/src/license.h"
#endif
/*
* Copyright (c) 2013-2014 Cesanta Software Limited
* All rights reserved
*
* This software is dual-licensed: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. For the terms of this
* license, see .
*
* You are free to use this software under the terms of the GNU General
* Public License, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* Alternatively, you can license this software under a commercial
* license, as set out in .
*/
#ifdef V7_EXPOSE_PRIVATE
#define V7_PRIVATE
#define V7_EXTERN extern
#else
#define V7_PRIVATE static
#define V7_EXTERN static
#endif /* CS_V7_SRC_LICENSE_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/platform.h"
#endif
#ifndef CS_COMMON_PLATFORM_H_
#define CS_COMMON_PLATFORM_H_
/*
* For the "custom" platform, includes and dependencies can be
* provided through mg_locals.h.
*/
#define CS_P_CUSTOM 0
#define CS_P_UNIX 1
#define CS_P_WINDOWS 2
#define CS_P_ESP32 15
#define CS_P_ESP8266 3
#define CS_P_CC3100 6
#define CS_P_CC3200 4
#define CS_P_CC3220 17
#define CS_P_MSP432 5
#define CS_P_TM4C129 14
#define CS_P_MBED 7
#define CS_P_WINCE 8
#define CS_P_NXP_LPC 13
#define CS_P_NXP_KINETIS 9
#define CS_P_NRF51 12
#define CS_P_NRF52 10
#define CS_P_PIC32 11
#define CS_P_STM32 16
/* Next id: 18 */
/* If not specified explicitly, we guess platform by defines. */
#ifndef CS_PLATFORM
#if defined(TARGET_IS_MSP432P4XX) || defined(__MSP432P401R__)
#define CS_PLATFORM CS_P_MSP432
#elif defined(cc3200) || defined(TARGET_IS_CC3200)
#define CS_PLATFORM CS_P_CC3200
#elif defined(cc3220) || defined(TARGET_IS_CC3220)
#define CS_PLATFORM CS_P_CC3220
#elif defined(__unix__) || defined(__APPLE__)
#define CS_PLATFORM CS_P_UNIX
#elif defined(WINCE)
#define CS_PLATFORM CS_P_WINCE
#elif defined(_WIN32)
#define CS_PLATFORM CS_P_WINDOWS
#elif defined(__MBED__)
#define CS_PLATFORM CS_P_MBED
#elif defined(__USE_LPCOPEN)
#define CS_PLATFORM CS_P_NXP_LPC
#elif defined(FRDM_K64F) || defined(FREEDOM)
#define CS_PLATFORM CS_P_NXP_KINETIS
#elif defined(PIC32)
#define CS_PLATFORM CS_P_PIC32
#elif defined(ESP_PLATFORM)
#define CS_PLATFORM CS_P_ESP32
#elif defined(ICACHE_FLASH)
#define CS_PLATFORM CS_P_ESP8266
#elif defined(TARGET_IS_TM4C129_RA0) || defined(TARGET_IS_TM4C129_RA1) || \
defined(TARGET_IS_TM4C129_RA2)
#define CS_PLATFORM CS_P_TM4C129
#elif defined(STM32)
#define CS_PLATFORM CS_P_STM32
#endif
#ifndef CS_PLATFORM
#error "CS_PLATFORM is not specified and we couldn't guess it."
#endif
#endif /* !defined(CS_PLATFORM) */
#define MG_NET_IF_SOCKET 1
#define MG_NET_IF_SIMPLELINK 2
#define MG_NET_IF_LWIP_LOW_LEVEL 3
#define MG_NET_IF_PIC32 4
#define MG_SSL_IF_OPENSSL 1
#define MG_SSL_IF_MBEDTLS 2
#define MG_SSL_IF_SIMPLELINK 3
/* Amalgamated: #include "common/platforms/platform_unix.h" */
/* Amalgamated: #include "common/platforms/platform_windows.h" */
/* Amalgamated: #include "common/platforms/platform_esp32.h" */
/* Amalgamated: #include "common/platforms/platform_esp8266.h" */
/* Amalgamated: #include "common/platforms/platform_cc3100.h" */
/* Amalgamated: #include "common/platforms/platform_cc3200.h" */
/* Amalgamated: #include "common/platforms/platform_cc3220.h" */
/* Amalgamated: #include "common/platforms/platform_mbed.h" */
/* Amalgamated: #include "common/platforms/platform_nrf51.h" */
/* Amalgamated: #include "common/platforms/platform_nrf52.h" */
/* Amalgamated: #include "common/platforms/platform_wince.h" */
/* Amalgamated: #include "common/platforms/platform_nxp_lpc.h" */
/* Amalgamated: #include "common/platforms/platform_nxp_kinetis.h" */
/* Amalgamated: #include "common/platforms/platform_pic32.h" */
/* Amalgamated: #include "common/platforms/platform_stm32.h" */
/* Common stuff */
#if !defined(WEAK)
#if (defined(__GNUC__) || defined(__TI_COMPILER_VERSION__)) && !defined(_WIN32)
#define WEAK __attribute__((weak))
#else
#define WEAK
#endif
#endif
#ifdef __GNUC__
#define NORETURN __attribute__((noreturn))
#define NOINLINE __attribute__((noinline))
#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
#define NOINSTR __attribute__((no_instrument_function))
#define DO_NOT_WARN_UNUSED __attribute__((unused))
#else
#define NORETURN
#define NOINLINE
#define WARN_UNUSED_RESULT
#define NOINSTR
#define DO_NOT_WARN_UNUSED
#endif /* __GNUC__ */
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
#endif
#endif /* CS_COMMON_PLATFORM_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/platforms/platform_windows.h"
#endif
#ifndef CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_
#define CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_
#if CS_PLATFORM == CS_P_WINDOWS
/*
* MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
* MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013)
* MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012)
* MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010)
* MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008)
* MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005)
* MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio 2003)
* MSVC++ 7.0 _MSC_VER == 1300
* MSVC++ 6.0 _MSC_VER == 1200
* MSVC++ 5.0 _MSC_VER == 1100
*/
#ifdef _MSC_VER
#pragma warning(disable : 4127) /* FD_SET() emits warning, disable it */
#pragma warning(disable : 4204) /* missing c99 support */
#endif
#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
#endif
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef _MSC_VER
#pragma comment(lib, "ws2_32.lib") /* Linking with winsock library */
#endif
#include
#include
#include
#include
#if defined(_MSC_VER) && _MSC_VER >= 1800
#define strdup _strdup
#endif
#ifndef EINPROGRESS
#define EINPROGRESS WSAEINPROGRESS
#endif
#ifndef EWOULDBLOCK
#define EWOULDBLOCK WSAEWOULDBLOCK
#endif
#ifndef __func__
#define STRX(x) #x
#define STR(x) STRX(x)
#define __func__ __FILE__ ":" STR(__LINE__)
#endif
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#define to64(x) _atoi64(x)
#if !defined(__MINGW32__) && !defined(__MINGW64__)
#define popen(x, y) _popen((x), (y))
#define pclose(x) _pclose(x)
#define fileno _fileno
#endif
#if defined(_MSC_VER) && _MSC_VER >= 1400
#define fseeko(x, y, z) _fseeki64((x), (y), (z))
#else
#define fseeko(x, y, z) fseek((x), (y), (z))
#endif
#if defined(_MSC_VER) && _MSC_VER <= 1200
typedef unsigned long uintptr_t;
typedef long intptr_t;
#endif
typedef int socklen_t;
#if _MSC_VER >= 1700
#include
#else
typedef signed char int8_t;
typedef unsigned char uint8_t;
typedef int int32_t;
typedef unsigned int uint32_t;
typedef short int16_t;
typedef unsigned short uint16_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#endif
typedef SOCKET sock_t;
typedef uint32_t in_addr_t;
#ifndef UINT16_MAX
#define UINT16_MAX 65535
#endif
#ifndef UINT32_MAX
#define UINT32_MAX 4294967295
#endif
#ifndef pid_t
#define pid_t HANDLE
#endif
#define INT64_FMT "I64d"
#define INT64_X_FMT "I64x"
#define SIZE_T_FMT "Iu"
typedef struct _stati64 cs_stat_t;
#ifndef S_ISDIR
#define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR)
#endif
#ifndef S_ISREG
#define S_ISREG(x) (((x) &_S_IFMT) == _S_IFREG)
#endif
#define DIRSEP '\\'
#define CS_DEFINE_DIRENT
#ifndef va_copy
#ifdef __va_copy
#define va_copy __va_copy
#else
#define va_copy(x, y) (x) = (y)
#endif
#endif
#ifndef MG_MAX_HTTP_REQUEST_SIZE
#define MG_MAX_HTTP_REQUEST_SIZE 8192
#endif
#ifndef MG_MAX_HTTP_SEND_MBUF
#define MG_MAX_HTTP_SEND_MBUF 4096
#endif
#ifndef MG_MAX_HTTP_HEADERS
#define MG_MAX_HTTP_HEADERS 40
#endif
#ifndef CS_ENABLE_STDIO
#define CS_ENABLE_STDIO 1
#endif
#ifndef MG_ENABLE_BROADCAST
#define MG_ENABLE_BROADCAST 1
#endif
#ifndef MG_ENABLE_DIRECTORY_LISTING
#define MG_ENABLE_DIRECTORY_LISTING 1
#endif
#ifndef MG_ENABLE_FILESYSTEM
#define MG_ENABLE_FILESYSTEM 1
#endif
#ifndef MG_ENABLE_HTTP_CGI
#define MG_ENABLE_HTTP_CGI MG_ENABLE_FILESYSTEM
#endif
#ifndef MG_NET_IF
#define MG_NET_IF MG_NET_IF_SOCKET
#endif
int rmdir(const char *dirname);
unsigned int sleep(unsigned int seconds);
#endif /* CS_PLATFORM == CS_P_WINDOWS */
#endif /* CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/platforms/platform_unix.h"
#endif
#ifndef CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_
#define CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_
#if CS_PLATFORM == CS_P_UNIX
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600
#endif
/* wants this for C++ */
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
/* C++ wants that for INT64_MAX */
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
/* Enable fseeko() and ftello() functions */
#ifndef _LARGEFILE_SOURCE
#define _LARGEFILE_SOURCE
#endif
/* Enable 64-bit file offsets */
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef __APPLE__
#include
#ifndef BYTE_ORDER
#define LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN
#define BIG_ENDIAN __DARWIN_BIG_ENDIAN
#define PDP_ENDIAN __DARWIN_PDP_ENDIAN
#define BYTE_ORDER __DARWIN_BYTE_ORDER
#endif
#endif
/*
* osx correctly avoids defining strtoll when compiling in strict ansi mode.
* c++ 11 standard defines strtoll as well.
* We require strtoll, and if your embedded pre-c99 compiler lacks one, please
* implement a shim.
*/
#if !(defined(__cplusplus) && __cplusplus >= 201103L) && \
!(defined(__DARWIN_C_LEVEL) && __DARWIN_C_LEVEL >= 200809L)
long long strtoll(const char *, char **, int);
#endif
typedef int sock_t;
#define INVALID_SOCKET (-1)
#define SIZE_T_FMT "zu"
typedef struct stat cs_stat_t;
#define DIRSEP '/'
#define to64(x) strtoll(x, NULL, 10)
#define INT64_FMT PRId64
#define INT64_X_FMT PRIx64
#ifndef __cdecl
#define __cdecl
#endif
#ifndef va_copy
#ifdef __va_copy
#define va_copy __va_copy
#else
#define va_copy(x, y) (x) = (y)
#endif
#endif
#define closesocket(x) close(x)
#ifndef MG_MAX_HTTP_REQUEST_SIZE
#define MG_MAX_HTTP_REQUEST_SIZE 8192
#endif
#ifndef MG_MAX_HTTP_SEND_MBUF
#define MG_MAX_HTTP_SEND_MBUF 4096
#endif
#ifndef MG_MAX_HTTP_HEADERS
#define MG_MAX_HTTP_HEADERS 40
#endif
#ifndef CS_ENABLE_STDIO
#define CS_ENABLE_STDIO 1
#endif
#ifndef MG_ENABLE_BROADCAST
#define MG_ENABLE_BROADCAST 1
#endif
#ifndef MG_ENABLE_DIRECTORY_LISTING
#define MG_ENABLE_DIRECTORY_LISTING 1
#endif
#ifndef MG_ENABLE_FILESYSTEM
#define MG_ENABLE_FILESYSTEM 1
#endif
#ifndef MG_ENABLE_HTTP_CGI
#define MG_ENABLE_HTTP_CGI MG_ENABLE_FILESYSTEM
#endif
#ifndef MG_NET_IF
#define MG_NET_IF MG_NET_IF_SOCKET
#endif
#ifndef MG_HOSTS_FILE_NAME
#define MG_HOSTS_FILE_NAME "/etc/hosts"
#endif
#ifndef MG_RESOLV_CONF_FILE_NAME
#define MG_RESOLV_CONF_FILE_NAME "/etc/resolv.conf"
#endif
#endif /* CS_PLATFORM == CS_P_UNIX */
#endif /* CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/platforms/platform_esp32.h"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_
#define CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_
#if CS_PLATFORM == CS_P_ESP32
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SIZE_T_FMT "u"
typedef struct stat cs_stat_t;
#define DIRSEP '/'
#define to64(x) strtoll(x, NULL, 10)
#define INT64_FMT PRId64
#define INT64_X_FMT PRIx64
#define __cdecl
#define _FILE_OFFSET_BITS 32
#define MG_LWIP 1
#ifndef MG_NET_IF
#define MG_NET_IF MG_NET_IF_SOCKET
#endif
#ifndef CS_ENABLE_STDIO
#define CS_ENABLE_STDIO 1
#endif
#endif /* CS_PLATFORM == CS_P_ESP32 */
#endif /* CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/platforms/platform_esp8266.h"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_
#define CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_
#if CS_PLATFORM == CS_P_ESP8266
#include
#include
#include
#include
#include
#include
#include
#include
#define SIZE_T_FMT "u"
typedef struct stat cs_stat_t;
#define DIRSEP '/'
#if !defined(MGOS_VFS_DEFINE_DIRENT)
#define CS_DEFINE_DIRENT
#endif
#define to64(x) strtoll(x, NULL, 10)
#define INT64_FMT PRId64
#define INT64_X_FMT PRIx64
#define __cdecl
#define _FILE_OFFSET_BITS 32
#if !defined(RTOS_SDK) && !defined(__cplusplus)
#define fileno(x) -1
#endif
#define MG_LWIP 1
/* struct timeval is defined in sys/time.h. */
#define LWIP_TIMEVAL_PRIVATE 0
#ifndef MG_NET_IF
#include
#if LWIP_SOCKET /* RTOS SDK has LWIP sockets */
#define MG_NET_IF MG_NET_IF_SOCKET
#else
#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL
#endif
#endif
#ifndef CS_ENABLE_STDIO
#define CS_ENABLE_STDIO 1
#endif
#endif /* CS_PLATFORM == CS_P_ESP8266 */
#endif /* CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/platforms/platform_cc3100.h"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_
#define CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_
#if CS_PLATFORM == CS_P_CC3100
#include
#include
#include
#include
#include
#include
#include
#define MG_NET_IF MG_NET_IF_SIMPLELINK
#define MG_SSL_IF MG_SSL_IF_SIMPLELINK
/*
* CC3100 SDK and STM32 SDK include headers w/out path, just like
* #include "simplelink.h". As result, we have to add all required directories
* into Makefile IPATH and do the same thing (include w/out path)
*/
#include
#include
#undef timeval
typedef int sock_t;
#define INVALID_SOCKET (-1)
#define to64(x) strtoll(x, NULL, 10)
#define INT64_FMT PRId64
#define INT64_X_FMT PRIx64
#define SIZE_T_FMT "u"
#define SOMAXCONN 8
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
char *inet_ntoa(struct in_addr in);
int inet_pton(int af, const char *src, void *dst);
#endif /* CS_PLATFORM == CS_P_CC3100 */
#endif /* CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/platforms/simplelink/cs_simplelink.h"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_
#define CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_
#if defined(MG_NET_IF) && MG_NET_IF == MG_NET_IF_SIMPLELINK
/* If simplelink.h is already included, all bets are off. */
#if !defined(__SIMPLELINK_H__)
#include
#ifndef __TI_COMPILER_VERSION__
#undef __CONCAT
#undef FD_CLR
#undef FD_ISSET
#undef FD_SET
#undef FD_SETSIZE
#undef FD_ZERO
#undef fd_set
#endif
#if CS_PLATFORM == CS_P_CC3220
#include
#include
#include
#include
#else
/* We want to disable SL_INC_STD_BSD_API_NAMING, so we include user.h ourselves
* and undef it. */
#define PROVISIONING_API_H_
#include
#undef PROVISIONING_API_H_
#undef SL_INC_STD_BSD_API_NAMING
#include
#include
#endif /* CS_PLATFORM == CS_P_CC3220 */
/* Now define only the subset of the BSD API that we use.
* Notably, close(), read() and write() are not defined. */
#define AF_INET SL_AF_INET
#define socklen_t SlSocklen_t
#define sockaddr SlSockAddr_t
#define sockaddr_in SlSockAddrIn_t
#define in_addr SlInAddr_t
#define SOCK_STREAM SL_SOCK_STREAM
#define SOCK_DGRAM SL_SOCK_DGRAM
#define htonl sl_Htonl
#define ntohl sl_Ntohl
#define htons sl_Htons
#define ntohs sl_Ntohs
#ifndef EACCES
#define EACCES SL_EACCES
#endif
#ifndef EAFNOSUPPORT
#define EAFNOSUPPORT SL_EAFNOSUPPORT
#endif
#ifndef EAGAIN
#define EAGAIN SL_EAGAIN
#endif
#ifndef EBADF
#define EBADF SL_EBADF
#endif
#ifndef EINVAL
#define EINVAL SL_EINVAL
#endif
#ifndef ENOMEM
#define ENOMEM SL_ENOMEM
#endif
#ifndef EWOULDBLOCK
#define EWOULDBLOCK SL_EWOULDBLOCK
#endif
#define SOMAXCONN 8
#ifdef __cplusplus
extern "C" {
#endif
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
char *inet_ntoa(struct in_addr in);
int inet_pton(int af, const char *src, void *dst);
struct mg_mgr;
struct mg_connection;
typedef void (*mg_init_cb)(struct mg_mgr *mgr);
bool mg_start_task(int priority, int stack_size, mg_init_cb mg_init);
void mg_run_in_task(void (*cb)(struct mg_mgr *mgr, void *arg), void *cb_arg);
int sl_fs_init(void);
void sl_restart_cb(struct mg_mgr *mgr);
int sl_set_ssl_opts(struct mg_connection *nc);
#ifdef __cplusplus
}
#endif
#endif /* !defined(__SIMPLELINK_H__) */
/* Compatibility with older versions of SimpleLink */
#if SL_MAJOR_VERSION_NUM < 2
#define SL_ERROR_BSD_EAGAIN SL_EAGAIN
#define SL_ERROR_BSD_EALREADY SL_EALREADY
#define SL_ERROR_BSD_ENOPROTOOPT SL_ENOPROTOOPT
#define SL_ERROR_BSD_ESECDATEERROR SL_ESECDATEERROR
#define SL_ERROR_BSD_ESECSNOVERIFY SL_ESECSNOVERIFY
#define SL_ERROR_FS_FAILED_TO_ALLOCATE_MEM SL_FS_ERR_FAILED_TO_ALLOCATE_MEM
#define SL_ERROR_FS_FILE_HAS_NOT_BEEN_CLOSE_CORRECTLY \
SL_FS_FILE_HAS_NOT_BEEN_CLOSE_CORRECTLY
#define SL_ERROR_FS_FILE_NAME_EXIST SL_FS_FILE_NAME_EXIST
#define SL_ERROR_FS_FILE_NOT_EXISTS SL_FS_ERR_FILE_NOT_EXISTS
#define SL_ERROR_FS_NO_AVAILABLE_NV_INDEX SL_FS_ERR_NO_AVAILABLE_NV_INDEX
#define SL_ERROR_FS_NOT_ENOUGH_STORAGE_SPACE SL_FS_ERR_NO_AVAILABLE_BLOCKS
#define SL_ERROR_FS_NOT_SUPPORTED SL_FS_ERR_NOT_SUPPORTED
#define SL_ERROR_FS_WRONG_FILE_NAME SL_FS_WRONG_FILE_NAME
#define SL_ERROR_FS_INVALID_HANDLE SL_FS_ERR_INVALID_HANDLE
#define SL_NETCFG_MAC_ADDRESS_GET SL_MAC_ADDRESS_GET
#define SL_SOCKET_FD_ZERO SL_FD_ZERO
#define SL_SOCKET_FD_SET SL_FD_SET
#define SL_SOCKET_FD_ISSET SL_FD_ISSET
#define SL_SO_SECURE_DOMAIN_NAME_VERIFICATION SO_SECURE_DOMAIN_NAME_VERIFICATION
#define SL_FS_READ FS_MODE_OPEN_READ
#define SL_FS_WRITE FS_MODE_OPEN_WRITE
#define SL_FI_FILE_SIZE(fi) ((fi).FileLen)
#define SL_FI_FILE_MAX_SIZE(fi) ((fi).AllocatedLen)
#define SlDeviceVersion_t SlVersionFull
#define sl_DeviceGet sl_DevGet
#define SL_DEVICE_GENERAL SL_DEVICE_GENERAL_CONFIGURATION
#define SL_LEN_TYPE _u8
#define SL_OPT_TYPE _u8
#else /* SL_MAJOR_VERSION_NUM >= 2 */
#define FS_MODE_OPEN_CREATE(max_size, flag) \
(SL_FS_CREATE | SL_FS_CREATE_MAX_SIZE(max_size))
#define SL_FI_FILE_SIZE(fi) ((fi).Len)
#define SL_FI_FILE_MAX_SIZE(fi) ((fi).MaxSize)
#define SL_LEN_TYPE _u16
#define SL_OPT_TYPE _u16
#endif /* SL_MAJOR_VERSION_NUM < 2 */
int slfs_open(const unsigned char *fname, uint32_t flags);
#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */
#endif /* CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/platforms/platform_cc3200.h"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_
#define CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_
#if CS_PLATFORM == CS_P_CC3200
#include
#include
#include
#include
#include
#include
#include
#ifndef __TI_COMPILER_VERSION__
#include
#include
#endif
#define MG_NET_IF MG_NET_IF_SIMPLELINK
#define MG_SSL_IF MG_SSL_IF_SIMPLELINK
/* Only SPIFFS supports directories, SLFS does not. */
#if defined(CC3200_FS_SPIFFS) && !defined(MG_ENABLE_DIRECTORY_LISTING)
#define MG_ENABLE_DIRECTORY_LISTING 1
#endif
/* Amalgamated: #include "common/platforms/simplelink/cs_simplelink.h" */
typedef int sock_t;
#define INVALID_SOCKET (-1)
#define SIZE_T_FMT "u"
typedef struct stat cs_stat_t;
#define DIRSEP '/'
#define to64(x) strtoll(x, NULL, 10)
#define INT64_FMT PRId64
#define INT64_X_FMT PRIx64
#define __cdecl
#define fileno(x) -1
/* Some functions we implement for Mongoose. */
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __TI_COMPILER_VERSION__
struct SlTimeval_t;
#define timeval SlTimeval_t
int gettimeofday(struct timeval *t, void *tz);
int settimeofday(const struct timeval *tv, const void *tz);
int asprintf(char **strp, const char *fmt, ...);
#endif
/* TI's libc does not have stat & friends, add them. */
#ifdef __TI_COMPILER_VERSION__
#include
typedef unsigned int mode_t;
typedef size_t _off_t;
typedef long ssize_t;
struct stat {
int st_ino;
mode_t st_mode;
int st_nlink;
time_t st_mtime;
off_t st_size;
};
int _stat(const char *pathname, struct stat *st);
int stat(const char *pathname, struct stat *st);
#define __S_IFMT 0170000
#define __S_IFDIR 0040000
#define __S_IFCHR 0020000
#define __S_IFREG 0100000
#define __S_ISTYPE(mode, mask) (((mode) &__S_IFMT) == (mask))
#define S_IFDIR __S_IFDIR
#define S_IFCHR __S_IFCHR
#define S_IFREG __S_IFREG
#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR)
#define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG)
/* 5.x series compilers don't have va_copy, 16.x do. */
#if __TI_COMPILER_VERSION__ < 16000000
#define va_copy(apc, ap) ((apc) = (ap))
#endif
#endif /* __TI_COMPILER_VERSION__ */
#ifdef CC3200_FS_SLFS
#define MG_FS_SLFS
#endif
#if (defined(CC3200_FS_SPIFFS) || defined(CC3200_FS_SLFS)) && \
!defined(MG_ENABLE_FILESYSTEM)
#define MG_ENABLE_FILESYSTEM 1
#define CS_DEFINE_DIRENT
#endif
#ifndef CS_ENABLE_STDIO
#define CS_ENABLE_STDIO 1
#endif
#ifdef __cplusplus
}
#endif
#endif /* CS_PLATFORM == CS_P_CC3200 */
#endif /* CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/platforms/platform_cc3220.h"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_PLATFORMS_PLATFORM_CC3220_H_
#define CS_COMMON_PLATFORMS_PLATFORM_CC3220_H_
#if CS_PLATFORM == CS_P_CC3220
#include
#include
#include
#include
#include
#include
#include
#ifndef __TI_COMPILER_VERSION__
#include
#include
#endif
#define MG_NET_IF MG_NET_IF_SIMPLELINK
#define MG_SSL_IF MG_SSL_IF_SIMPLELINK
/* Only SPIFFS supports directories, SLFS does not. */
#if defined(CC3220_FS_SPIFFS) && !defined(MG_ENABLE_DIRECTORY_LISTING)
#define MG_ENABLE_DIRECTORY_LISTING 1
#endif
/* Amalgamated: #include "common/platforms/simplelink/cs_simplelink.h" */
typedef int sock_t;
#define INVALID_SOCKET (-1)
#define SIZE_T_FMT "u"
typedef struct stat cs_stat_t;
#define DIRSEP '/'
#define to64(x) strtoll(x, NULL, 10)
#define INT64_FMT PRId64
#define INT64_X_FMT PRIx64
#define __cdecl
#define fileno(x) -1
/* Some functions we implement for Mongoose. */
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __TI_COMPILER_VERSION__
struct SlTimeval_t;
#define timeval SlTimeval_t
int gettimeofday(struct timeval *t, void *tz);
int settimeofday(const struct timeval *tv, const void *tz);
int asprintf(char **strp, const char *fmt, ...);
#endif
/* TI's libc does not have stat & friends, add them. */
#ifdef __TI_COMPILER_VERSION__
#include
typedef unsigned int mode_t;
typedef size_t _off_t;
typedef long ssize_t;
struct stat {
int st_ino;
mode_t st_mode;
int st_nlink;
time_t st_mtime;
off_t st_size;
};
int _stat(const char *pathname, struct stat *st);
int stat(const char *pathname, struct stat *st);
#define __S_IFMT 0170000
#define __S_IFDIR 0040000
#define __S_IFCHR 0020000
#define __S_IFREG 0100000
#define __S_ISTYPE(mode, mask) (((mode) &__S_IFMT) == (mask))
#define S_IFDIR __S_IFDIR
#define S_IFCHR __S_IFCHR
#define S_IFREG __S_IFREG
#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR)
#define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG)
#endif /* __TI_COMPILER_VERSION__ */
#ifndef CS_ENABLE_STDIO
#define CS_ENABLE_STDIO 1
#endif
#ifdef __cplusplus
}
#endif
#endif /* CS_PLATFORM == CS_P_CC3220 */
#endif /* CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/platforms/platform_mbed.h"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_PLATFORMS_PLATFORM_MBED_H_
#define CS_COMMON_PLATFORMS_PLATFORM_MBED_H_
#if CS_PLATFORM == CS_P_MBED
/*
* mbed.h contains C++ code (e.g. templates), thus, it should be processed
* only if included directly to startup file (ex: main.cpp)
*/
#ifdef __cplusplus
/* Amalgamated: #include "mbed.h" */
#endif /* __cplusplus */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef struct stat cs_stat_t;
#define DIRSEP '/'
#ifndef CS_ENABLE_STDIO
#define CS_ENABLE_STDIO 1
#endif
/*
* mbed can be compiled with the ARM compiler which
* just doesn't come with a gettimeofday shim
* because it's a BSD API and ARM targets embedded
* non-unix platforms.
*/
#if defined(__ARMCC_VERSION) || defined(__ICCARM__)
#define _TIMEVAL_DEFINED
#define gettimeofday _gettimeofday
/* copied from GCC on ARM; for some reason useconds are signed */
typedef long suseconds_t; /* microseconds (signed) */
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* and microseconds */
};
#endif
#if MG_NET_IF == MG_NET_IF_SIMPLELINK
#define MG_SIMPLELINK_NO_OSI 1
#include
typedef int sock_t;
#define INVALID_SOCKET (-1)
#define to64(x) strtoll(x, NULL, 10)
#define INT64_FMT PRId64
#define INT64_X_FMT PRIx64
#define SIZE_T_FMT "u"
#define SOMAXCONN 8
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
char *inet_ntoa(struct in_addr in);
int inet_pton(int af, const char *src, void *dst);
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);
#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */
#endif /* CS_PLATFORM == CS_P_MBED */
#endif /* CS_COMMON_PLATFORMS_PLATFORM_MBED_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/platforms/platform_nrf51.h"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_
#define CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_
#if CS_PLATFORM == CS_P_NRF51
#include
#include
#include
#include
#include
#include
#define to64(x) strtoll(x, NULL, 10)
#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL
#define MG_LWIP 1
#define MG_ENABLE_IPV6 1
/*
* For ARM C Compiler, make lwip to export `struct timeval`; for other
* compilers, suppress it.
*/
#if !defined(__ARMCC_VERSION)
#define LWIP_TIMEVAL_PRIVATE 0
#else
struct timeval;
int gettimeofday(struct timeval *tp, void *tzp);
#endif
#define INT64_FMT PRId64
#define SIZE_T_FMT "u"
/*
* ARM C Compiler doesn't have strdup, so we provide it
*/
#define CS_ENABLE_STRDUP defined(__ARMCC_VERSION)
#endif /* CS_PLATFORM == CS_P_NRF51 */
#endif /* CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/platforms/platform_nrf52.h"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_
#define CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_
#if CS_PLATFORM == CS_P_NRF52
#include
#include
#include
#include
#include
#include
#include
#define to64(x) strtoll(x, NULL, 10)
#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL
#define MG_LWIP 1
#define MG_ENABLE_IPV6 1
#if !defined(ENOSPC)
#define ENOSPC 28 /* No space left on device */
#endif
/*
* For ARM C Compiler, make lwip to export `struct timeval`; for other
* compilers, suppress it.
*/
#if !defined(__ARMCC_VERSION)
#define LWIP_TIMEVAL_PRIVATE 0
#endif
#define INT64_FMT PRId64
#define SIZE_T_FMT "u"
/*
* ARM C Compiler doesn't have strdup, so we provide it
*/
#define CS_ENABLE_STRDUP defined(__ARMCC_VERSION)
#endif /* CS_PLATFORM == CS_P_NRF52 */
#endif /* CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/platforms/platform_wince.h"
#endif
#ifndef CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_
#define CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_
#if CS_PLATFORM == CS_P_WINCE
/*
* MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
* MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013)
* MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012)
* MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010)
* MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008)
* MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005)
* MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio 2003)
* MSVC++ 7.0 _MSC_VER == 1300
* MSVC++ 6.0 _MSC_VER == 1200
* MSVC++ 5.0 _MSC_VER == 1100
*/
#pragma warning(disable : 4127) /* FD_SET() emits warning, disable it */
#pragma warning(disable : 4204) /* missing c99 support */
#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
#endif
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include
#include
#include
#include
#include
#include
#pragma comment(lib, "ws2.lib") /* Linking with WinCE winsock library */
#include
#include
#include
#define strdup _strdup
#ifndef EINPROGRESS
#define EINPROGRESS WSAEINPROGRESS
#endif
#ifndef EWOULDBLOCK
#define EWOULDBLOCK WSAEWOULDBLOCK
#endif
#ifndef EAGAIN
#define EAGAIN EWOULDBLOCK
#endif
#ifndef __func__
#define STRX(x) #x
#define STR(x) STRX(x)
#define __func__ __FILE__ ":" STR(__LINE__)
#endif
#define snprintf _snprintf
#define fileno _fileno
#define vsnprintf _vsnprintf
#define sleep(x) Sleep((x) *1000)
#define to64(x) _atoi64(x)
#define rmdir _rmdir
#if defined(_MSC_VER) && _MSC_VER >= 1400
#define fseeko(x, y, z) _fseeki64((x), (y), (z))
#else
#define fseeko(x, y, z) fseek((x), (y), (z))
#endif
typedef int socklen_t;
#if _MSC_VER >= 1700
#include
#else
typedef signed char int8_t;
typedef unsigned char uint8_t;
typedef int int32_t;
typedef unsigned int uint32_t;
typedef short int16_t;
typedef unsigned short uint16_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#endif
typedef SOCKET sock_t;
typedef uint32_t in_addr_t;
#ifndef UINT16_MAX
#define UINT16_MAX 65535
#endif
#ifndef UINT32_MAX
#define UINT32_MAX 4294967295
#endif
#ifndef pid_t
#define pid_t HANDLE
#endif
#define INT64_FMT "I64d"
#define INT64_X_FMT "I64x"
/* TODO(alashkin): check if this is correct */
#define SIZE_T_FMT "u"
#define DIRSEP '\\'
#define CS_DEFINE_DIRENT
#ifndef va_copy
#ifdef __va_copy
#define va_copy __va_copy
#else
#define va_copy(x, y) (x) = (y)
#endif
#endif
#ifndef MG_MAX_HTTP_REQUEST_SIZE
#define MG_MAX_HTTP_REQUEST_SIZE 8192
#endif
#ifndef MG_MAX_HTTP_SEND_MBUF
#define MG_MAX_HTTP_SEND_MBUF 4096
#endif
#ifndef MG_MAX_HTTP_HEADERS
#define MG_MAX_HTTP_HEADERS 40
#endif
#ifndef CS_ENABLE_STDIO
#define CS_ENABLE_STDIO 1
#endif
#define abort() DebugBreak();
#ifndef BUFSIZ
#define BUFSIZ 4096
#endif
/*
* Explicitly disabling MG_ENABLE_THREADS for WinCE
* because they are enabled for _WIN32 by default
*/
#ifndef MG_ENABLE_THREADS
#define MG_ENABLE_THREADS 0
#endif
#ifndef MG_ENABLE_FILESYSTEM
#define MG_ENABLE_FILESYSTEM 1
#endif
#ifndef MG_NET_IF
#define MG_NET_IF MG_NET_IF_SOCKET
#endif
typedef struct _stati64 {
uint32_t st_mtime;
uint32_t st_size;
uint32_t st_mode;
} cs_stat_t;
/*
* WinCE 6.0 has a lot of useful definitions in ATL (not windows.h) headers
* use #ifdefs to avoid conflicts
*/
#ifndef ENOENT
#define ENOENT ERROR_PATH_NOT_FOUND
#endif
#ifndef EACCES
#define EACCES ERROR_ACCESS_DENIED
#endif
#ifndef ENOMEM
#define ENOMEM ERROR_NOT_ENOUGH_MEMORY
#endif
#ifndef _UINTPTR_T_DEFINED
typedef unsigned int *uintptr_t;
#endif
#define _S_IFREG 2
#define _S_IFDIR 4
#ifndef S_ISDIR
#define S_ISDIR(x) (((x) &_S_IFDIR) != 0)
#endif
#ifndef S_ISREG
#define S_ISREG(x) (((x) &_S_IFREG) != 0)
#endif
int open(const char *filename, int oflag, int pmode);
int _wstati64(const wchar_t *path, cs_stat_t *st);
const char *strerror();
#endif /* CS_PLATFORM == CS_P_WINCE */
#endif /* CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/platforms/platform_nxp_lpc.h"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_
#define CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_
#if CS_PLATFORM == CS_P_NXP_LPC
#include
#include
#include
#define SIZE_T_FMT "u"
typedef struct stat cs_stat_t;
#define INT64_FMT "lld"
#define INT64_X_FMT "llx"
#define __cdecl
#define MG_LWIP 1
#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL
/*
* LPCXpress comes with 3 C library implementations: Newlib, NewlibNano and
*Redlib.
* See https://community.nxp.com/message/630860 for more details.
*
* Redlib is the default and lacks certain things, so we provide them.
*/
#ifdef __REDLIB_INTERFACE_VERSION__
/* Let LWIP define timeval for us. */
#define LWIP_TIMEVAL_PRIVATE 1
#define va_copy(d, s) __builtin_va_copy(d, s)
#define CS_ENABLE_TO64 1
#define to64(x) cs_to64(x)
#define CS_ENABLE_STRDUP 1
#else
#include
#define LWIP_TIMEVAL_PRIVATE 0
#define to64(x) strtoll(x, NULL, 10)
#endif
#endif /* CS_PLATFORM == CS_P_NXP_LPC */
#endif /* CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/platforms/platform_nxp_kinetis.h"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_
#define CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_
#if CS_PLATFORM == CS_P_NXP_KINETIS
#include
#include
#include
#include
#define SIZE_T_FMT "u"
typedef struct stat cs_stat_t;
#define to64(x) strtoll(x, NULL, 10)
#define INT64_FMT "lld"
#define INT64_X_FMT "llx"
#define __cdecl
#define MG_LWIP 1
#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL
/* struct timeval is defined in sys/time.h. */
#define LWIP_TIMEVAL_PRIVATE 0
#endif /* CS_PLATFORM == CS_P_NXP_KINETIS */
#endif /* CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/platforms/platform_pic32.h"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_
#define CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_
#if CS_PLATFORM == CS_P_PIC32
#define MG_NET_IF MG_NET_IF_PIC32
#include
#include
#include
#include
#include
#include
#include
typedef TCP_SOCKET sock_t;
#define to64(x) strtoll(x, NULL, 10)
#define SIZE_T_FMT "lu"
#define INT64_FMT "lld"
#ifndef CS_ENABLE_STDIO
#define CS_ENABLE_STDIO 1
#endif
char *inet_ntoa(struct in_addr in);
#endif /* CS_PLATFORM == CS_P_PIC32 */
#endif /* CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/platforms/platform_stm32.h"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_PLATFORMS_PLATFORM_STM32_H_
#define CS_COMMON_PLATFORMS_PLATFORM_STM32_H_
#if CS_PLATFORM == CS_P_STM32
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define to64(x) strtoll(x, NULL, 10)
#define INT64_FMT PRId64
#define SIZE_T_FMT "u"
typedef struct stat cs_stat_t;
#define DIRSEP '/'
#ifndef CS_ENABLE_STDIO
#define CS_ENABLE_STDIO 1
#endif
#ifndef MG_ENABLE_FILESYSTEM
#define MG_ENABLE_FILESYSTEM 1
#endif
#define CS_DEFINE_DIRENT
#endif /* CS_PLATFORM == CS_P_STM32 */
#endif /* CS_COMMON_PLATFORMS_PLATFORM_STM32_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/mbuf.h"
#endif
/*
* Copyright (c) 2015 Cesanta Software Limited
* All rights reserved
*/
/*
* === Memory Buffers
*
* Mbufs are mutable/growing memory buffers, like C++ strings.
* Mbuf can append data to the end of a buffer or insert data into arbitrary
* position in the middle of a buffer. The buffer grows automatically when
* needed.
*/
#ifndef CS_COMMON_MBUF_H_
#define CS_COMMON_MBUF_H_
#include
/* Amalgamated: #include "common/platform.h" */
#if defined(__cplusplus)
extern "C" {
#endif
#ifndef MBUF_SIZE_MULTIPLIER
#define MBUF_SIZE_MULTIPLIER 1.5
#endif
/* Memory buffer descriptor */
struct mbuf {
char *buf; /* Buffer pointer */
size_t len; /* Data length. Data is located between offset 0 and len. */
size_t size; /* Buffer size allocated by realloc(1). Must be >= len */
};
/*
* Initialises an Mbuf.
* `initial_capacity` specifies the initial capacity of the mbuf.
*/
void mbuf_init(struct mbuf *, size_t initial_capacity);
/* Frees the space allocated for the mbuffer and resets the mbuf structure. */
void mbuf_free(struct mbuf *);
/*
* Appends data to the Mbuf.
*
* Returns the number of bytes appended or 0 if out of memory.
*/
size_t mbuf_append(struct mbuf *, const void *data, size_t data_size);
/*
* Inserts data at a specified offset in the Mbuf.
*
* Existing data will be shifted forwards and the buffer will
* be grown if necessary.
* Returns the number of bytes inserted.
*/
size_t mbuf_insert(struct mbuf *, size_t, const void *, size_t);
/* Removes `data_size` bytes from the beginning of the buffer. */
void mbuf_remove(struct mbuf *, size_t data_size);
/*
* Resizes an Mbuf.
*
* If `new_size` is smaller than buffer's `len`, the
* resize is not performed.
*/
void mbuf_resize(struct mbuf *, size_t new_size);
/* Shrinks an Mbuf by resizing its `size` to `len`. */
void mbuf_trim(struct mbuf *);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_COMMON_MBUF_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/mg_mem.h"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_MG_MEM_H_
#define CS_COMMON_MG_MEM_H_
#ifdef __cplusplus
extern "C" {
#endif
#ifndef MG_MALLOC
#define MG_MALLOC malloc
#endif
#ifndef MG_CALLOC
#define MG_CALLOC calloc
#endif
#ifndef MG_REALLOC
#define MG_REALLOC realloc
#endif
#ifndef MG_FREE
#define MG_FREE free
#endif
#ifdef __cplusplus
}
#endif
#endif /* CS_COMMON_MG_MEM_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/mg_str.h"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_MG_STR_H_
#define CS_COMMON_MG_STR_H_
#include
/* Amalgamated: #include "common/platform.h" */
#ifdef __cplusplus
extern "C" {
#endif
/* Describes chunk of memory */
struct mg_str {
const char *p; /* Memory chunk pointer */
size_t len; /* Memory chunk length */
};
/*
* Helper functions for creating mg_str struct from plain C string.
* `NULL` is allowed and becomes `{NULL, 0}`.
*/
struct mg_str mg_mk_str(const char *s);
struct mg_str mg_mk_str_n(const char *s, size_t len);
/* Macro for initializing mg_str. */
#define MG_MK_STR(str_literal) \
{ str_literal, sizeof(str_literal) - 1 }
#define MG_NULL_STR \
{ NULL, 0 }
/*
* Cross-platform version of `strcmp()` where where first string is
* specified by `struct mg_str`.
*/
int mg_vcmp(const struct mg_str *str2, const char *str1);
/*
* Cross-platform version of `strncasecmp()` where first string is
* specified by `struct mg_str`.
*/
int mg_vcasecmp(const struct mg_str *str2, const char *str1);
/* Creates a copy of s (heap-allocated). */
struct mg_str mg_strdup(const struct mg_str s);
/*
* Creates a copy of s (heap-allocated).
* Resulting string is NUL-terminated (but NUL is not included in len).
*/
struct mg_str mg_strdup_nul(const struct mg_str s);
/*
* Locates character in a string.
*/
const char *mg_strchr(const struct mg_str s, int c);
int mg_strcmp(const struct mg_str str1, const struct mg_str str2);
int mg_strncmp(const struct mg_str str1, const struct mg_str str2, size_t n);
const char *mg_strstr(const struct mg_str haystack, const struct mg_str needle);
#ifdef __cplusplus
}
#endif
#endif /* CS_COMMON_MG_STR_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/str_util.h"
#endif
/*
* Copyright (c) 2015 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_STR_UTIL_H_
#define CS_COMMON_STR_UTIL_H_
#include
#include
/* Amalgamated: #include "common/platform.h" */
/* Amalgamated: #include "common/mg_str.h" */
#ifndef CS_ENABLE_STRDUP
#define CS_ENABLE_STRDUP 0
#endif
#ifndef CS_ENABLE_TO64
#define CS_ENABLE_TO64 0
#endif
/*
* Expands to a string representation of its argument: e.g.
* `CS_STRINGIFY_LIT(5) expands to "5"`
*/
#define CS_STRINGIFY_LIT(x) #x
/*
* Expands to a string representation of its argument, which is allowed
* to be a macro: e.g.
*
* #define FOO 123
* CS_STRINGIFY_MACRO(FOO)
*
* expands to 123.
*/
#define CS_STRINGIFY_MACRO(x) CS_STRINGIFY_LIT(x)
#ifdef __cplusplus
extern "C" {
#endif
size_t c_strnlen(const char *s, size_t maxlen);
int c_snprintf(char *buf, size_t buf_size, const char *format, ...);
int c_vsnprintf(char *buf, size_t buf_size, const char *format, va_list ap);
/*
* Find the first occurrence of find in s, where the search is limited to the
* first slen characters of s.
*/
const char *c_strnstr(const char *s, const char *find, size_t slen);
/*
* Stringify binary data. Output buffer size must be 2 * size_of_input + 1
* because each byte of input takes 2 bytes in string representation
* plus 1 byte for the terminating \0 character.
*/
void cs_to_hex(char *to, const unsigned char *p, size_t len);
/*
* Convert stringified binary data back to binary.
* Does the reverse of `cs_to_hex()`.
*/
void cs_from_hex(char *to, const char *p, size_t len);
#if CS_ENABLE_STRDUP
char *strdup(const char *src);
#endif
#if CS_ENABLE_TO64
#include
/*
* Simple string -> int64 conversion routine.
*/
int64_t cs_to64(const char *s);
#endif
/*
* Cross-platform version of `strncasecmp()`.
*/
int mg_ncasecmp(const char *s1, const char *s2, size_t len);
/*
* Cross-platform version of `strcasecmp()`.
*/
int mg_casecmp(const char *s1, const char *s2);
/*
* Prints message to the buffer. If the buffer is large enough to hold the
* message, it returns buffer. If buffer is to small, it allocates a large
* enough buffer on heap and returns allocated buffer.
* This is a supposed use case:
*
* char buf[5], *p = buf;
* mg_avprintf(&p, sizeof(buf), "%s", "hi there");
* use_p_somehow(p);
* if (p != buf) {
* free(p);
* }
*
* The purpose of this is to avoid malloc-ing if generated strings are small.
*/
int mg_asprintf(char **buf, size_t size, const char *fmt, ...);
/* Same as mg_asprintf, but takes varargs list. */
int mg_avprintf(char **buf, size_t size, const char *fmt, va_list ap);
/*
* A helper function for traversing a comma separated list of values.
* It returns a list pointer shifted to the next value or NULL if the end
* of the list found.
* The value is stored in a val vector. If the value has a form "x=y", then
* eq_val vector is initialised to point to the "y" part, and val vector length
* is adjusted to point only to "x".
* If the list is just a comma separated list of entries, like "aa,bb,cc" then
* `eq_val` will contain zero-length string.
*
* The purpose of this function is to parse comma separated string without
* any copying/memory allocation.
*/
const char *mg_next_comma_list_entry(const char *list, struct mg_str *val,
struct mg_str *eq_val);
struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val,
struct mg_str *eq_val);
/*
* Matches 0-terminated string (mg_match_prefix) or string with given length
* mg_match_prefix_n against a glob pattern.
*
* Match is case-insensitive. Returns number of bytes matched, or -1 if no
* match.
*/
int mg_match_prefix(const char *pattern, int pattern_len, const char *str);
int mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str);
#ifdef __cplusplus
}
#endif
#endif /* CS_COMMON_STR_UTIL_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/utf.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_UTF_H_
#define CS_COMMON_UTF_H_
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
typedef unsigned char uchar;
typedef unsigned short Rune; /* 16 bits */
#define nelem(a) (sizeof(a) / sizeof(a)[0])
enum {
UTFmax = 3, /* maximum bytes per rune */
Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */
Runeself = 0x80, /* rune and UTF sequences are the same (<) */
Runeerror = 0xFFFD /* decoding error in UTF */
/* Runemax = 0xFFFC */ /* maximum rune value */
};
/* Edit .+1,/^$/ | cfn $PLAN9/src/lib9/utf/?*.c | grep -v static |grep -v __ */
int chartorune(Rune *rune, const char *str);
int fullrune(const char *str, int n);
int isdigitrune(Rune c);
int isnewline(Rune c);
int iswordchar(Rune c);
int isalpharune(Rune c);
int islowerrune(Rune c);
int isspacerune(Rune c);
int isupperrune(Rune c);
int runetochar(char *str, Rune *rune);
Rune tolowerrune(Rune c);
Rune toupperrune(Rune c);
int utfnlen(const char *s, long m);
const char *utfnshift(const char *s, long m);
#if 0 /* Not implemented. */
int istitlerune(Rune c);
int runelen(Rune c);
int runenlen(Rune *r, int nrune);
Rune *runestrcat(Rune *s1, Rune *s2);
Rune *runestrchr(Rune *s, Rune c);
Rune *runestrcpy(Rune *s1, Rune *s2);
Rune *runestrdup(Rune *s);
Rune *runestrecpy(Rune *s1, Rune *es1, Rune *s2);
int runestrcmp(Rune *s1, Rune *s2);
long runestrlen(Rune *s);
Rune *runestrncat(Rune *s1, Rune *s2, long n);
int runestrncmp(Rune *s1, Rune *s2, long n);
Rune *runestrncpy(Rune *s1, Rune *s2, long n);
Rune *runestrrchr(Rune *s, Rune c);
Rune *runestrstr(Rune *s1, Rune *s2);
Rune totitlerune(Rune c);
char *utfecpy(char *to, char *e, char *from);
int utflen(char *s);
char *utfrrune(char *s, long c);
char *utfrune(char *s, long c);
char *utfutf(char *s1, char *s2);
#endif
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_COMMON_UTF_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/base64.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_BASE64_H_
#define CS_COMMON_BASE64_H_
#ifndef DISABLE_BASE64
#define DISABLE_BASE64 0
#endif
#if !DISABLE_BASE64
#include
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*cs_base64_putc_t)(char, void *);
struct cs_base64_ctx {
/* cannot call it putc because it's a macro on some environments */
cs_base64_putc_t b64_putc;
unsigned char chunk[3];
int chunk_size;
void *user_data;
};
void cs_base64_init(struct cs_base64_ctx *ctx, cs_base64_putc_t putc,
void *user_data);
void cs_base64_update(struct cs_base64_ctx *ctx, const char *str, size_t len);
void cs_base64_finish(struct cs_base64_ctx *ctx);
void cs_base64_encode(const unsigned char *src, int src_len, char *dst);
void cs_fprint_base64(FILE *f, const unsigned char *src, int src_len);
int cs_base64_decode(const unsigned char *s, int len, char *dst, int *dec_len);
#ifdef __cplusplus
}
#endif
#endif /* DISABLE_BASE64 */
#endif /* CS_COMMON_BASE64_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/cs_dbg.h"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_CS_DBG_H_
#define CS_COMMON_CS_DBG_H_
/* Amalgamated: #include "common/platform.h" */
#if CS_ENABLE_STDIO
#include
#endif
#ifndef CS_ENABLE_DEBUG
#define CS_ENABLE_DEBUG 0
#endif
#ifndef CS_LOG_ENABLE_TS_DIFF
#define CS_LOG_ENABLE_TS_DIFF 0
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
enum cs_log_level {
LL_NONE = -1,
LL_ERROR = 0,
LL_WARN = 1,
LL_INFO = 2,
LL_DEBUG = 3,
LL_VERBOSE_DEBUG = 4,
_LL_MIN = -2,
_LL_MAX = 5,
};
/* Set log level. */
void cs_log_set_level(enum cs_log_level level);
/* Set log filter. NULL (a default) logs everything. */
void cs_log_set_filter(const char *source_file_name);
int cs_log_print_prefix(enum cs_log_level level, const char *func,
const char *filename);
extern enum cs_log_level cs_log_threshold;
#if CS_ENABLE_STDIO
void cs_log_set_file(FILE *file);
void cs_log_printf(const char *fmt, ...)
#ifdef __GNUC__
__attribute__((format(printf, 1, 2)))
#endif
;
#define LOG(l, x) \
do { \
if (cs_log_print_prefix(l, __func__, __FILE__)) cs_log_printf x; \
} while (0)
#ifndef CS_NDEBUG
#define DBG(x) LOG(LL_VERBOSE_DEBUG, x)
#else /* NDEBUG */
#define DBG(x)
#endif
#else /* CS_ENABLE_STDIO */
#define LOG(l, x)
#define DBG(x)
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* CS_COMMON_CS_DBG_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/cs_md5.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_MD5_H_
#define CS_COMMON_MD5_H_
/* Amalgamated: #include "common/platform.h" */
#ifndef CS_DISABLE_MD5
#define CS_DISABLE_MD5 0
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct {
uint32_t buf[4];
uint32_t bits[2];
unsigned char in[64];
} cs_md5_ctx;
void cs_md5_init(cs_md5_ctx *c);
void cs_md5_update(cs_md5_ctx *c, const unsigned char *data, size_t len);
void cs_md5_final(unsigned char *md, cs_md5_ctx *c);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* CS_COMMON_MD5_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/cs_endian.h"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_CS_ENDIAN_H_
#define CS_COMMON_CS_ENDIAN_H_
#ifdef __cplusplus
extern "C" {
#endif
/*
* clang with std=-c99 uses __LITTLE_ENDIAN, by default
* while for ex, RTOS gcc - LITTLE_ENDIAN, by default
* it depends on __USE_BSD, but let's have everything
*/
#if !defined(BYTE_ORDER) && defined(__BYTE_ORDER)
#define BYTE_ORDER __BYTE_ORDER
#ifndef LITTLE_ENDIAN
#define LITTLE_ENDIAN __LITTLE_ENDIAN
#endif /* LITTLE_ENDIAN */
#ifndef BIG_ENDIAN
#define BIG_ENDIAN __LITTLE_ENDIAN
#endif /* BIG_ENDIAN */
#endif /* BYTE_ORDER */
#ifdef __cplusplus
}
#endif
#endif /* CS_COMMON_CS_ENDIAN_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/cs_sha1.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_SHA1_H_
#define CS_COMMON_SHA1_H_
#ifndef CS_DISABLE_SHA1
#define CS_DISABLE_SHA1 0
#endif
#if !CS_DISABLE_SHA1
/* Amalgamated: #include "common/platform.h" */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct {
uint32_t state[5];
uint32_t count[2];
unsigned char buffer[64];
} cs_sha1_ctx;
void cs_sha1_init(cs_sha1_ctx *);
void cs_sha1_update(cs_sha1_ctx *, const unsigned char *data, uint32_t len);
void cs_sha1_final(unsigned char digest[20], cs_sha1_ctx *);
void cs_hmac_sha1(const unsigned char *key, size_t key_len,
const unsigned char *text, size_t text_len,
unsigned char out[20]);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* CS_DISABLE_SHA1 */
#endif /* CS_COMMON_SHA1_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/cs_dirent.h"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_CS_DIRENT_H_
#define CS_COMMON_CS_DIRENT_H_
#include
/* Amalgamated: #include "common/platform.h" */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#ifdef CS_DEFINE_DIRENT
typedef struct { int dummy; } DIR;
struct dirent {
int d_ino;
#ifdef _WIN32
char d_name[MAX_PATH];
#else
/* TODO(rojer): Use PATH_MAX but make sure it's sane on every platform */
char d_name[256];
#endif
};
DIR *opendir(const char *dir_name);
int closedir(DIR *dir);
struct dirent *readdir(DIR *dir);
#endif /* CS_DEFINE_DIRENT */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* CS_COMMON_CS_DIRENT_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/cs_file.h"
#endif
/*
* Copyright (c) 2015 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_CS_FILE_H_
#define CS_COMMON_CS_FILE_H_
/* Amalgamated: #include "common/platform.h" */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/*
* Read whole file `path` in memory. It is responsibility of the caller
* to `free()` allocated memory. File content is guaranteed to be
* '\0'-terminated. File size is returned in `size` variable, which does not
* count terminating `\0`.
* Return: allocated memory, or NULL on error.
*/
char *cs_read_file(const char *path, size_t *size);
#ifdef CS_MMAP
char *cs_mmap_file(const char *path, size_t *size);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* CS_COMMON_CS_FILE_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/coroutine.h"
#endif
/*
* Copyright (c) 2015 Cesanta Software Limited
* All rights reserved
*/
/*
* Module that provides generic macros and functions to implement "coroutines",
* i.e. C code that uses `mbuf` as a stack for function calls.
*
* More info: see the design doc: https://goo.gl/kfcG61
*/
#ifndef CS_COMMON_COROUTINE_H_
#define CS_COMMON_COROUTINE_H_
/* Amalgamated: #include "common/mbuf.h" */
/* Amalgamated: #include "common/platform.h" */
#ifdef __cplusplus
extern "C" {
#endif
/* user-defined union, this module only operates on the pointer */
union user_arg_ret;
/*
* Type that represents size of local function variables. We assume we'll never
* need more than 255 bytes of stack frame.
*/
typedef uint8_t cr_locals_size_t;
/*
* Descriptor of a single function; const array of such descriptors should
* be given to `cr_context_init()`
*/
struct cr_func_desc {
/*
* Size of the function's data that should be stored on stack.
*
* NOTE: you should use `CR_LOCALS_SIZEOF(your_type)` instead of `sizeof()`,
* since this value should be aligned by the word boundary, and
* `CR_LOCALS_SIZEOF()` takes care of this.
*/
cr_locals_size_t locals_size;
};
enum cr_status {
CR_RES__OK,
CR_RES__OK_YIELDED,
CR_RES__ERR_STACK_OVERFLOW,
/* Underflow can only be caused by memory corruption or bug in CR */
CR_RES__ERR_STACK_DATA_UNDERFLOW,
/* Underflow can only be caused by memory corruption or bug in CR */
CR_RES__ERR_STACK_CALL_UNDERFLOW,
CR_RES__ERR_UNCAUGHT_EXCEPTION,
};
/* Context of the coroutine engine */
struct cr_ctx {
/*
* id of the next "function" to call. If no function is going to be called,
* it's CR_FID__NONE.
*/
uint8_t called_fid;
/*
* when `called_fid` is not `CR_FID__NONE`, this field holds called
* function's stack frame size
*/
size_t call_locals_size;
/*
* when `called_fid` is not `CR_FID__NONE`, this field holds called
* function's arguments size
*/
size_t call_arg_size;
/*
* pointer to the current function's locals.
* Needed to make `CR_CUR_LOCALS_PT()` fast.
*/
uint8_t *p_cur_func_locals;
/* data stack */
struct mbuf stack_data;
/* return stack */
struct mbuf stack_ret;
/* index of the current fid + 1 in return stack */
size_t cur_fid_idx;
/* pointer to the array of function descriptors */
const struct cr_func_desc *p_func_descrs;
/* thrown exception. If nothing is currently thrown, it's `CR_EXC_ID__NONE` */
uint8_t thrown_exc;
/* status: normally, it's `CR_RES__OK` */
enum cr_status status;
/*
* pointer to user-dependent union of arguments for all functions, as well as
* return values, yielded and resumed values.
*/
union user_arg_ret *p_arg_retval;
/* true if currently running function returns */
unsigned need_return : 1;
/* true if currently running function yields */
unsigned need_yield : 1;
#if defined(CR_TRACK_MAX_STACK_LEN)
size_t stack_data_max_len;
size_t stack_ret_max_len;
#endif
};
/*
* User's enum with function ids should use items of this one like this:
*
* enum my_func_id {
* my_func_none = CR_FID__NONE,
*
* my_foo = CR_FID__USER,
* my_foo1,
* my_foo2,
*
* my_bar,
* my_bar1,
* };
*
*/
enum cr_fid {
CR_FID__NONE,
CR_FID__USER,
/* for internal usage only */
CR_FID__TRY_MARKER = 0xff,
};
/*
* User's enum with exception ids should use items of this one like this:
*
* enum my_exc_id {
* MY_EXC_ID__FIRST = CR_EXC_ID__USER,
* MY_EXC_ID__SECOND,
* MY_EXC_ID__THIRD,
* };
*/
enum cr_exc_id {
CR_EXC_ID__NONE,
CR_EXC_ID__USER,
};
/*
* A type whose size is a special case for macros `CR_LOCALS_SIZEOF()` and
* `CR_ARG_SIZEOF()` : it is assumed as zero size.
*
* This hackery is needed because empty structs (that would yield sizeof 0) are
* illegal in plain C.
*/
typedef struct { uint8_t _dummy[((cr_locals_size_t) -1)]; } cr_zero_size_type_t;
/*
* To be used in dispatcher switch: depending on the "fid" (function id), we
* jump to the appropriate label.
*/
#define CR_DEFINE_ENTRY_POINT(fid) \
case fid: \
goto fid
/*
* Returns lvalue: id of the currently active "function". It just takes the id
* from the appropriate position of the "stack".
*
* Client code only needs it in dispatcher switch.
*/
#define CR_CURR_FUNC_C(p_ctx) \
*(((cr_locals_size_t *) (p_ctx)->stack_ret.buf) + (p_ctx)->cur_fid_idx - 1)
/*
* Prepare context for calling first function.
*
* Should be used outside of the exec loop, right after initializing
* context with `cr_context_init()`
*
* `call_fid`: id of the function to be called
*/
#define CR_FIRST_CALL_PREPARE_C(p_ctx, call_fid) \
_CR_CALL_PREPARE(p_ctx, call_fid, CR_LOCALS_SIZEOF(call_fid##_locals_t), \
CR_ARG_SIZEOF(call_fid##_arg_t), CR_FID__NONE)
/*
* Call "function" with id `call_fid`: uses `_CR_CALL_PREPARE()` to prepare
* stuff, and then jumps to the `_cr_iter_begin`, which will perform all
* necessary bookkeeping.
*
* Should be used from eval loop only.
*
* `local_ret_fid`: id of the label right after the function call (where
* currently running function will be resumed later)
*/
#define CR_CALL_C(p_ctx, call_fid, local_ret_fid) \
do { \
_CR_CALL_PREPARE(p_ctx, call_fid, CR_LOCALS_SIZEOF(call_fid##_locals_t), \
CR_ARG_SIZEOF(call_fid##_arg_t), local_ret_fid); \
goto _cr_iter_begin; \
local_ret_fid: \
/* we'll get here when called function returns */ \
; \
} while (0)
/*
* "Return" the value `retval` from the current "function" with id `cur_fid`.
* You have to specify `cur_fid` since different functions may have different
* return types.
*
* Should be used from eval loop only.
*/
#define CR_RETURN_C(p_ctx, cur_fid, retval) \
do { \
/* copy ret to arg_retval */ \
CR_ARG_RET_PT_C(p_ctx)->ret.cur_fid = (retval); \
/* set need_return flag */ \
(p_ctx)->need_return = 1; \
goto _cr_iter_begin; \
} while (0)
/*
* Same as `CR_RETURN_C`, but without any return value
*/
#define CR_RETURN_VOID_C(p_ctx) \
do { \
/* set need_return flag */ \
(p_ctx)->need_return = 1; \
goto _cr_iter_begin; \
} while (0)
/*
* Yield with the value `value`. It will be set just by the assigment operator
* in the `yielded` field of the `union user_arg_ret`.
*
* `local_ret_fid`: id of the label right after the yielding (where currently
* running function will be resumed later)
*
*/
#define CR_YIELD_C(p_ctx, value, local_ret_fid) \
do { \
/* copy ret to arg_retval */ \
CR_ARG_RET_PT_C(p_ctx)->yielded = (value); \
/* set need_yield flag */ \
(p_ctx)->need_yield = 1; \
\
/* adjust return func id */ \
CR_CURR_FUNC_C(p_ctx) = (local_ret_fid); \
\
goto _cr_iter_begin; \
local_ret_fid: \
/* we'll get here when the machine will be resumed */ \
; \
} while (0)
/*
* Prepare context for resuming with the given value. After using this
* macro, you need to call your user-dependent exec function.
*/
#define CR_RESUME_C(p_ctx, value) \
do { \
if ((p_ctx)->status == CR_RES__OK_YIELDED) { \
CR_ARG_RET_PT_C(p_ctx)->resumed = (value); \
(p_ctx)->status = CR_RES__OK; \
} \
} while (0)
/*
* Evaluates to the yielded value (value given to `CR_YIELD_C()`)
*/
#define CR_YIELDED_C(p_ctx) (CR_ARG_RET_PT_C(p_ctx)->yielded)
/*
* Evaluates to the value given to `CR_RESUME_C()`
*/
#define CR_RESUMED_C(p_ctx) (CR_ARG_RET_PT_C(p_ctx)->resumed)
/*
* Beginning of the try-catch block.
*
* Should be used in eval loop only.
*
* `first_catch_fid`: function id of the first catch block.
*/
#define CR_TRY_C(p_ctx, first_catch_fid) \
do { \
_CR_STACK_RET_ALLOC((p_ctx), _CR_TRY_SIZE); \
/* update pointer to current function's locals (may be invalidated) */ \
_CR_CUR_FUNC_LOCALS_UPD(p_ctx); \
/* */ \
_CR_TRY_MARKER(p_ctx) = CR_FID__TRY_MARKER; \
_CR_TRY_CATCH_FID(p_ctx) = (first_catch_fid); \
} while (0)
/*
* Beginning of the individual catch block (and the end of the previous one, if
* any)
*
* Should be used in eval loop only.
*
* `exc_id`: exception id to catch
*
* `catch_fid`: function id of this catch block.
*
* `next_catch_fid`: function id of the next catch block (or of the
* `CR_ENDCATCH()`)
*/
#define CR_CATCH_C(p_ctx, exc_id, catch_fid, next_catch_fid) \
catch_fid: \
do { \
if ((p_ctx)->thrown_exc != (exc_id)) { \
goto next_catch_fid; \
} \
(p_ctx)->thrown_exc = CR_EXC_ID__NONE; \
} while (0)
/*
* End of all catch blocks.
*
* Should be used in eval loop only.
*
* `endcatch_fid`: function id of this endcatch.
*/
#define CR_ENDCATCH_C(p_ctx, endcatch_fid) \
endcatch_fid: \
do { \
(p_ctx)->stack_ret.len -= _CR_TRY_SIZE; \
/* if we still have non-handled exception, continue unwinding "stack" */ \
if ((p_ctx)->thrown_exc != CR_EXC_ID__NONE) { \
goto _cr_iter_begin; \
} \
} while (0)
/*
* Throw exception.
*
* Should be used from eval loop only.
*
* `exc_id`: exception id to throw
*/
#define CR_THROW_C(p_ctx, exc_id) \
do { \
assert((enum cr_exc_id)(exc_id) != CR_EXC_ID__NONE); \
/* clear need_return flag */ \
(p_ctx)->thrown_exc = (exc_id); \
goto _cr_iter_begin; \
} while (0)
/*
* Get latest returned value from the given "function".
*
* `fid`: id of the function which returned value. Needed to ret value value
* from the right field in the `(p_ctx)->arg_retval.ret` (different functions
* may have different return types)
*/
#define CR_RETURNED_C(p_ctx, fid) (CR_ARG_RET_PT_C(p_ctx)->ret.fid)
/*
* Get currently thrown exception id. If nothing is being thrown at the moment,
* `CR_EXC_ID__NONE` is returned
*/
#define CR_THROWN_C(p_ctx) ((p_ctx)->thrown_exc)
/*
* Like `sizeof()`, but it always evaluates to the multiple of `sizeof(void *)`
*
* It should be used for (struct cr_func_desc)::locals_size
*
* NOTE: instead of checking `sizeof(type) <= ((cr_locals_size_t) -1)`, I'd
* better put the calculated value as it is, and if it overflows, then compiler
* will generate warning, and this would help us to reveal our mistake. But
* unfortunately, clang *always* generates this warning (even if the whole
* expression yields 0), so we have to apply a bit more of dirty hacks here.
*/
#define CR_LOCALS_SIZEOF(type) \
((sizeof(type) == sizeof(cr_zero_size_type_t)) \
? 0 \
: (sizeof(type) <= ((cr_locals_size_t) -1) \
? ((cr_locals_size_t)(((sizeof(type)) + (sizeof(void *) - 1)) & \
(~(sizeof(void *) - 1)))) \
: ((cr_locals_size_t) -1)))
#define CR_ARG_SIZEOF(type) \
((sizeof(type) == sizeof(cr_zero_size_type_t)) ? 0 : sizeof(type))
/*
* Returns pointer to the current function's stack locals, and casts to given
* type.
*
* Typical usage might look as follows:
*
* #undef L
* #define L CR_CUR_LOCALS_PT(p_ctx, struct my_foo_locals)
*
* Then, assuming `struct my_foo_locals` has the field `bar`, we can access it
* like this:
*
* L->bar
*/
#define CR_CUR_LOCALS_PT_C(p_ctx, type) ((type *) ((p_ctx)->p_cur_func_locals))
/*
* Returns pointer to the user-defined union of arguments and return values:
* `union user_arg_ret`
*/
#define CR_ARG_RET_PT_C(p_ctx) ((p_ctx)->p_arg_retval)
#define CR_ARG_RET_PT() CR_ARG_RET_PT_C(p_ctx)
#define CR_CUR_LOCALS_PT(type) CR_CUR_LOCALS_PT_C(p_ctx, type)
#define CR_CURR_FUNC() CR_CURR_FUNC_C(p_ctx)
#define CR_CALL(call_fid, local_ret_fid) \
CR_CALL_C(p_ctx, call_fid, local_ret_fid)
#define CR_RETURN(cur_fid, retval) CR_RETURN_C(p_ctx, cur_fid, retval)
#define CR_RETURN_VOID() CR_RETURN_VOID_C(p_ctx)
#define CR_RETURNED(fid) CR_RETURNED_C(p_ctx, fid)
#define CR_YIELD(value, local_ret_fid) CR_YIELD_C(p_ctx, value, local_ret_fid)
#define CR_YIELDED() CR_YIELDED_C(p_ctx)
#define CR_RESUME(value) CR_RESUME_C(p_ctx, value)
#define CR_RESUMED() CR_RESUMED_C(p_ctx)
#define CR_TRY(catch_name) CR_TRY_C(p_ctx, catch_name)
#define CR_CATCH(exc_id, catch_name, next_catch_name) \
CR_CATCH_C(p_ctx, exc_id, catch_name, next_catch_name)
#define CR_ENDCATCH(endcatch_name) CR_ENDCATCH_C(p_ctx, endcatch_name)
#define CR_THROW(exc_id) CR_THROW_C(p_ctx, exc_id)
/* Private macros {{{ */
#define _CR_CUR_FUNC_LOCALS_UPD(p_ctx) \
do { \
(p_ctx)->p_cur_func_locals = (uint8_t *) (p_ctx)->stack_data.buf + \
(p_ctx)->stack_data.len - \
_CR_CURR_FUNC_LOCALS_SIZE(p_ctx); \
} while (0)
/*
* Size of the stack needed for each try-catch block.
* Use `_CR_TRY_MARKER()` and `_CR_TRY_CATCH_FID()` to get/set parts.
*/
#define _CR_TRY_SIZE 2 /*CR_FID__TRY_MARKER, catch_fid*/
/*
* Evaluates to lvalue where `CR_FID__TRY_MARKER` should be stored
*/
#define _CR_TRY_MARKER(p_ctx) \
*(((uint8_t *) (p_ctx)->stack_ret.buf) + (p_ctx)->stack_ret.len - 1)
/*
* Evaluates to lvalue where `catch_fid` should be stored
*/
#define _CR_TRY_CATCH_FID(p_ctx) \
*(((uint8_t *) (p_ctx)->stack_ret.buf) + (p_ctx)->stack_ret.len - 2)
#define _CR_CURR_FUNC_LOCALS_SIZE(p_ctx) \
((p_ctx)->p_func_descrs[CR_CURR_FUNC_C(p_ctx)].locals_size)
/*
* Prepare context for calling next function.
*
* See comments for `CR_CALL()` macro.
*/
#define _CR_CALL_PREPARE(p_ctx, _call_fid, _locals_size, _arg_size, \
local_ret_fid) \
do { \
/* adjust return func id */ \
CR_CURR_FUNC_C(p_ctx) = (local_ret_fid); \
\
/* set called_fid */ \
(p_ctx)->called_fid = (_call_fid); \
\
/* set sizes: locals and arg */ \
(p_ctx)->call_locals_size = (_locals_size); \
(p_ctx)->call_arg_size = (_arg_size); \
} while (0)
#define _CR_STACK_DATA_OVF_CHECK(p_ctx, inc) (0)
#define _CR_STACK_DATA_UND_CHECK(p_ctx, dec) ((p_ctx)->stack_data.len < (dec))
#define _CR_STACK_RET_OVF_CHECK(p_ctx, inc) (0)
#define _CR_STACK_RET_UND_CHECK(p_ctx, dec) ((p_ctx)->stack_ret.len < (dec))
#define _CR_STACK_FID_OVF_CHECK(p_ctx, inc) (0)
#define _CR_STACK_FID_UND_CHECK(p_ctx, dec) ((p_ctx)->cur_fid_idx < (dec))
#if defined(CR_TRACK_MAX_STACK_LEN)
#define _CR_STACK_DATA_ALLOC(p_ctx, inc) \
do { \
mbuf_append(&((p_ctx)->stack_data), NULL, (inc)); \
if ((p_ctx)->stack_data_max_len < (p_ctx)->stack_data.len) { \
(p_ctx)->stack_data_max_len = (p_ctx)->stack_data.len; \
} \
} while (0)
#define _CR_STACK_RET_ALLOC(p_ctx, inc) \
do { \
mbuf_append(&((p_ctx)->stack_ret), NULL, (inc)); \
if ((p_ctx)->stack_ret_max_len < (p_ctx)->stack_ret.len) { \
(p_ctx)->stack_ret_max_len = (p_ctx)->stack_ret.len; \
} \
} while (0)
#else
#define _CR_STACK_DATA_ALLOC(p_ctx, inc) \
do { \
mbuf_append(&((p_ctx)->stack_data), NULL, (inc)); \
} while (0)
#define _CR_STACK_RET_ALLOC(p_ctx, inc) \
do { \
mbuf_append(&((p_ctx)->stack_ret), NULL, (inc)); \
} while (0)
#endif
#define _CR_STACK_DATA_FREE(p_ctx, dec) \
do { \
(p_ctx)->stack_data.len -= (dec); \
} while (0)
#define _CR_STACK_RET_FREE(p_ctx, dec) \
do { \
(p_ctx)->stack_ret.len -= (dec); \
} while (0)
#define _CR_STACK_FID_ALLOC(p_ctx, inc) \
do { \
(p_ctx)->cur_fid_idx += (inc); \
} while (0)
#define _CR_STACK_FID_FREE(p_ctx, dec) \
do { \
(p_ctx)->cur_fid_idx -= (dec); \
} while (0)
/* }}} */
/*
* Should be used in eval loop right after `_cr_iter_begin:` label
*/
enum cr_status cr_on_iter_begin(struct cr_ctx *p_ctx);
/*
* Initialize context `p_ctx`.
*
* `p_arg_retval`: pointer to the user-defined `union user_arg_ret`
*
* `p_func_descrs`: array of all user function descriptors
*/
void cr_context_init(struct cr_ctx *p_ctx, union user_arg_ret *p_arg_retval,
size_t arg_retval_size,
const struct cr_func_desc *p_func_descrs);
/*
* free resources occupied by context (at least, "stack" arrays)
*/
void cr_context_free(struct cr_ctx *p_ctx);
#ifdef __cplusplus
}
#endif
#endif /* CS_COMMON_COROUTINE_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/features_profiles.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_FEATURES_PROFILES_H_
#define CS_V7_SRC_FEATURES_PROFILES_H_
#define V7_BUILD_PROFILE_MINIMAL 1
#define V7_BUILD_PROFILE_MEDIUM 2
#define V7_BUILD_PROFILE_FULL 3
#ifndef V7_BUILD_PROFILE
#define V7_BUILD_PROFILE V7_BUILD_PROFILE_FULL
#endif
#endif /* CS_V7_SRC_FEATURES_PROFILES_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/features_minimal.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "v7/src/features_profiles.h" */
#if V7_BUILD_PROFILE == V7_BUILD_PROFILE_MINIMAL
/* This space is intentionally left blank. */
#endif /* CS_V7_SRC_FEATURES_MINIMAL_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/features_medium.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "v7/src/features_profiles.h" */
#if V7_BUILD_PROFILE == V7_BUILD_PROFILE_MEDIUM
#define V7_ENABLE__Date 1
#define V7_ENABLE__Date__now 1
#define V7_ENABLE__Date__UTC 1
#define V7_ENABLE__Math 1
#define V7_ENABLE__Math__atan2 1
#define V7_ENABLE__RegExp 1
#endif /* CS_V7_SRC_FEATURES_MEDIUM_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/features_full.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_FEATURES_FULL_H_
#define CS_V7_SRC_FEATURES_FULL_H_
/* Amalgamated: #include "v7/src/features_profiles.h" */
#if V7_BUILD_PROFILE == V7_BUILD_PROFILE_FULL
/*
* DO NOT EDIT.
* This file is generated by scripts/gen-features-full.pl.
*/
#ifndef CS_ENABLE_UTF8 /* ifdef-ok */
#define CS_ENABLE_UTF8 1
#endif
#define V7_ENABLE__Array__reduce 1
#define V7_ENABLE__Blob 1
#define V7_ENABLE__Date 1
#define V7_ENABLE__Date__UTC 1
#define V7_ENABLE__Date__getters 1
#define V7_ENABLE__Date__now 1
#define V7_ENABLE__Date__parse 1
#define V7_ENABLE__Date__setters 1
#define V7_ENABLE__Date__toJSON 1
#define V7_ENABLE__Date__toLocaleString 1
#define V7_ENABLE__Date__toString 1
#define V7_ENABLE__File__list 1
#define V7_ENABLE__File__require 1
#define V7_ENABLE__Function__bind 1
#define V7_ENABLE__Function__call 1
#define V7_ENABLE__Math 1
#define V7_ENABLE__Math__abs 1
#define V7_ENABLE__Math__acos 1
#define V7_ENABLE__Math__asin 1
#define V7_ENABLE__Math__atan 1
#define V7_ENABLE__Math__atan2 1
#define V7_ENABLE__Math__ceil 1
#define V7_ENABLE__Math__constants 1
#define V7_ENABLE__Math__cos 1
#define V7_ENABLE__Math__exp 1
#define V7_ENABLE__Math__floor 1
#define V7_ENABLE__Math__log 1
#define V7_ENABLE__Math__max 1
#define V7_ENABLE__Math__min 1
#define V7_ENABLE__Math__pow 1
#define V7_ENABLE__Math__random 1
#define V7_ENABLE__Math__round 1
#define V7_ENABLE__Math__sin 1
#define V7_ENABLE__Math__sqrt 1
#define V7_ENABLE__Math__tan 1
#define V7_ENABLE__Memory__stats 1
#define V7_ENABLE__NUMBER__NEGATIVE_INFINITY 1
#define V7_ENABLE__NUMBER__POSITIVE_INFINITY 1
#define V7_ENABLE__Object__create 1
#define V7_ENABLE__Object__defineProperties 1
#define V7_ENABLE__Object__getOwnPropertyDescriptor 1
#define V7_ENABLE__Object__getOwnPropertyNames 1
#define V7_ENABLE__Object__getPrototypeOf 1
#define V7_ENABLE__Object__hasOwnProperty 1
#define V7_ENABLE__Object__isExtensible 1
#define V7_ENABLE__Object__isFrozen 1
#define V7_ENABLE__Object__isPrototypeOf 1
#define V7_ENABLE__Object__isSealed 1
#define V7_ENABLE__Object__keys 1
#define V7_ENABLE__Object__preventExtensions 1
#define V7_ENABLE__Object__propertyIsEnumerable 1
#define V7_ENABLE__Proxy 1
#define V7_ENABLE__RegExp 1
#define V7_ENABLE__StackTrace 1
#define V7_ENABLE__String__localeCompare 1
#define V7_ENABLE__String__localeLowerCase 1
#define V7_ENABLE__String__localeUpperCase 1
#endif /* V7_BUILD_PROFILE == V7_BUILD_PROFILE_FULL */
#endif /* CS_V7_SRC_FEATURES_FULL_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/features_all.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_FEATURES_ALL_H_
#define CS_V7_SRC_FEATURES_ALL_H_
/*
* DO NOT EDIT.
* This file is generated by scripts/gen-features-all.pl.
*/
#ifndef V7_ENABLE__Array__reduce
#define V7_ENABLE__Array__reduce 0
#endif
#ifndef V7_ENABLE__Blob
#define V7_ENABLE__Blob 0
#endif
#ifndef V7_ENABLE__Date
#define V7_ENABLE__Date 0
#endif
#ifndef V7_ENABLE__Date__UTC
#define V7_ENABLE__Date__UTC 0
#endif
#ifndef V7_ENABLE__Date__getters
#define V7_ENABLE__Date__getters 0
#endif
#ifndef V7_ENABLE__Date__now
#define V7_ENABLE__Date__now 0
#endif
#ifndef V7_ENABLE__Date__parse
#define V7_ENABLE__Date__parse 0
#endif
#ifndef V7_ENABLE__Date__setters
#define V7_ENABLE__Date__setters 0
#endif
#ifndef V7_ENABLE__Date__toJSON
#define V7_ENABLE__Date__toJSON 0
#endif
#ifndef V7_ENABLE__Date__toLocaleString
#define V7_ENABLE__Date__toLocaleString 0
#endif
#ifndef V7_ENABLE__Date__toString
#define V7_ENABLE__Date__toString 0
#endif
#ifndef V7_ENABLE__File__list
#define V7_ENABLE__File__list 0
#endif
#ifndef V7_ENABLE__File__require
#define V7_ENABLE__File__require 0
#endif
#ifndef V7_ENABLE__Function__bind
#define V7_ENABLE__Function__bind 0
#endif
#ifndef V7_ENABLE__Function__call
#define V7_ENABLE__Function__call 0
#endif
#ifndef V7_ENABLE__Math
#define V7_ENABLE__Math 0
#endif
#ifndef V7_ENABLE__Math__abs
#define V7_ENABLE__Math__abs 0
#endif
#ifndef V7_ENABLE__Math__acos
#define V7_ENABLE__Math__acos 0
#endif
#ifndef V7_ENABLE__Math__asin
#define V7_ENABLE__Math__asin 0
#endif
#ifndef V7_ENABLE__Math__atan
#define V7_ENABLE__Math__atan 0
#endif
#ifndef V7_ENABLE__Math__atan2
#define V7_ENABLE__Math__atan2 0
#endif
#ifndef V7_ENABLE__Math__ceil
#define V7_ENABLE__Math__ceil 0
#endif
#ifndef V7_ENABLE__Math__constants
#define V7_ENABLE__Math__constants 0
#endif
#ifndef V7_ENABLE__Math__cos
#define V7_ENABLE__Math__cos 0
#endif
#ifndef V7_ENABLE__Math__exp
#define V7_ENABLE__Math__exp 0
#endif
#ifndef V7_ENABLE__Math__floor
#define V7_ENABLE__Math__floor 0
#endif
#ifndef V7_ENABLE__Math__log
#define V7_ENABLE__Math__log 0
#endif
#ifndef V7_ENABLE__Math__max
#define V7_ENABLE__Math__max 0
#endif
#ifndef V7_ENABLE__Math__min
#define V7_ENABLE__Math__min 0
#endif
#ifndef V7_ENABLE__Math__pow
#define V7_ENABLE__Math__pow 0
#endif
#ifndef V7_ENABLE__Math__random
#define V7_ENABLE__Math__random 0
#endif
#ifndef V7_ENABLE__Math__round
#define V7_ENABLE__Math__round 0
#endif
#ifndef V7_ENABLE__Math__sin
#define V7_ENABLE__Math__sin 0
#endif
#ifndef V7_ENABLE__Math__sqrt
#define V7_ENABLE__Math__sqrt 0
#endif
#ifndef V7_ENABLE__Math__tan
#define V7_ENABLE__Math__tan 0
#endif
#ifndef V7_ENABLE__Memory__stats
#define V7_ENABLE__Memory__stats 0
#endif
#ifndef V7_ENABLE__NUMBER__NEGATIVE_INFINITY
#define V7_ENABLE__NUMBER__NEGATIVE_INFINITY 0
#endif
#ifndef V7_ENABLE__NUMBER__POSITIVE_INFINITY
#define V7_ENABLE__NUMBER__POSITIVE_INFINITY 0
#endif
#ifndef V7_ENABLE__Object__create
#define V7_ENABLE__Object__create 0
#endif
#ifndef V7_ENABLE__Object__defineProperties
#define V7_ENABLE__Object__defineProperties 0
#endif
#ifndef V7_ENABLE__Object__getOwnPropertyDescriptor
#define V7_ENABLE__Object__getOwnPropertyDescriptor 0
#endif
#ifndef V7_ENABLE__Object__getOwnPropertyNames
#define V7_ENABLE__Object__getOwnPropertyNames 0
#endif
#ifndef V7_ENABLE__Object__getPrototypeOf
#define V7_ENABLE__Object__getPrototypeOf 0
#endif
#ifndef V7_ENABLE__Object__hasOwnProperty
#define V7_ENABLE__Object__hasOwnProperty 0
#endif
#ifndef V7_ENABLE__Object__isExtensible
#define V7_ENABLE__Object__isExtensible 0
#endif
#ifndef V7_ENABLE__Object__isFrozen
#define V7_ENABLE__Object__isFrozen 0
#endif
#ifndef V7_ENABLE__Object__isPrototypeOf
#define V7_ENABLE__Object__isPrototypeOf 0
#endif
#ifndef V7_ENABLE__Object__isSealed
#define V7_ENABLE__Object__isSealed 0
#endif
#ifndef V7_ENABLE__Object__keys
#define V7_ENABLE__Object__keys 0
#endif
#ifndef V7_ENABLE__Object__preventExtensions
#define V7_ENABLE__Object__preventExtensions 0
#endif
#ifndef V7_ENABLE__Object__propertyIsEnumerable
#define V7_ENABLE__Object__propertyIsEnumerable 0
#endif
#ifndef V7_ENABLE__Proxy
#define V7_ENABLE__Proxy 0
#endif
#ifndef V7_ENABLE__RegExp
#define V7_ENABLE__RegExp 0
#endif
#ifndef V7_ENABLE__StackTrace
#define V7_ENABLE__StackTrace 0
#endif
#ifndef V7_ENABLE__String__localeCompare
#define V7_ENABLE__String__localeCompare 0
#endif
#ifndef V7_ENABLE__String__localeLowerCase
#define V7_ENABLE__String__localeLowerCase 0
#endif
#ifndef V7_ENABLE__String__localeUpperCase
#define V7_ENABLE__String__localeUpperCase 0
#endif
#endif /* CS_V7_SRC_FEATURES_ALL_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/v7_features.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_V7_FEATURES_H_
#define CS_V7_SRC_V7_FEATURES_H_
/* Only one will actually be used based on V7_BUILD_PROFILE. */
/* Amalgamated: #include "v7/src/features_minimal.h" */
/* Amalgamated: #include "v7/src/features_medium.h" */
/* Amalgamated: #include "v7/src/features_full.h" */
/* All the features will be default-defined to 0. */
/* Amalgamated: #include "v7/src/features_all.h" */
#ifndef V7_DISABLE_AST_TAG_NAMES
#define V7_DISABLE_AST_TAG_NAMES 0
#endif
#ifndef V7_DISABLE_GC
#define V7_DISABLE_GC 0
#endif
#ifndef V7_DISABLE_CALL_ERROR_CONTEXT
#define V7_DISABLE_CALL_ERROR_CONTEXT 0
#endif
#ifndef V7_DISABLE_FILENAMES
#define V7_DISABLE_FILENAMES 0
#endif
#ifndef V7_DISABLE_LINE_NUMBERS
#define V7_DISABLE_LINE_NUMBERS 0
#endif
#ifndef V7_DISABLE_STR_ALLOC_SEQ
#define V7_DISABLE_STR_ALLOC_SEQ 0
#endif
#ifndef V7_ENABLE_CALL_TRACE
#define V7_ENABLE_CALL_TRACE 0
#endif
#ifndef V7_ENABLE_CRYPTO
#define V7_ENABLE_CRYPTO 0
#endif
#ifndef V7_ENABLE_DENSE_ARRAYS
#define V7_ENABLE_DENSE_ARRAYS 0
#endif
#ifndef V7_ENABLE_ENTITY_IDS
#define V7_ENABLE_ENTITY_IDS 0
#endif
#ifndef V7_ENABLE_FILE
#define V7_ENABLE_FILE 0
#endif
#ifndef V7_ENABLE_FOOTPRINT_REPORT
#define V7_ENABLE_FOOTPRINT_REPORT 0
#endif
#ifndef V7_ENABLE_GC_CHECK
#define V7_ENABLE_GC_CHECK 0
#endif
#ifndef V7_ENABLE_JS_GETTERS
#define V7_ENABLE_JS_GETTERS 0
#endif
#ifndef V7_ENABLE_JS_SETTERS
#define V7_ENABLE_JS_SETTERS 0
#endif
#ifndef V7_ENABLE_STACK_TRACKING
#define V7_ENABLE_STACK_TRACKING 0
#endif
#ifndef V7_ENABLE_SOCKET
#define V7_ENABLE_SOCKET 0
#endif
#ifndef V7_AST_FORCE_LINE_NUMBERS
#define V7_AST_FORCE_LINE_NUMBERS 0
#endif
#ifndef V7_HEAPUSAGE_ENABLE
#define V7_HEAPUSAGE_ENABLE 0
#endif
#endif /* CS_V7_SRC_V7_FEATURES_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/platform.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_PLATFORM_H_
#define CS_V7_SRC_PLATFORM_H_
#ifdef __arm
#undef V7_ENABLE__Date
#define V7_ENABLE__Date 0
#endif
#endif /* CS_V7_SRC_PLATFORM_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/internal.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_INTERNAL_H_
#define CS_V7_SRC_INTERNAL_H_
/* Amalgamated: #include "v7/src/license.h" */
#ifndef FAST
#define FAST
#endif
#ifndef STATIC
#define STATIC
#endif
#ifndef ENDL
#define ENDL "\n"
#endif
/*
* In some compilers (watcom) NAN == NAN (and other comparisons) don't follow
* the rules of IEEE 754. Since we don't know a priori which compilers
* will generate correct code, we disable the fallback on selected platforms.
* TODO(mkm): selectively disable on clang/gcc once we test this out.
*/
#define V7_BROKEN_NAN
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200809L
#include
#ifndef NO_LIBC
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* Public API. Implemented in api.c */
/* Amalgamated: #include "common/platform.h" */
#ifdef V7_WINDOWS
#define vsnprintf _vsnprintf
#define snprintf _snprintf
/* VS2015 Update 1 has ISO C99 `isnan` and `isinf` defined in math.h */
#if _MSC_FULL_VER < 190023506
#define isnan(x) _isnan(x)
#define isinf(x) (!_finite(x))
#endif
#define __unused __pragma(warning(suppress : 4100))
typedef __int64 int64_t;
typedef int int32_t;
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
typedef unsigned char uint8_t;
/* For 64bit VisualStudio 2010 */
#ifndef _UINTPTR_T_DEFINED
typedef unsigned long uintptr_t;
#endif
#ifndef __func__
#define __func__ ""
#endif
#else
#include
#endif
/* Amalgamated: #include "v7/src/v7_features.h" */
/* Amalgamated: #include "v7/src/platform.h" */
/* MSVC6 doesn't have standard C math constants defined */
#ifndef M_E
#define M_E 2.71828182845904523536028747135266250
#endif
#ifndef M_LOG2E
#define M_LOG2E 1.44269504088896340735992468100189214
#endif
#ifndef M_LOG10E
#define M_LOG10E 0.434294481903251827651128918916605082
#endif
#ifndef M_LN2
#define M_LN2 0.693147180559945309417232121458176568
#endif
#ifndef M_LN10
#define M_LN10 2.30258509299404568401799145468436421
#endif
#ifndef M_PI
#define M_PI 3.14159265358979323846264338327950288
#endif
#ifndef M_SQRT2
#define M_SQRT2 1.41421356237309504880168872420969808
#endif
#ifndef M_SQRT1_2
#define M_SQRT1_2 0.707106781186547524400844362104849039
#endif
#ifndef NAN
extern double _v7_nan;
#define HAS_V7_NAN
#define NAN (_v7_nan)
#endif
#ifndef INFINITY
extern double _v7_infinity;
#define HAS_V7_INFINITY
#define INFINITY (_v7_infinity)
#endif
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif
#if V7_ENABLE_GC_CHECK || defined(V7_STACK_GUARD_MIN_SIZE) || \
V7_ENABLE_STACK_TRACKING || V7_ENABLE_CALL_TRACE
/* Need to enable GCC/clang instrumentation */
#define V7_CYG_PROFILE_ON
#endif
#if defined(V7_CYG_PROFILE_ON)
extern struct v7 *v7_head;
#if defined(V7_STACK_GUARD_MIN_SIZE)
extern void *v7_sp_limit;
#endif
#endif
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
#endif
#define V7_STATIC_ASSERT(COND, MSG) \
typedef char static_assertion_##MSG[2 * (!!(COND)) - 1]
#define BUF_LEFT(size, used) (((size_t)(used) < (size)) ? ((size) - (used)) : 0)
#endif /* CS_V7_SRC_INTERNAL_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/core_public.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/*
* === Core
*/
#ifndef CS_V7_SRC_CORE_PUBLIC_H_
#define CS_V7_SRC_CORE_PUBLIC_H_
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200809L
#endif
/* Amalgamated: #include "v7/src/license.h" */
/* Amalgamated: #include "v7/src/v7_features.h" */
/* Amalgamated: #include "v7/src/platform.h" */
#include /* For size_t */
#include /* For FILE */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
/*
* TODO(dfrank) : improve amalgamation, so that we'll be able to include
* files here, and include common/platform.h
*
* For now, copy-pasting `WARN_UNUSED_RESULT` here
*/
#ifdef __GNUC__
#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
#define NOINSTR __attribute__((no_instrument_function))
#else
#define WARN_UNUSED_RESULT
#define NOINSTR
#endif
#define V7_VERSION "1.0"
#if (defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__)) || \
(defined(_MSC_VER) && _MSC_VER <= 1200)
#define V7_WINDOWS
#endif
#ifdef V7_WINDOWS
typedef unsigned __int64 uint64_t;
#else
#include
#endif
/* 64-bit value, used to store JS values */
typedef uint64_t v7_val_t;
/* JavaScript `null` value */
#define V7_NULL ((uint64_t) 0xfffe << 48)
/* JavaScript `undefined` value */
#define V7_UNDEFINED ((uint64_t) 0xfffd << 48)
/* This if-0 is a dirty workaround to force etags to pick `struct v7` */
#if 0
/* Opaque structure. V7 engine context. */
struct v7 {
/* ... */
};
#endif
struct v7;
/*
* Code which is returned by some of the v7 functions. If something other than
* `V7_OK` is returned from some function, the caller function typically should
* either immediately cleanup and return the code further, or handle the error.
*/
enum v7_err {
V7_OK,
V7_SYNTAX_ERROR,
V7_EXEC_EXCEPTION,
V7_AST_TOO_LARGE,
V7_INTERNAL_ERROR,
};
/* JavaScript -> C call interface */
WARN_UNUSED_RESULT
typedef enum v7_err(v7_cfunction_t)(struct v7 *v7, v7_val_t *res);
/* Create V7 instance */
struct v7 *v7_create(void);
/*
* Customizations of initial V7 state; used by `v7_create_opt()`.
*/
struct v7_create_opts {
size_t object_arena_size;
size_t function_arena_size;
size_t property_arena_size;
#ifdef V7_STACK_SIZE
void *c_stack_base;
#endif
#ifdef V7_FREEZE
/* if not NULL, dump JS heap after init */
char *freeze_file;
#endif
};
/*
* Like `v7_create()`, but allows to customize initial v7 state, see `struct
* v7_create_opts`.
*/
struct v7 *v7_create_opt(struct v7_create_opts opts);
/* Destroy V7 instance */
void v7_destroy(struct v7 *v7);
/* Return root level (`global`) object of the given V7 instance. */
v7_val_t v7_get_global(struct v7 *v);
/* Return current `this` object. */
v7_val_t v7_get_this(struct v7 *v);
/* Return current `arguments` array */
v7_val_t v7_get_arguments(struct v7 *v);
/* Return i-th argument */
v7_val_t v7_arg(struct v7 *v, unsigned long i);
/* Return the length of `arguments` */
unsigned long v7_argc(struct v7 *v7);
/*
* Tells the GC about a JS value variable/field owned
* by C code.
*
* User C code should own v7_val_t variables
* if the value's lifetime crosses any invocation
* to the v7 runtime that creates new objects or new
* properties and thus can potentially trigger GC.
*
* The registration of the variable prevents the GC from mistakenly treat
* the object as garbage. The GC might be triggered potentially
* allows the GC to update pointers
*
* User code should also explicitly disown the variables with v7_disown once
* it goes out of scope or the structure containing the v7_val_t field is freed.
*
* Example:
*
* ```
* struct v7_val cb;
* v7_own(v7, &cb);
* cb = v7_array_get(v7, args, 0);
* // do something with cb
* v7_disown(v7, &cb);
* ```
*/
void v7_own(struct v7 *v7, v7_val_t *v);
/*
* Returns 1 if value is found, 0 otherwise
*/
int v7_disown(struct v7 *v7, v7_val_t *v);
/*
* Enable or disable GC.
*
* Must be called before invoking v7_exec or v7_apply
* from within a cfunction unless you know what you're doing.
*
* GC is disabled during execution of cfunctions in order to simplify
* memory management of simple cfunctions.
* However executing even small snippets of JS code causes a lot of memory
* pressure. Enabling GC solves that but forces you to take care of the
* reachability of your temporary V7 v7_val_t variables, as the GC needs
* to know where they are since objects and strings can be either reclaimed
* or relocated during a GC pass.
*/
void v7_set_gc_enabled(struct v7 *v7, int enabled);
/*
* Set an optional C stack limit.
*
* It sets a flag that will cause the interpreter
* to throw an InterruptedError.
* It's safe to call it from signal handlers and ISRs
* on single threaded environments.
*/
void v7_interrupt(struct v7 *v7);
/* Returns last parser error message. TODO: rename it to `v7_get_error()` */
const char *v7_get_parser_error(struct v7 *v7);
#if V7_ENABLE_STACK_TRACKING
/*
* Available if only `V7_ENABLE_STACK_TRACKING` is defined.
*
* Stack metric id. See `v7_stack_stat()`
*/
enum v7_stack_stat_what {
/* max stack size consumed by `i_exec()` */
V7_STACK_STAT_EXEC,
/* max stack size consumed by `parse()` (which is called from `i_exec()`) */
V7_STACK_STAT_PARSER,
V7_STACK_STATS_CNT
};
/*
* Available if only `V7_ENABLE_STACK_TRACKING` is defined.
*
* Returns stack metric specified by the metric id `what`. See
* `v7_stack_stat_clean()`
*/
int v7_stack_stat(struct v7 *v7, enum v7_stack_stat_what what);
/*
* Available if only `V7_ENABLE_STACK_TRACKING` is defined.
*
* Clean all stack statistics gathered so far. See `v7_stack_stat()`
*/
void v7_stack_stat_clean(struct v7 *v7);
#endif
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_CORE_PUBLIC_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/std_error.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_STD_ERROR_H_
#define CS_V7_SRC_STD_ERROR_H_
/* Amalgamated: #include "v7/src/license.h" */
struct v7;
/*
* JavaScript error types
*/
#define TYPE_ERROR "TypeError"
#define SYNTAX_ERROR "SyntaxError"
#define REFERENCE_ERROR "ReferenceError"
#define INTERNAL_ERROR "InternalError"
#define RANGE_ERROR "RangeError"
#define EVAL_ERROR "EvalError"
#define ERROR_CTOR_MAX 6
/*
* TODO(mkm): EvalError is not so important, we should guard it behind
* something like `V7_ENABLE__EvalError`. However doing so makes it hard to
* keep ERROR_CTOR_MAX up to date; perhaps let's find a better way of doing it.
*
* EvalError is useful mostly because we now have ecma tests failing:
*
* 8129 FAIL ch15/15.4/15.4.4/15.4.4.16/15.4.4.16-7-c-iii-24.js (tail -c
* +7600043 tests/ecmac.db|head -c 496): [{"message":"[EvalError] is not
* defined"}]
*
* Those tests are not EvalError specific, and they do test that the exception
* handling machinery works as intended.
*/
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
V7_PRIVATE void init_error(struct v7 *v7);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_STD_ERROR_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/mm.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_MM_H_
#define CS_V7_SRC_MM_H_
/* Amalgamated: #include "v7/src/internal.h" */
typedef void (*gc_cell_destructor_t)(struct v7 *v7, void *);
struct gc_block {
struct gc_block *next;
struct gc_cell *base;
size_t size;
};
struct gc_arena {
struct gc_block *blocks;
size_t size_increment;
struct gc_cell *free; /* head of free list */
size_t cell_size;
#if V7_ENABLE__Memory__stats
unsigned long allocations; /* cumulative counter of allocations */
unsigned long garbage; /* cumulative counter of garbage */
unsigned long alive; /* number of living cells */
#endif
gc_cell_destructor_t destructor;
int verbose;
const char *name; /* for debugging purposes */
};
#endif /* CS_V7_SRC_MM_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/parser.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_PARSER_H_
#define CS_V7_SRC_PARSER_H_
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/core_public.h" */
#if !defined(V7_NO_COMPILER)
struct v7;
struct ast;
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
struct v7_pstate {
const char *file_name;
const char *source_code;
const char *pc; /* Current parsing position */
const char *src_end; /* End of source code */
int line_no; /* Line number */
int prev_line_no; /* Line number of previous token */
int inhibit_in; /* True while `in` expressions are inhibited */
int in_function; /* True if in a function */
int in_loop; /* True if in a loop */
int in_switch; /* True if in a switch block */
int in_strict; /* True if in strict mode */
};
V7_PRIVATE enum v7_err parse(struct v7 *v7, struct ast *a, const char *src,
size_t src_len, int is_json);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* V7_NO_COMPILER */
#endif /* CS_V7_SRC_PARSER_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/object_public.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/*
* === Objects
*/
#ifndef CS_V7_SRC_OBJECT_PUBLIC_H_
#define CS_V7_SRC_OBJECT_PUBLIC_H_
/* Amalgamated: #include "v7/src/core_public.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
/*
* Property attributes bitmask
*/
typedef unsigned short v7_prop_attr_t;
#define V7_PROPERTY_NON_WRITABLE (1 << 0)
#define V7_PROPERTY_NON_ENUMERABLE (1 << 1)
#define V7_PROPERTY_NON_CONFIGURABLE (1 << 2)
#define V7_PROPERTY_GETTER (1 << 3)
#define V7_PROPERTY_SETTER (1 << 4)
#define _V7_PROPERTY_HIDDEN (1 << 5)
/* property not managed by V7 HEAP */
#define _V7_PROPERTY_OFF_HEAP (1 << 6)
/* special property holding user data and destructor cb */
#define _V7_PROPERTY_USER_DATA_AND_DESTRUCTOR (1 << 7)
/*
* not a property attribute, but a flag for `v7_def()`. It's here in order to
* keep all offsets in one place
*/
#define _V7_DESC_PRESERVE_VALUE (1 << 8)
#define V7_PROP_ATTR_IS_WRITABLE(a) (!(a & V7_PROPERTY_NON_WRITABLE))
#define V7_PROP_ATTR_IS_ENUMERABLE(a) (!(a & V7_PROPERTY_NON_ENUMERABLE))
#define V7_PROP_ATTR_IS_CONFIGURABLE(a) (!(a & V7_PROPERTY_NON_CONFIGURABLE))
/*
* Internal helpers for `V7_DESC_...` macros
*/
#define _V7_DESC_SHIFT 16
#define _V7_DESC_MASK ((1 << _V7_DESC_SHIFT) - 1)
#define _V7_MK_DESC(v, n) \
(((v7_prop_attr_desc_t)(n)) << _V7_DESC_SHIFT | ((v) ? (n) : 0))
#define _V7_MK_DESC_INV(v, n) _V7_MK_DESC(!(v), (n))
/*
* Property attribute descriptors that may be given to `v7_def()`: for each
* attribute (`v7_prop_attr_t`), there is a corresponding macro, which takes
* param: either 1 (set attribute) or 0 (clear attribute). If some particular
* attribute isn't mentioned at all, it's left unchanged (or default, if the
* property is being created)
*
* There is additional flag: `V7_DESC_PRESERVE_VALUE`. If it is set, the
* property value isn't changed (or set to `undefined` if the property is being
* created)
*/
typedef unsigned long v7_prop_attr_desc_t;
#define V7_DESC_WRITABLE(v) _V7_MK_DESC_INV(v, V7_PROPERTY_NON_WRITABLE)
#define V7_DESC_ENUMERABLE(v) _V7_MK_DESC_INV(v, V7_PROPERTY_NON_ENUMERABLE)
#define V7_DESC_CONFIGURABLE(v) _V7_MK_DESC_INV(v, V7_PROPERTY_NON_CONFIGURABLE)
#define V7_DESC_GETTER(v) _V7_MK_DESC(v, V7_PROPERTY_GETTER)
#define V7_DESC_SETTER(v) _V7_MK_DESC(v, V7_PROPERTY_SETTER)
#define V7_DESC_PRESERVE_VALUE _V7_DESC_PRESERVE_VALUE
#define _V7_DESC_HIDDEN(v) _V7_MK_DESC(v, _V7_PROPERTY_HIDDEN)
#define _V7_DESC_OFF_HEAP(v) _V7_MK_DESC(v, _V7_PROPERTY_OFF_HEAP)
/* See `v7_set_destructor_cb` */
typedef void(v7_destructor_cb_t)(struct v7 *v7, void *ud);
/* Make an empty object */
v7_val_t v7_mk_object(struct v7 *v7);
/*
* Returns true if the given value is an object or function.
* i.e. it returns true if the value holds properties and can be
* used as argument to `v7_get`, `v7_set` and `v7_def`.
*/
int v7_is_object(v7_val_t v);
/* Set object's prototype. Return old prototype or undefined on error. */
v7_val_t v7_set_proto(struct v7 *v7, v7_val_t obj, v7_val_t proto);
/* Get object's prototype. */
v7_val_t v7_get_proto(struct v7 *v7, v7_val_t obj);
/*
* Lookup property `name` in object `obj`. If `obj` holds no such property,
* an `undefined` value is returned.
*
* If `name_len` is ~0, `name` is assumed to be NUL-terminated and
* `strlen(name)` is used.
*/
v7_val_t v7_get(struct v7 *v7, v7_val_t obj, const char *name, size_t name_len);
/*
* Like `v7_get()`, but "returns" value through `res` pointer argument.
* `res` must not be `NULL`.
*
* Caller should check the error code returned, and if it's something other
* than `V7_OK`, perform cleanup and return this code further.
*/
WARN_UNUSED_RESULT
enum v7_err v7_get_throwing(struct v7 *v7, v7_val_t obj, const char *name,
size_t name_len, v7_val_t *res);
/*
* Define object property, similar to JavaScript `Object.defineProperty()`.
*
* `name`, `name_len` specify property name, `val` is a property value.
* `attrs_desc` is a set of flags which can affect property's attributes,
* see comment of `v7_prop_attr_desc_t` for details.
*
* If `name_len` is ~0, `name` is assumed to be NUL-terminated and
* `strlen(name)` is used.
*
* Returns non-zero on success, 0 on error (e.g. out of memory).
*
* See also `v7_set()`.
*/
int v7_def(struct v7 *v7, v7_val_t obj, const char *name, size_t name_len,
v7_prop_attr_desc_t attrs_desc, v7_val_t v);
/*
* Set object property. Behaves just like JavaScript assignment.
*
* See also `v7_def()`.
*/
int v7_set(struct v7 *v7, v7_val_t obj, const char *name, size_t len,
v7_val_t val);
/*
* A helper function to define object's method backed by a C function `func`.
* `name` must be NUL-terminated.
*
* Return value is the same as for `v7_set()`.
*/
int v7_set_method(struct v7 *, v7_val_t obj, const char *name,
v7_cfunction_t *func);
/*
* Delete own property `name` of the object `obj`. Does not follow the
* prototype chain.
*
* If `name_len` is ~0, `name` is assumed to be NUL-terminated and
* `strlen(name)` is used.
*
* Returns 0 on success, -1 on error.
*/
int v7_del(struct v7 *v7, v7_val_t obj, const char *name, size_t name_len);
#if V7_ENABLE__Proxy
struct prop_iter_proxy_ctx;
#endif
/*
* Context for property iteration, see `v7_next_prop()`.
*
* Clients should not interpret contents of this structure, it's here merely to
* allow clients to allocate it not from the heap.
*/
struct prop_iter_ctx {
#if V7_ENABLE__Proxy
struct prop_iter_proxy_ctx *proxy_ctx;
#endif
struct v7_property *cur_prop;
unsigned init : 1;
};
/*
* Initialize the property iteration context `ctx`, see `v7_next_prop()` for
* usage example.
*/
enum v7_err v7_init_prop_iter_ctx(struct v7 *v7, v7_val_t obj,
struct prop_iter_ctx *ctx);
/*
* Destruct the property iteration context `ctx`, see `v7_next_prop()` for
* usage example
*/
void v7_destruct_prop_iter_ctx(struct v7 *v7, struct prop_iter_ctx *ctx);
/*
* Iterate over the `obj`'s properties.
*
* Usage example (here we assume we have some `v7_val_t obj`):
*
* struct prop_iter_ctx ctx;
* v7_val_t name, val;
* v7_prop_attr_t attrs;
*
* v7_init_prop_iter_ctx(v7, obj, &ctx);
* while (v7_next_prop(v7, &ctx, &name, &val, &attrs)) {
* if (V7_PROP_ATTR_IS_ENUMERABLE(attrs)) continue;
* ...
* }
* v7_destruct_prop_iter_ctx(v7, &ctx);
*
* As you see, v7_next_prop will iterate through all properties, including
* non-enumerable ones, and it's your responsibility to test the attributes
* with the provided `V7_PROP_ATTR_*` macros and proceed as you see fit.
*/
int v7_next_prop(struct v7 *v7, struct prop_iter_ctx *ctx, v7_val_t *name,
v7_val_t *value, v7_prop_attr_t *attrs);
/* Returns true if the object is an instance of a given constructor. */
int v7_is_instanceof(struct v7 *v7, v7_val_t o, const char *c);
/* Returns true if the object is an instance of a given constructor. */
int v7_is_instanceof_v(struct v7 *v7, v7_val_t o, v7_val_t c);
/*
* Associates an opaque C value (anything that can be casted to a `void * )
* with an object.
*
* You can achieve a similar effect by just setting a special property with
* a foreign value (see `v7_mk_foreign`), except user data offers the following
* advantages:
*
* 1. You don't have to come up with some arbitrary "special" property name.
* 2. JS scripts cannot access user data by mistake via property lookup.
* 3. The user data is available to the destructor. When the desctructor is
* invoked you cannot access any of its properties.
* 4. Allows the implementation to use a more compact encoding
*
* Does nothing if `obj` is not a mutable object.
*/
void v7_set_user_data(struct v7 *v7, v7_val_t obj, void *ud);
/*
* Get the opaque user data set with `v7_set_user_data`.
*
* Returns NULL if there is no user data set or if `obj` is not an object.
*/
void *v7_get_user_data(struct v7 *v7, v7_val_t obj);
/*
* Register a callback which will be invoked when a given object gets
* reclaimed by the garbage collector.
*
* The callback will be invoked while garbage collection is still in progress
* and hence the internal state of the JS heap is in an undefined state.
*
* The only v7 API which is safe to use in this callback is `v7_disown()`,
* that's why `v7` pointer is given to it. *Calls to any other v7 functions are
* illegal here*.
*
* The intended use case is to reclaim resources allocated by C code.
*/
void v7_set_destructor_cb(struct v7 *v7, v7_val_t obj, v7_destructor_cb_t *d);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_OBJECT_PUBLIC_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/tokenizer.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_TOKENIZER_H_
#define CS_V7_SRC_TOKENIZER_H_
/* Amalgamated: #include "v7/src/internal.h" */
#if !defined(V7_NO_COMPILER)
enum v7_tok {
TOK_END_OF_INPUT,
TOK_NUMBER,
TOK_STRING_LITERAL,
TOK_REGEX_LITERAL,
TOK_IDENTIFIER,
/* Punctuators */
TOK_OPEN_CURLY,
TOK_CLOSE_CURLY,
TOK_OPEN_PAREN,
TOK_CLOSE_PAREN,
TOK_COMMA,
TOK_OPEN_BRACKET,
TOK_CLOSE_BRACKET,
TOK_DOT,
TOK_COLON,
TOK_SEMICOLON,
/* Equality ops, in this order */
TOK_EQ,
TOK_EQ_EQ,
TOK_NE,
TOK_NE_NE,
/* Assigns */
TOK_ASSIGN,
TOK_REM_ASSIGN,
TOK_MUL_ASSIGN,
TOK_DIV_ASSIGN,
TOK_XOR_ASSIGN,
TOK_PLUS_ASSIGN,
TOK_MINUS_ASSIGN,
TOK_OR_ASSIGN,
TOK_AND_ASSIGN,
TOK_LSHIFT_ASSIGN,
TOK_RSHIFT_ASSIGN,
TOK_URSHIFT_ASSIGN,
TOK_AND,
TOK_LOGICAL_OR,
TOK_PLUS,
TOK_MINUS,
TOK_PLUS_PLUS,
TOK_MINUS_MINUS,
TOK_LOGICAL_AND,
TOK_OR,
TOK_QUESTION,
TOK_TILDA,
TOK_REM,
TOK_MUL,
TOK_DIV,
TOK_XOR,
/* Relational ops, must go in this order */
TOK_LE,
TOK_LT,
TOK_GE,
TOK_GT,
TOK_LSHIFT,
TOK_RSHIFT,
TOK_URSHIFT,
TOK_NOT,
/* Keywords. must be in the same order as tokenizer.c::s_keywords array */
TOK_BREAK,
TOK_CASE,
TOK_CATCH,
TOK_CONTINUE,
TOK_DEBUGGER,
TOK_DEFAULT,
TOK_DELETE,
TOK_DO,
TOK_ELSE,
TOK_FALSE,
TOK_FINALLY,
TOK_FOR,
TOK_FUNCTION,
TOK_IF,
TOK_IN,
TOK_INSTANCEOF,
TOK_NEW,
TOK_NULL,
TOK_RETURN,
TOK_SWITCH,
TOK_THIS,
TOK_THROW,
TOK_TRUE,
TOK_TRY,
TOK_TYPEOF,
TOK_VAR,
TOK_VOID,
TOK_WHILE,
TOK_WITH,
/* TODO(lsm): process these reserved words too */
TOK_CLASS,
TOK_ENUM,
TOK_EXTENDS,
TOK_SUPER,
TOK_CONST,
TOK_EXPORT,
TOK_IMPORT,
TOK_IMPLEMENTS,
TOK_LET,
TOK_PRIVATE,
TOK_PUBLIC,
TOK_INTERFACE,
TOK_PACKAGE,
TOK_PROTECTED,
TOK_STATIC,
TOK_YIELD,
NUM_TOKENS
};
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
V7_PRIVATE int skip_to_next_tok(const char **ptr, const char *src_end);
V7_PRIVATE enum v7_tok get_tok(const char **s, const char *src_end, double *n,
enum v7_tok prev_tok);
V7_PRIVATE int is_reserved_word_token(enum v7_tok tok);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* V7_NO_COMPILER */
#endif /* CS_V7_SRC_TOKENIZER_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/opcodes.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_OPCODES_H_
#define CS_V7_SRC_OPCODES_H_
/*
* ==== Instructions
*
* Bytecode instructions consist of 1-byte opcode, optionally followed by N
* bytes of arguments.
*
* Opcodes that accept an index in the literal table (PUSH_LIT, GET_VAR,
* SET_VAR, ...) also accept inline literals. In order to distinguish indices in
* the literals table and the inline literals, indices 0 and 1 are reserved as
* type tags for inline literals:
*
* if 0, the following bytes encode a string literal
* if 1, they encode a number (textual, like in the AST)
*
* (see enum bcode_inline_lit_type_tag)
*
*
* Stack diagrams follow the syntax and semantics of:
*
* http://everything2.com/title/Forth+stack+diagrams[Forth stack diagrams].
*
* We use the following extension in the terminology:
*
* `T`: "Try stack".
* `A`: opcode arguments.
* `S`: stash register (one element stack).
*
*/
enum opcode {
/*
* Removes an item from the top of the stack. It is undefined what happens if
* the stack is empty.
*
* `( a -- )`
*/
OP_DROP,
/*
* Duplicates a value on top of the stack.
*
* `( a -- a a)`
*/
OP_DUP,
/*
* Duplicates 2 values from the top of the stack in the same order.
*
* `( a b -- a b a b)`
*/
OP_2DUP,
/*
* Swap the top two items on the stack.
*
* `( a b -- b a )`
*/
OP_SWAP,
/*
* Copy current top of the stack to the temporary stash register.
*
* The content of the stash register will be cleared in the event of an
* exception.
*
* `( a S: b -- a S: a)` saves TOS to stash reg
*/
OP_STASH,
/*
* Replace the top of the stack with the content of the temporary stash
* register.
*
* The stash register is cleared afterwards.
*
* `( a S: b -- b S: nil )` replaces tos with stash reg
*/
OP_UNSTASH,
/*
* Effectively drops the last-but-one element from stack
*
* `( a b -- b )`
*/
OP_SWAP_DROP,
/*
* Pushes `undefined` onto the stack.
*
* `( -- undefined )`
*/
OP_PUSH_UNDEFINED,
/*
* Pushes `null` onto the stack.
*
* `( -- null )`
*/
OP_PUSH_NULL,
/*
* Pushes current value of `this` onto the stack.
*
* `( -- this )`
*/
OP_PUSH_THIS,
/*
* Pushes `true` onto the stack.
*
* `( -- true )`
*/
OP_PUSH_TRUE,
/*
* Pushes `false` onto the stack.
*
* `( -- false )`
*/
OP_PUSH_FALSE,
/*
* Pushes `0` onto the stack.
*
* `( -- 0 )`
*/
OP_PUSH_ZERO,
/*
* Pushes `1` onto the stack.
*
* `( -- 1 )`
*/
OP_PUSH_ONE,
/*
* Pushes a value from literals table onto the stack.
*
* The opcode takes a varint operand interpreted as an index in the current
* literal table (see lit table).
*
* ( -- a )
*/
OP_PUSH_LIT,
OP_NOT,
OP_LOGICAL_NOT,
/*
* Takes a number from the top of the stack, inverts the sign and pushes it
* back.
*
* `( a -- -a )`
*/
OP_NEG,
/*
* Takes a number from the top of the stack pushes the evaluation of
* `Number()`.
*
* `( a -- Number(a) )`
*/
OP_POS,
/*
* Takes 2 values from the top of the stack and performs addition operation:
* If any of the two values is not `undefined`, number or boolean, both values
* are converted into strings and concatenated.
* Otherwise, both values are treated as numbers:
* * `undefined` is converted into NaN
* * `true` is converted into 1
* * `false` is converted into 0
*
* Result is pushed back onto the stack.
*
* TODO: make it behave exactly like JavaScript's `+` operator.
*
* `( a b -- a+b )`
*/
OP_ADD,
OP_SUB, /* ( a b -- a-b ) */
OP_REM, /* ( a b -- a%b ) */
OP_MUL, /* ( a b -- a*b ) */
OP_DIV, /* ( a b -- a/b ) */
OP_LSHIFT, /* ( a b -- a<>b ) */
OP_URSHIFT, /* ( a b -- a>>>b ) */
OP_OR, /* ( a b -- a|b ) */
OP_XOR, /* ( a b -- a^b ) */
OP_AND, /* ( a b -- a&b ) */
/*
* Takes two numbers form the top of the stack and pushes `true` if they are
* equal, or `false` if they are not equal.
*
* ( a b -- a===b )
*/
OP_EQ_EQ,
OP_EQ, /* ( a b -- a==b ) */
OP_NE, /* ( a b -- a!=b ) */
OP_NE_NE, /* ( a b -- a!==b ) */
OP_LT, /* ( a b -- ab ) */
OP_GE, /* ( a b -- a>=b ) */
OP_INSTANCEOF,
OP_TYPEOF,
OP_IN,
/*
* Takes 2 values from the stack, treats the top of the stack as property name
* and the next value must be an object, an array or a string.
* If it's an object, pushes the value of its named property onto the stack.
* If it's an array or a string, returns a value at a given position.
*/
OP_GET,
/*
* Takes 3 items from the stack: value, property name, object. Sets the given
* property of a given object to a given value, pushes value back onto the
*stack.
*
* `( a b c -- a[b]=c )`
*/
OP_SET,
/*
* Takes 1 value from the stack and a varint argument -- index of the var name
* in the literals table. Tries to find the variable in the current scope
* chain and assign the value to it. If the variable is not found -- creates
* a new one in the global scope. Pushes the value back to the stack.
*
* `( a -- a )`
*/
OP_SET_VAR,
/*
* Takes a varint argument -- index of the var name in the literals table.
* Looks up that variable in the scope chain and pushes its value onto the
* stack.
*
* `( -- a )`
*/
OP_GET_VAR,
/*
* Like OP_GET_VAR but returns undefined
* instead of throwing reference error.
*
* `( -- a )`
*/
OP_SAFE_GET_VAR,
/*
* ==== Jumps
*
* All jump instructions take one 4-byte argument: offset to jump to. Offset
*is a
* index of the byte in the instruction stream, starting with 0. No byte order
* conversion is applied.
*
* TODO: specify byte order for the offset.
*/
/*
* Unconditiona jump.
*/
OP_JMP,
/*
* Takes one value from the stack and performs a jump if conversion of that
* value to boolean results in `true`.
*
* `( a -- )`
*/
OP_JMP_TRUE,
/*
* Takes one value from the stack and performs a jump if conversion of that
* value to boolean results in `false`.
*
* `( a -- )`
*/
OP_JMP_FALSE,
/*
* Like OP_JMP_TRUE but if the branch
* is taken it also drops another stack element:
*
* if `b` is true: `( a b -- )`
* if `b` is false: `( a b -- a )`
*/
OP_JMP_TRUE_DROP,
/*
* Conditional jump on the v7->is_continuing flag.
* Clears the flag once executed.
*
* `( -- )`
*/
OP_JMP_IF_CONTINUE,
/*
* Constructs a new empty object and pushes it onto the stack.
*
* `( -- {} )`
*/
OP_CREATE_OBJ,
/*
* Constructs a new empty array and pushes it onto the stack.
*
* `( -- [] )`
*/
OP_CREATE_ARR,
/*
* Allocates the iteration context (for `OP_NEXT_PROP`) from heap and pushes
* a foreign pointer to it on stack. The allocated data is stored as "user
* data" of the object, and it will be reclaimed automatically when the
* object gets garbage-collected.
*
* `( -- ctx )`
*/
OP_PUSH_PROP_ITER_CTX,
/*
* Yields the next property name.
* Used in the for..in construct.
*
* The first evaluation must receive `null` as handle.
* Subsequent evaluations will either:
*
* a) produce a new handle, the key and true value:
*
* `( o h -- o h' key true)`
*
* b) produce a false value only, indicating no more properties:
*
* `( o h -- false)`
*/
OP_NEXT_PROP,
/*
* Copies the function object at TOS and assigns current scope
* in func->scope.
*
* `( a -- a )`
*/
OP_FUNC_LIT,
/*
* Takes the number of arguments as parameter.
*
* Pops N function arguments from stack, then pops function, then pops `this`.
* Calls a function and populates TOS with the returned value.
*
* `( this f a0 a1 ... aN -- f(a0,a1,...) )`
*/
OP_CALL,
OP_NEW,
/*
* Checks that TOS is a callable and if not saves an exception
* that will will be thrown by CALL after all arguments have been evaluated.
*/
OP_CHECK_CALL,
/*
* Returns the current function.
*
* It has no stack side effects. The function upon return will leave the
* return value on the stack. The return value must be pushed on the stack
* prior to invoking a RET.
*
* `( -- )`
*/
OP_RET,
/*
* Deletes the property of given name `p` from the given object `o`. Returns
* boolean value `a`.
*
* `( o p -- a )`
*/
OP_DELETE,
/*
* Like `OP_DELETE`, but uses the current scope as an object to delete
* a property from.
*
* `( p -- a )`
*/
OP_DELETE_VAR,
/*
* Pushes a value (bcode offset of `catch` block) from opcode argument to
* "try stack".
*
* Used in the beginning of the `try` block.
*
* `( A: a -- T: a )`
*/
OP_TRY_PUSH_CATCH,
/*
* Pushes a value (bcode offset of `finally` block) from opcode argument to
* "try stack".
*
* Used in the beginning of the `try` block.
*
* `( A: a -- T: a )`
*
* TODO: implement me
*/
OP_TRY_PUSH_FINALLY,
/*
* Pushes a value (bcode offset of a label) from opcode argument to
* "try stack".
*
* Used at the beginning of loops that contain break or continue.
* Possible optimisation: don't emit if we can ensure that no break or
* continue statement is used.
*
* `( A: a -- T: a )`
*/
OP_TRY_PUSH_LOOP,
/*
* Pushes a value (bcode offset of a label) from opcode argument to
* "try stack".
*
* Used at the beginning of switch statements.
*
* `( A: a -- T: a )`
*/
OP_TRY_PUSH_SWITCH,
/*
* Pops a value (bcode offset of `finally` or `catch` block) from "try
* stack", and discards it
*
* Used in the end of the `try` block, as well as in the beginning of the
* `catch` and `finally` blocks
*
* `( T: a -- T: )`
*/
OP_TRY_POP,
/*
* Used in the end of the `finally` block:
*
* - if some value is currently being thrown, keep throwing it.
* If eventually we encounter `catch` block, the thrown value gets
* populated on TOS:
*
* `( -- a )`
*
* - if there is some pending value to return, keep returning it.
* If we encounter no further `finally` blocks, then the returned value
* gets populated on TOS:
*
* `( -- a )`
*
* And return is performed.
*
* - otherwise, do nothing
*/
OP_AFTER_FINALLY,
/*
* Throw value from TOS. First of all, it pops the value and saves it into
* `v7->vals.thrown_error`:
*
* `( a -- )`
*
* Then unwinds stack looking for the first `catch` or `finally` blocks.
*
* - if `finally` is found, thrown value is kept into `v7->vals.thrown_error`.
* - if `catch` is found, thrown value is pushed back to the stack:
* `( -- a )`
* - otherwise, thrown value is kept into `v7->vals.thrown_error`
*/
OP_THROW,
/*
* Unwind to next break entry in the try stack, evaluating
* all finally blocks on its way up.
*
* `( -- )`
*/
OP_BREAK,
/*
* Like OP_BREAK, but sets the v7->is_continuing flag
* which will cause OP_JMP_IF_CONTINUE to restart the loop.
*
* `( -- )`
*/
OP_CONTINUE,
/*
* Used when we enter the `catch` block. Takes a varint argument -- index of
* the exception variable name in the literals table.
*
* Pops the exception value from the stack, creates a private frame,
* sets exception property on it with the given name. pushes this
* private frame to call stack.
*
* `( e -- )`
*/
OP_ENTER_CATCH,
/*
* Ued when we exit from the `catch` block. Merely pops the private frame
* from the call stack.
*
* `( -- )`
*/
OP_EXIT_CATCH,
OP_MAX,
};
#define _OP_LINE_NO 0x80
#endif /* CS_V7_SRC_OPCODES_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/core.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_CORE_H_
#define CS_V7_SRC_CORE_H_
/* Amalgamated: #include "v7/src/core_public.h" */
/* Amalgamated: #include "common/mbuf.h" */
/* Amalgamated: #include "v7/src/std_error.h" */
/* Amalgamated: #include "v7/src/mm.h" */
/* Amalgamated: #include "v7/src/parser.h" */
/* Amalgamated: #include "v7/src/object_public.h" */
/* Amalgamated: #include "v7/src/tokenizer.h" */
/* Amalgamated: #include "v7/src/opcodes.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
typedef uint64_t val_t;
#if V7_ENABLE_ENTITY_IDS
typedef unsigned short entity_id_t;
typedef unsigned char entity_id_part_t;
/*
* Magic numbers that are stored in various objects in order to identify their
* type in runtime
*/
#define V7_ENTITY_ID_PROP 0xe9a1
#define V7_ENTITY_ID_PART_OBJ 0x57
#define V7_ENTITY_ID_PART_GEN_OBJ 0x31
#define V7_ENTITY_ID_PART_JS_FUNC 0x0d
#define V7_ENTITY_ID_NONE 0xa5a5
#define V7_ENTITY_ID_PART_NONE 0xa5
#endif
/*
* Double-precision floating-point number, IEEE 754
*
* 64 bit (8 bytes) in total
* 1 bit sign
* 11 bits exponent
* 52 bits mantissa
* 7 6 5 4 3 2 1 0
* seeeeeee|eeeemmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm
*
* If an exponent is all-1 and mantissa is all-0, then it is an INFINITY:
* 11111111|11110000|00000000|00000000|00000000|00000000|00000000|00000000
*
* If an exponent is all-1 and mantissa's MSB is 1, it is a quiet NaN:
* 11111111|11111000|00000000|00000000|00000000|00000000|00000000|00000000
*
* V7 NaN-packing:
* sign and exponent is 0xfff
* 4 bits specify type (tag), must be non-zero
* 48 bits specify value
*
* 11111111|1111tttt|vvvvvvvv|vvvvvvvv|vvvvvvvv|vvvvvvvv|vvvvvvvv|vvvvvvvv
* NaN marker |type| 48-bit placeholder for values: pointers, strings
*
* On 64-bit platforms, pointers are really 48 bit only, so they can fit,
* provided they are sign extended
*/
/*
* A tag is made of the sign bit and the 4 lower order bits of byte 6.
* So in total we have 32 possible tags.
*
* Tag (1,0) however cannot hold a zero payload otherwise it's interpreted as an
* INFINITY; for simplicity we're just not going to use that combination.
*/
#define MAKE_TAG(s, t) \
((uint64_t)(s) << 63 | (uint64_t) 0x7ff0 << 48 | (uint64_t)(t) << 48)
#define V7_TAG_OBJECT MAKE_TAG(1, 0xF)
#define V7_TAG_FOREIGN MAKE_TAG(1, 0xE)
#define V7_TAG_UNDEFINED MAKE_TAG(1, 0xD)
#define V7_TAG_BOOLEAN MAKE_TAG(1, 0xC)
#define V7_TAG_NAN MAKE_TAG(1, 0xB)
#define V7_TAG_STRING_I MAKE_TAG(1, 0xA) /* Inlined string len < 5 */
#define V7_TAG_STRING_5 MAKE_TAG(1, 0x9) /* Inlined string len 5 */
#define V7_TAG_STRING_O MAKE_TAG(1, 0x8) /* Owned string */
#define V7_TAG_STRING_F MAKE_TAG(1, 0x7) /* Foreign string */
#define V7_TAG_STRING_C MAKE_TAG(1, 0x6) /* String chunk */
#define V7_TAG_FUNCTION MAKE_TAG(1, 0x5) /* JavaScript function */
#define V7_TAG_CFUNCTION MAKE_TAG(1, 0x4) /* C function */
#define V7_TAG_STRING_D MAKE_TAG(1, 0x3) /* Dictionary string */
#define V7_TAG_REGEXP MAKE_TAG(1, 0x2) /* Regex */
#define V7_TAG_NOVALUE MAKE_TAG(1, 0x1) /* Sentinel for no value */
#define V7_TAG_MASK MAKE_TAG(1, 0xF)
#define _V7_NULL V7_TAG_FOREIGN
#define _V7_UNDEFINED V7_TAG_UNDEFINED
V7_STATIC_ASSERT(_V7_NULL == V7_NULL, public_V7_NULL_is_wrong);
V7_STATIC_ASSERT(_V7_UNDEFINED == V7_UNDEFINED, public_V7_UNDEFINED_is_wrong);
/*
* Object attributes bitmask
*/
typedef unsigned char v7_obj_attr_t;
#define V7_OBJ_NOT_EXTENSIBLE (1 << 0) /* TODO(lsm): store this in LSB */
#define V7_OBJ_DENSE_ARRAY (1 << 1) /* TODO(mkm): store in some tag */
#define V7_OBJ_FUNCTION (1 << 2) /* function object */
#define V7_OBJ_OFF_HEAP (1 << 3) /* object not managed by V7 HEAP */
#define V7_OBJ_HAS_DESTRUCTOR (1 << 4) /* has user data */
#define V7_OBJ_PROXY (1 << 5) /* it's a Proxy object */
/*
* JavaScript value is either a primitive, or an object.
* There are 5 primitive types: Undefined, Null, Boolean, Number, String.
* Non-primitive type is an Object type. There are several classes of Objects,
* see description of `struct v7_generic_object` below for more details.
* This enumeration combines types and object classes in one enumeration.
* NOTE(lsm): compile with `-fshort-enums` to reduce sizeof(enum v7_type) to 1.
*/
enum v7_type {
/* Primitive types */
V7_TYPE_UNDEFINED,
V7_TYPE_NULL,
V7_TYPE_BOOLEAN,
V7_TYPE_NUMBER,
V7_TYPE_STRING,
V7_TYPE_FOREIGN,
V7_TYPE_CFUNCTION,
/* Different classes of Object type */
V7_TYPE_GENERIC_OBJECT,
V7_TYPE_BOOLEAN_OBJECT,
V7_TYPE_STRING_OBJECT,
V7_TYPE_NUMBER_OBJECT,
V7_TYPE_FUNCTION_OBJECT,
V7_TYPE_CFUNCTION_OBJECT,
V7_TYPE_REGEXP_OBJECT,
V7_TYPE_ARRAY_OBJECT,
V7_TYPE_DATE_OBJECT,
V7_TYPE_ERROR_OBJECT,
V7_TYPE_MAX_OBJECT_TYPE,
V7_NUM_TYPES
};
/*
* Call frame type mask: we have a "class hierarchy" of the call frames, see
* `struct v7_call_frame_base`, and the `type_mask` field represents the exact
* frame type.
*
* Possible values are:
*
* - `V7_CALL_FRAME_MASK_PRIVATE | V7_CALL_FRAME_MASK_BCODE`: the most popular
* frame type: call frame for bcode execution, either top-level code or JS
* function.
* - `V7_CALL_FRAME_MASK_PRIVATE`: used for `catch` clauses only: the variables
* we create in `catch` clause should not be visible from the outside of the
* clause, so we have to create a separate scope object for it.
* - `V7_CALL_FRAME_MASK_CFUNC`: call frame for C function.
*/
typedef uint8_t v7_call_frame_mask_t;
#define V7_CALL_FRAME_MASK_BCODE (1 << 0)
#define V7_CALL_FRAME_MASK_PRIVATE (1 << 1)
#define V7_CALL_FRAME_MASK_CFUNC (1 << 2)
/*
* Base of the call frame; includes the pointer to the previous frame,
* and the frame type.
*
* In order to save memory, it also contains some bitfields which actually
* belong to some "sub-structures".
*
* The hierarchy is as follows:
*
* - v7_call_frame_base
* - v7_call_frame_private
* - v7_call_frame_bcode
* - v7_call_frame_cfunc
*/
struct v7_call_frame_base {
struct v7_call_frame_base *prev;
/* See comment for `v7_call_frame_mask_t` */
v7_call_frame_mask_t type_mask : 3;
/* Belongs to `struct v7_call_frame_private` */
unsigned int line_no : 16;
/* Belongs to `struct v7_call_frame_bcode` */
unsigned is_constructor : 1;
/* Belongs to `struct v7_call_frame_bcode` */
unsigned int is_thrown : 1;
};
/*
* "private" call frame, used in `catch` blocks, merely for using a separate
* scope object there. It is also a "base class" for the bcode call frame,
* see `struct v7_call_frame_bcode`.
*
* TODO(dfrank): probably implement it differently, so that we can get rid of
* the separate "private" frames whatsoever (and just include it into struct
* v7_call_frame_bcode )
*/
struct v7_call_frame_private {
struct v7_call_frame_base base;
size_t stack_size;
struct {
/*
* Current execution scope. Initially, it is equal to the `global_object`;
* and at each function call, it is augmented by the new scope object, which
* has the previous value as a prototype.
*/
val_t scope;
val_t try_stack;
} vals;
};
/*
* "bcode" call frame, augments "private" frame with `bcode` and the position
* in it, and `this` object. It is the primary frame type, used when executing
* a bcode script or calling a function.
*/
struct v7_call_frame_bcode {
struct v7_call_frame_private base;
struct {
val_t this_obj;
val_t thrown_error;
} vals;
struct bcode *bcode;
char *bcode_ops;
};
/*
* "cfunc" call frame, used when calling cfunctions.
*/
struct v7_call_frame_cfunc {
struct v7_call_frame_base base;
struct {
val_t this_obj;
} vals;
v7_cfunction_t *cfunc;
};
/*
* This structure groups together all val_t logical members
* of struct v7 so that GC and freeze logic can easily access all
* of them together. This structure must contain only val_t members.
*/
struct v7_vals {
val_t global_object;
val_t arguments; /* arguments of current call */
val_t object_prototype;
val_t array_prototype;
val_t boolean_prototype;
val_t error_prototype;
val_t string_prototype;
val_t regexp_prototype;
val_t number_prototype;
val_t date_prototype;
val_t function_prototype;
val_t proxy_prototype;
/*
* temporary register for `OP_STASH` and `OP_UNSTASH` instructions. Valid if
* `v7->is_stashed` is non-zero
*/
val_t stash;
val_t error_objects[ERROR_CTOR_MAX];
/*
* Value that is being thrown. Valid if `is_thrown` is non-zero (see below)
*/
val_t thrown_error;
/*
* value that is going to be returned. Needed when some `finally` block needs
* to be executed after `return my_value;` was issued. Used in bcode.
* See also `is_returned` below
*/
val_t returned_value;
val_t last_name[2]; /* used for error reporting */
/* most recent OP_CHECK_CALL exceptions, to be thrown by OP_CALL|OP_NEW */
val_t call_check_ex;
};
struct v7 {
struct v7_vals vals;
/*
* Stack of call frames.
*
* Execution contexts are contained in two chains:
* - Stack of call frames: to allow returning, throwing, and stack trace
* generation;
* - In the lexical scope via their prototype chain (to allow variable
* lookup), see `struct v7_call_frame_private::scope`.
*
* Execution contexts should be allocated on heap, because they might not be
* on a call stack but still referenced (closures).
*
* New call frame is created every time some top-level code is evaluated,
* or some code is being `eval`-d, or some function is called, either JS
* function or C function (although the call frame types are different for
* JS functions and cfunctions, see `struct v7_call_frame_base` and its
* sub-structures)
*
* When no code is being evaluated at the moment, `call_stack` is `NULL`.
*
* See comment for `struct v7_call_frame_base` for some more details.
*/
struct v7_call_frame_base *call_stack;
/*
* Bcode executes until it reaches `bottom_call_frame`. When some top-level
* or `eval`-d code starts execution, the `bottom_call_frame` is set to the
* call frame which was just created for the execution.
*/
struct v7_call_frame_base *bottom_call_frame;
struct mbuf stack; /* value stack for bcode interpreter */
struct mbuf owned_strings; /* Sequence of (varint len, char data[]) */
struct mbuf foreign_strings; /* Sequence of (varint len, char *data) */
struct mbuf tmp_stack; /* Stack of val_t* elements, used as root set */
int need_gc; /* Set to true to trigger GC when safe */
struct gc_arena generic_object_arena;
struct gc_arena function_arena;
struct gc_arena property_arena;
#if V7_ENABLE__Memory__stats
size_t function_arena_ast_size;
size_t bcode_ops_size;
size_t bcode_lit_total_size;
size_t bcode_lit_deser_size;
#endif
struct mbuf owned_values; /* buffer for GC roots owned by C code */
/*
* Stack of the root bcodes being executed at the moment. Note that when some
* regular JS function is called inside `eval_bcode()`, the function's bcode
* is NOT added here. Buf if some cfunction is called, which in turn calls
* `b_exec()` (or `b_apply()`) recursively, the new bcode is added to this
* stack.
*/
struct mbuf act_bcodes;
char error_msg[80]; /* Exception message */
struct mbuf json_visited_stack; /* Detecting cycle in to_json */
/* Parser state */
#if !defined(V7_NO_COMPILER)
struct v7_pstate pstate; /* Parsing state */
enum v7_tok cur_tok; /* Current token */
const char *tok; /* Parsed terminal token (ident, number, string) */
unsigned long tok_len; /* Length of the parsed terminal token */
size_t last_var_node; /* Offset of last var node or function/script node */
int after_newline; /* True if the cur_tok starts a new line */
double cur_tok_dbl; /* When tokenizing, parser stores TOK_NUMBER here */
/*
* Current linenumber. Currently it is used by parser, compiler and bcode
* evaluator.
*
* - Parser: it's the last line_no emitted to AST
* - Compiler: it's the last line_no emitted to bcode
*/
int line_no;
#endif /* V7_NO_COMPILER */
/* singleton, pointer because of amalgamation */
struct v7_property *cur_dense_prop;
volatile int interrupted;
#ifdef V7_STACK_SIZE
void *sp_limit;
void *sp_lwm;
#endif
#if defined(V7_CYG_PROFILE_ON)
/* linked list of v7 contexts, needed by cyg_profile hooks */
struct v7 *next_v7;
#if V7_ENABLE_STACK_TRACKING
/* linked list of stack tracking contexts */
struct stack_track_ctx *stack_track_ctx;
int stack_stat[V7_STACK_STATS_CNT];
#endif
#endif
#ifdef V7_MALLOC_GC
struct mbuf malloc_trace;
#endif
/*
* TODO(imax): remove V7_DISABLE_STR_ALLOC_SEQ knob after 2015/12/01 if there
* are no issues.
*/
#if !V7_DISABLE_STR_ALLOC_SEQ
uint16_t gc_next_asn; /* Next sequence number to use. */
uint16_t gc_min_asn; /* Minimal sequence number currently in use. */
#endif
#if defined(V7_TRACK_MAX_PARSER_STACK_SIZE)
size_t parser_stack_data_max_size;
size_t parser_stack_ret_max_size;
size_t parser_stack_data_max_len;
size_t parser_stack_ret_max_len;
#endif
#ifdef V7_FREEZE
FILE *freeze_file;
#endif
/*
* true if exception is currently being created. Needed to avoid recursive
* exception creation
*/
unsigned int creating_exception : 1;
/* while true, GC is inhibited */
unsigned int inhibit_gc : 1;
/* true if `thrown_error` is valid */
unsigned int is_thrown : 1;
/* true if `returned_value` is valid */
unsigned int is_returned : 1;
/* true if a finally block is executing while breaking */
unsigned int is_breaking : 1;
/* true when a continue OP is executed, reset by `OP_JMP_IF_CONTINUE` */
unsigned int is_continuing : 1;
/* true if some value is currently stashed (`v7->vals.stash`) */
unsigned int is_stashed : 1;
/* true if last emitted statement does not affect data stack */
unsigned int is_stack_neutral : 1;
/* true if precompiling; affects compiler bcode choices */
unsigned int is_precompiling : 1;
enum opcode last_ops[2]; /* trace of last ops, used for error reporting */
};
struct v7_property {
struct v7_property *
next; /* Linkage in struct v7_generic_object::properties */
v7_prop_attr_t attributes;
#if V7_ENABLE_ENTITY_IDS
entity_id_t entity_id;
#endif
val_t name; /* Property name (a string) */
val_t value; /* Property value */
};
/*
* "base object": structure which is shared between objects and functions.
*/
struct v7_object {
/* First HIDDEN property in a chain is an internal object value */
struct v7_property *properties;
v7_obj_attr_t attributes;
#if V7_ENABLE_ENTITY_IDS
entity_id_part_t entity_id_base;
entity_id_part_t entity_id_spec;
#endif
};
/*
* An object is an unordered collection of properties.
* A function stored in a property of an object is called a method.
* A property has a name, a value, and set of attributes.
* Attributes are: ReadOnly, DontEnum, DontDelete, Internal.
*
* A constructor is a function that creates and initializes objects.
* Each constructor has an associated prototype object that is used for
* inheritance and shared properties. When a constructor creates an object,
* the new object references the constructor’s prototype.
*
* Objects could be a "generic objects" which is a collection of properties,
* or a "typed object" which also hold an internal value like String or Number.
* Those values are implicit, unnamed properties of the respective types,
* and can be coerced into primitive types by calling a respective constructor
* as a function:
* var a = new Number(123);
* typeof(a) == 'object';
* typeof(Number(a)) == 'number';
*/
struct v7_generic_object {
/*
* This has to be the first field so that objects can be managed by the GC.
*/
struct v7_object base;
struct v7_object *prototype;
};
/*
* Variables are function-scoped and are hoisted.
* Lexical scoping & closures: each function has a chain of scopes, defined
* by the lexicographic order of function definitions.
* Scope is different from the execution context.
* Execution context carries "variable object" which is variable/value
* mapping for all variables defined in a function, and `this` object.
* If function is not called as a method, then `this` is a global object.
* Otherwise, `this` is an object that contains called method.
* New execution context is created each time a function call is performed.
* Passing arguments through recursion is done using execution context, e.g.
*
* var factorial = function(num) {
* return num < 2 ? 1 : num * factorial(num - 1);
* };
*
* Here, recursion calls the same function `factorial` several times. Execution
* contexts for each call form a stack. Each context has different variable
* object, `vars`, with different values of `num`.
*/
struct v7_js_function {
/*
* Functions are objects. This has to be the first field so that function
* objects can be managed by the GC.
*/
struct v7_object base;
struct v7_generic_object *scope; /* lexical scope of the closure */
/* bytecode, might be shared between functions */
struct bcode *bcode;
};
struct v7_regexp {
val_t regexp_string;
struct slre_prog *compiled_regexp;
long lastIndex;
};
/* Vector, describes some memory location pointed by `p` with length `len` */
struct v7_vec {
char *p;
size_t len;
};
/*
* Constant vector, describes some const memory location pointed by `p` with
* length `len`
*/
struct v7_vec_const {
const char *p;
size_t len;
};
#define V7_VEC(str) \
{ (str), sizeof(str) - 1 }
/*
* Returns current execution scope.
*
* See comment for `struct v7_call_frame_private::vals::scope`
*/
V7_PRIVATE v7_val_t get_scope(struct v7 *v7);
/*
* Returns 1 if currently executing bcode in the "strict mode", 0 otherwise
*/
V7_PRIVATE uint8_t is_strict_mode(struct v7 *v7);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_CORE_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/primitive_public.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/*
* === Primitives
*
* All primitive values but strings.
*
* "foreign" values are also here, see `v7_mk_foreign()`.
*/
#ifndef CS_V7_SRC_PRIMITIVE_PUBLIC_H_
#define CS_V7_SRC_PRIMITIVE_PUBLIC_H_
/* Amalgamated: #include "v7/src/core_public.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
/* Make numeric primitive value */
NOINSTR v7_val_t v7_mk_number(struct v7 *v7, double num);
/*
* Returns number value stored in `v7_val_t` as `double`.
*
* Returns NaN for non-numbers.
*/
NOINSTR double v7_get_double(struct v7 *v7, v7_val_t v);
/*
* Returns number value stored in `v7_val_t` as `int`. If the number value is
* not an integer, the fraction part will be discarded.
*
* If the given value is a non-number, or NaN, the result is undefined.
*/
NOINSTR int v7_get_int(struct v7 *v7, v7_val_t v);
/* Returns true if given value is a primitive number value */
int v7_is_number(v7_val_t v);
/* Make boolean primitive value (either `true` or `false`) */
NOINSTR v7_val_t v7_mk_boolean(struct v7 *v7, int is_true);
/*
* Returns boolean stored in `v7_val_t`:
* 0 for `false` or non-boolean, non-0 for `true`
*/
NOINSTR int v7_get_bool(struct v7 *v7, v7_val_t v);
/* Returns true if given value is a primitive boolean value */
int v7_is_boolean(v7_val_t v);
/*
* Make `null` primitive value.
*
* NOTE: this function is deprecated and will be removed in future releases.
* Use `V7_NULL` instead.
*/
NOINSTR v7_val_t v7_mk_null(void);
/* Returns true if given value is a primitive `null` value */
int v7_is_null(v7_val_t v);
/*
* Make `undefined` primitive value.
*
* NOTE: this function is deprecated and will be removed in future releases.
* Use `V7_UNDEFINED` instead.
*/
NOINSTR v7_val_t v7_mk_undefined(void);
/* Returns true if given value is a primitive `undefined` value */
int v7_is_undefined(v7_val_t v);
/*
* Make JavaScript value that holds C/C++ `void *` pointer.
*
* A foreign value is completely opaque and JS code cannot do anything useful
* with it except holding it in properties and passing it around.
* It behaves like a sealed object with no properties.
*
* NOTE:
* Only valid pointers (as defined by each supported architecture) will fully
* preserved. In particular, all supported 64-bit architectures (x86_64, ARM-64)
* actually define a 48-bit virtual address space.
* Foreign values will be sign-extended as required, i.e creating a foreign
* value of something like `(void *) -1` will work as expected. This is
* important because in some 64-bit OSs (e.g. Solaris) the user stack grows
* downwards from the end of the address space.
*
* If you need to store exactly sizeof(void*) bytes of raw data where
* `sizeof(void*)` >= 8, please use byte arrays instead.
*/
NOINSTR v7_val_t v7_mk_foreign(struct v7 *v7, void *ptr);
/*
* Returns `void *` pointer stored in `v7_val_t`.
*
* Returns NULL if the value is not a foreign pointer.
*/
NOINSTR void *v7_get_ptr(struct v7 *v7, v7_val_t v);
/* Returns true if given value holds `void *` pointer */
int v7_is_foreign(v7_val_t v);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_PRIMITIVE_PUBLIC_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/primitive.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_PRIMITIVE_H_
#define CS_V7_SRC_PRIMITIVE_H_
/* Amalgamated: #include "v7/src/primitive_public.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* Returns true if given value is a number, not NaN and not Infinity. */
V7_PRIVATE int is_finite(struct v7 *v7, v7_val_t v);
V7_PRIVATE val_t pointer_to_value(void *p);
V7_PRIVATE void *get_ptr(val_t v);
#endif /* CS_V7_SRC_PRIMITIVE_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/string_public.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/*
* === Strings
*/
#ifndef CS_V7_SRC_STRING_PUBLIC_H_
#define CS_V7_SRC_STRING_PUBLIC_H_
/* Amalgamated: #include "v7/src/core_public.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
/*
* Creates a string primitive value.
* `str` must point to the utf8 string of length `len`.
* If `len` is ~0, `str` is assumed to be NUL-terminated and `strlen(str)` is
* used.
*
* If `copy` is non-zero, the string data is copied and owned by the GC. The
* caller can free the string data afterwards. Otherwise (`copy` is zero), the
* caller owns the string data, and is responsible for not freeing it while it
* is used.
*/
v7_val_t v7_mk_string(struct v7 *v7, const char *str, size_t len, int copy);
/* Returns true if given value is a primitive string value */
int v7_is_string(v7_val_t v);
/*
* Returns a pointer to the string stored in `v7_val_t`.
*
* String length returned in `len`, which is allowed to be NULL. Returns NULL
* if the value is not a string.
*
* JS strings can contain embedded NUL chars and may or may not be NUL
* terminated.
*
* CAUTION: creating new JavaScript object, array, or string may kick in a
* garbage collector, which in turn may relocate string data and invalidate
* pointer returned by `v7_get_string()`.
*
* Short JS strings are embedded inside the `v7_val_t` value itself. This is why
* a pointer to a `v7_val_t` is required. It also means that the string data
* will become invalid once that `v7_val_t` value goes out of scope.
*/
const char *v7_get_string(struct v7 *v7, v7_val_t *v, size_t *len);
/*
* Returns a pointer to the string stored in `v7_val_t`.
*
* Returns NULL if the value is not a string or if the string is not compatible
* with a C string.
*
* C compatible strings contain exactly one NUL char, in terminal position.
*
* All strings owned by the V7 engine (see `v7_mk_string()`) are guaranteed to
* be NUL terminated. Out of these, those that don't include embedded NUL chars
* are guaranteed to be C compatible.
*/
const char *v7_get_cstring(struct v7 *v7, v7_val_t *v);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_STRING_PUBLIC_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/string.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_STRING_H_
#define CS_V7_SRC_STRING_H_
/* Amalgamated: #include "v7/src/string_public.h" */
/* Amalgamated: #include "v7/src/core.h" */
/*
* Size of the extra space for strings mbuf that is needed to avoid frequent
* reallocations
*/
#define _V7_STRING_BUF_RESERVE 500
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err v7_char_code_at(struct v7 *v7, v7_val_t s, v7_val_t at,
double *res);
V7_PRIVATE int s_cmp(struct v7 *, val_t a, val_t b);
V7_PRIVATE val_t s_concat(struct v7 *, val_t, val_t);
/*
* Convert a C string to to an unsigned integer.
* `ok` will be set to true if the string conforms to
* an unsigned long.
*/
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err str_to_ulong(struct v7 *v7, val_t v, int *ok,
unsigned long *res);
/*
* Convert a V7 string to to an unsigned integer.
* `ok` will be set to true if the string conforms to
* an unsigned long.
*
* Use it if only you need strong conformity of the value to an integer;
* otherwise, use `to_long()` or `to_number_v()` instead.
*/
V7_PRIVATE unsigned long cstr_to_ulong(const char *s, size_t len, int *ok);
enum embstr_flags {
EMBSTR_ZERO_TERM = (1 << 0),
EMBSTR_UNESCAPE = (1 << 1),
};
V7_PRIVATE void embed_string(struct mbuf *m, size_t offset, const char *p,
size_t len, uint8_t /*enum embstr_flags*/ flags);
V7_PRIVATE size_t unescape(const char *s, size_t len, char *to);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_STRING_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/exceptions_public.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/*
* === Exceptions
*/
#ifndef CS_V7_SRC_EXCEPTIONS_PUBLIC_H_
#define CS_V7_SRC_EXCEPTIONS_PUBLIC_H_
/* Amalgamated: #include "v7/src/core_public.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
/* Throw an exception with an already existing value. */
WARN_UNUSED_RESULT
enum v7_err v7_throw(struct v7 *v7, v7_val_t v);
/*
* Throw an exception with given formatted message.
*
* Pass "Error" as typ for a generic error.
*/
WARN_UNUSED_RESULT
enum v7_err v7_throwf(struct v7 *v7, const char *typ, const char *err_fmt, ...);
/*
* Rethrow the currently thrown object. In fact, it just returns
* V7_EXEC_EXCEPTION.
*/
WARN_UNUSED_RESULT
enum v7_err v7_rethrow(struct v7 *v7);
/*
* Returns the value that is being thrown at the moment, or `undefined` if
* nothing is being thrown. If `is_thrown` is not `NULL`, it will be set
* to either 0 or 1, depending on whether something is thrown at the moment.
*/
v7_val_t v7_get_thrown_value(struct v7 *v7, unsigned char *is_thrown);
/* Clears currently thrown value, if any. */
void v7_clear_thrown_value(struct v7 *v7);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_EXCEPTIONS_PUBLIC_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/exceptions.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_EXCEPTIONS_H_
#define CS_V7_SRC_EXCEPTIONS_H_
/* Amalgamated: #include "v7/src/exceptions_public.h" */
/* Amalgamated: #include "v7/src/core.h" */
/*
* Try to perform some arbitrary call, and if the result is other than `V7_OK`,
* "throws" an error with `V7_THROW()`
*/
#define V7_TRY2(call, clean_label) \
do { \
enum v7_err _e = call; \
V7_CHECK2(_e == V7_OK, _e, clean_label); \
} while (0)
/*
* Sets return value to the provided one, and `goto`s `clean`.
*
* For this to work, you should have local `enum v7_err rcode` variable,
* and a `clean` label.
*/
#define V7_THROW2(err_code, clean_label) \
do { \
(void) v7; \
rcode = (err_code); \
assert(rcode != V7_OK); \
assert(!v7_is_undefined(v7->vals.thrown_error) && v7->is_thrown); \
goto clean_label; \
} while (0)
/*
* Checks provided condition `cond`, and if it's false, then "throws"
* provided `err_code` (see `V7_THROW()`)
*/
#define V7_CHECK2(cond, err_code, clean_label) \
do { \
if (!(cond)) { \
V7_THROW2(err_code, clean_label); \
} \
} while (0)
/*
* Checks provided condition `cond`, and if it's false, then "throws"
* internal error
*
* TODO(dfrank): it would be good to have formatted string: then, we can
* specify file and line.
*/
#define V7_CHECK_INTERNAL2(cond, clean_label) \
do { \
if (!(cond)) { \
enum v7_err __rcode = v7_throwf(v7, "Error", "Internal error"); \
(void) __rcode; \
V7_THROW2(V7_INTERNAL_ERROR, clean_label); \
} \
} while (0)
/*
* Shortcuts for the macros above, but they assume the clean label `clean`.
*/
#define V7_TRY(call) V7_TRY2(call, clean)
#define V7_THROW(err_code) V7_THROW2(err_code, clean)
#define V7_CHECK(cond, err_code) V7_CHECK2(cond, err_code, clean)
#define V7_CHECK_INTERNAL(cond) V7_CHECK_INTERNAL2(cond, clean)
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
/*
* At the moment, most of the exception-related functions are public, and are
* declared in `exceptions_public.h`
*/
/*
* Create an instance of the exception with type `typ` (see `TYPE_ERROR`,
* `SYNTAX_ERROR`, etc), and message `msg`.
*/
V7_PRIVATE enum v7_err create_exception(struct v7 *v7, const char *typ,
const char *msg, val_t *res);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_EXCEPTIONS_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/object.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_OBJECT_H_
#define CS_V7_SRC_OBJECT_H_
/* Amalgamated: #include "v7/src/object_public.h" */
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/core.h" */
V7_PRIVATE val_t mk_object(struct v7 *v7, val_t prototype);
V7_PRIVATE val_t v7_object_to_value(struct v7_object *o);
V7_PRIVATE struct v7_generic_object *get_generic_object_struct(val_t v);
/*
* Returns pointer to the struct representing an object.
* Given value must be an object (the caller can verify it
* by calling `v7_is_object()`)
*/
V7_PRIVATE struct v7_object *get_object_struct(v7_val_t v);
/*
* Return true if given value is a JavaScript object (will return
* false for function)
*/
V7_PRIVATE int v7_is_generic_object(v7_val_t v);
V7_PRIVATE struct v7_property *v7_mk_property(struct v7 *v7);
V7_PRIVATE struct v7_property *v7_get_own_property2(struct v7 *v7, val_t obj,
const char *name,
size_t len,
v7_prop_attr_t attrs);
V7_PRIVATE struct v7_property *v7_get_own_property(struct v7 *v7, val_t obj,
const char *name,
size_t len);
/*
* If `len` is -1/MAXUINT/~0, then `name` must be 0-terminated
*
* Returns a pointer to the property structure, given an object and a name of
* the property as a pointer to string buffer and length.
*
* See also `v7_get_property_v`
*/
V7_PRIVATE struct v7_property *v7_get_property(struct v7 *v7, val_t obj,
const char *name, size_t len);
/*
* Just like `v7_get_property`, but takes name as a `v7_val_t`
*/
V7_PRIVATE enum v7_err v7_get_property_v(struct v7 *v7, val_t obj,
v7_val_t name,
struct v7_property **res);
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err v7_get_throwing_v(struct v7 *v7, v7_val_t obj,
v7_val_t name, v7_val_t *res);
V7_PRIVATE void v7_destroy_property(struct v7_property **p);
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err v7_invoke_setter(struct v7 *v7, struct v7_property *prop,
val_t obj, val_t val);
/*
* Like `set_property`, but takes property name as a `val_t`
*/
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err set_property_v(struct v7 *v7, val_t obj, val_t name,
val_t val, struct v7_property **res);
/*
* Like JavaScript assignment: set a property with given `name` + `len` at
* the object `obj` to value `val`. Returns a property through the `res`
* (which may be `NULL` if return value is not required)
*/
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err set_property(struct v7 *v7, val_t obj, const char *name,
size_t len, v7_val_t val,
struct v7_property **res);
/*
* Like `def_property()`, but takes property name as a `val_t`
*/
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err def_property_v(struct v7 *v7, val_t obj, val_t name,
v7_prop_attr_desc_t attrs_desc, val_t val,
uint8_t as_assign,
struct v7_property **res);
/*
* Define object property, similar to JavaScript `Object.defineProperty()`.
*
* Just like public `v7_def()`, but returns `enum v7_err`, and therefore can
* throw.
*
* Additionally, takes param `as_assign`: if it is non-zero, it behaves
* similarly to plain JavaScript assignment in terms of some exception-related
* corner cases.
*
* `res` may be `NULL`.
*/
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err def_property(struct v7 *v7, val_t obj, const char *name,
size_t len, v7_prop_attr_desc_t attrs_desc,
v7_val_t val, uint8_t as_assign,
struct v7_property **res);
V7_PRIVATE int set_method(struct v7 *v7, val_t obj, const char *name,
v7_cfunction_t *func, int num_args);
V7_PRIVATE int set_cfunc_prop(struct v7 *v7, val_t o, const char *name,
v7_cfunction_t *func);
/* Return address of property value or NULL if the passed property is NULL */
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err v7_property_value(struct v7 *v7, val_t obj,
struct v7_property *p, val_t *res);
#if V7_ENABLE__Proxy
/*
* Additional context for property iteration of a proxied object, see
* `v7_next_prop()`.
*/
struct prop_iter_proxy_ctx {
/* Proxy target object */
v7_val_t target_obj;
/* Proxy handler object */
v7_val_t handler_obj;
/*
* array returned by the `ownKeys` callback, valid if only `has_own_keys` is
* set
*/
v7_val_t own_keys;
/*
* callback to get property descriptor, one of these:
* - a JS or cfunction `getOwnPropertyDescriptor`
* (if `has_get_own_prop_desc_C` is not set);
* - a C callback `v7_get_own_prop_desc_cb_t`.
* (if `has_get_own_prop_desc_C` is set);
*/
v7_val_t get_own_prop_desc;
/*
* if `has_own_keys` is set, `own_key_idx` represents next index in the
* `own_keys` array
*/
unsigned own_key_idx : 29;
/* if set, `own_keys` is valid */
unsigned has_own_keys : 1;
/* if set, `get_own_prop_desc` is valid */
unsigned has_get_own_prop_desc : 1;
/*
* if set, `get_own_prop_desc` is a C callback `has_get_own_prop_desc_C`, not
* a JS callback
*/
unsigned has_get_own_prop_desc_C : 1;
};
#endif
/*
* Like public function `v7_init_prop_iter_ctx()`, but it takes additional
* argument `proxy_transp`; if it is zero, and the given `obj` is a Proxy, it
* will iterate the properties of the proxy itself, not the Proxy's target.
*/
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err init_prop_iter_ctx(struct v7 *v7, v7_val_t obj,
int proxy_transp,
struct prop_iter_ctx *ctx);
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err next_prop(struct v7 *v7, struct prop_iter_ctx *ctx,
v7_val_t *name, v7_val_t *value,
v7_prop_attr_t *attrs, int *ok);
/*
* Set new prototype `proto` for the given object `obj`. Returns `0` at
* success, `-1` at failure (it may fail if given `obj` is a function object:
* it's impossible to change function object's prototype)
*/
V7_PRIVATE int obj_prototype_set(struct v7 *v7, struct v7_object *obj,
struct v7_object *proto);
/*
* Given a pointer to the object structure, returns a
* pointer to the prototype object, or `NULL` if there is
* no prototype.
*/
V7_PRIVATE struct v7_object *obj_prototype(struct v7 *v7,
struct v7_object *obj);
V7_PRIVATE int is_prototype_of(struct v7 *v7, val_t o, val_t p);
/* Get the property holding user data and destructor, or NULL */
V7_PRIVATE struct v7_property *get_user_data_property(v7_val_t obj);
#endif /* CS_V7_SRC_OBJECT_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/exec_public.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/*
* === Execution of JavaScript code
*/
#ifndef CS_V7_SRC_EXEC_PUBLIC_H_
#define CS_V7_SRC_EXEC_PUBLIC_H_
/* Amalgamated: #include "v7/src/core_public.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
/*
* Execute JavaScript `js_code`. The result of evaluation is stored in
* the `result` variable.
* The code can be either a JavaScript source or a precompiled bytecode.
*
* Return:
*
* - V7_OK on success. `result` contains the result of execution.
* - V7_SYNTAX_ERROR if `js_code` in not a valid code. `result` is undefined.
* - V7_EXEC_EXCEPTION if `js_code` threw an exception. `result` stores
* an exception object.
* - V7_AST_TOO_LARGE if `js_code` contains an AST segment longer than 16 bit.
* `result` is undefined. To avoid this error, build V7 with V7_LARGE_AST.
*/
WARN_UNUSED_RESULT
enum v7_err v7_exec(struct v7 *v7, const char *js_code, v7_val_t *result);
/*
* Options for `v7_exec_opt()`. To get default options, like `v7_exec()` uses,
* just zero out this struct.
*/
struct v7_exec_opts {
/* Filename, used for stack traces only */
const char *filename;
/*
* Object to be used as `this`. Note: when it is zeroed out, i.e. it's a
* number `0`, the `undefined` value is assumed. It means that it's
* impossible to actually use the number `0` as `this` object, but it makes
* little sense anyway.
*/
v7_val_t this_obj;
/* Whether the given `js_code` should be interpreted as JSON, not JS code */
unsigned is_json : 1;
};
/*
* Customizable version of `v7_exec()`: allows to specify various options, see
* `struct v7_exec_opts`.
*/
enum v7_err v7_exec_opt(struct v7 *v7, const char *js_code,
const struct v7_exec_opts *opts, v7_val_t *res);
/*
* Like v7_exec but it expects an explicit length instead of treating the code
* as a null terminated string.
*
* The code can be either a JS source or a precompiled bytecode.
*/
WARN_UNUSED_RESULT
enum v7_err v7_exec_buf(struct v7 *v7, const char *js_code, size_t len,
v7_val_t *result);
/*
* Same as `v7_exec()`, but loads source code from `path` file.
*/
WARN_UNUSED_RESULT
enum v7_err v7_exec_file(struct v7 *v7, const char *path, v7_val_t *result);
/*
* Parse `str` and store corresponding JavaScript object in `res` variable.
* String `str` should be '\0'-terminated.
* Return value and semantic is the same as for `v7_exec()`.
*/
WARN_UNUSED_RESULT
enum v7_err v7_parse_json(struct v7 *v7, const char *str, v7_val_t *res);
/*
* Same as `v7_parse_json()`, but loads JSON string from `path`.
*/
WARN_UNUSED_RESULT
enum v7_err v7_parse_json_file(struct v7 *v7, const char *path, v7_val_t *res);
#if !defined(V7_NO_COMPILER)
/*
* Compile JavaScript code `js_code` into the byte code and write generated
* byte code into opened file stream `fp`. If `generate_binary_output` is 0,
* then generated byte code is in human-readable text format. Otherwise, it is
* in the binary format, suitable for execution by V7 instance.
* NOTE: `fp` must be a valid, opened, writable file stream.
*/
WARN_UNUSED_RESULT
enum v7_err v7_compile(const char *js_code, int generate_binary_output,
int use_bcode, FILE *fp);
#endif /* V7_NO_COMPILER */
/*
* Call function `func` with arguments `args`, using `this_obj` as `this`.
* `args` should be an array containing arguments or `undefined`.
*
* `res` can be `NULL` if return value is not required.
*/
WARN_UNUSED_RESULT
enum v7_err v7_apply(struct v7 *v7, v7_val_t func, v7_val_t this_obj,
v7_val_t args, v7_val_t *res);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_EXEC_PUBLIC_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/exec.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_EXEC_H_
#define CS_V7_SRC_EXEC_H_
/* Amalgamated: #include "v7/src/exec_public.h" */
/* Amalgamated: #include "v7/src/core.h" */
#if !defined(V7_NO_COMPILER)
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
/*
* At the moment, all exec-related functions are public, and are declared in
* `exec_public.h`
*/
WARN_UNUSED_RESULT
enum v7_err _v7_compile(const char *js_code, size_t js_code_size,
int generate_binary_output, int use_bcode, FILE *fp);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* V7_NO_COMPILER */
#endif /* CS_V7_SRC_EXEC_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/array_public.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/*
* === Arrays
*/
#ifndef CS_V7_SRC_ARRAY_PUBLIC_H_
#define CS_V7_SRC_ARRAY_PUBLIC_H_
/* Amalgamated: #include "v7/src/core_public.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
/* Make an empty array object */
v7_val_t v7_mk_array(struct v7 *v7);
/* Returns true if given value is an array object */
int v7_is_array(struct v7 *v7, v7_val_t v);
/* Returns length on an array. If `arr` is not an array, 0 is returned. */
unsigned long v7_array_length(struct v7 *v7, v7_val_t arr);
/* Insert value `v` in array `arr` at the end of the array. */
int v7_array_push(struct v7 *, v7_val_t arr, v7_val_t v);
/*
* Like `v7_array_push()`, but "returns" value through the `res` pointer
* argument. `res` is allowed to be `NULL`.
*
* Caller should check the error code returned, and if it's something other
* than `V7_OK`, perform cleanup and return this code further.
*/
WARN_UNUSED_RESULT
enum v7_err v7_array_push_throwing(struct v7 *v7, v7_val_t arr, v7_val_t v,
int *res);
/*
* Return array member at index `index`. If `index` is out of bounds, undefined
* is returned.
*/
v7_val_t v7_array_get(struct v7 *, v7_val_t arr, unsigned long index);
/* Insert value `v` into `arr` at index `index`. */
int v7_array_set(struct v7 *v7, v7_val_t arr, unsigned long index, v7_val_t v);
/*
* Like `v7_array_set()`, but "returns" value through the `res` pointer
* argument. `res` is allowed to be `NULL`.
*
* Caller should check the error code returned, and if it's something other
* than `V7_OK`, perform cleanup and return this code further.
*/
WARN_UNUSED_RESULT
enum v7_err v7_array_set_throwing(struct v7 *v7, v7_val_t arr,
unsigned long index, v7_val_t v, int *res);
/* Delete value in array `arr` at index `index`, if it exists. */
void v7_array_del(struct v7 *v7, v7_val_t arr, unsigned long index);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_ARRAY_PUBLIC_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/array.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_ARRAY_H_
#define CS_V7_SRC_ARRAY_H_
/* Amalgamated: #include "v7/src/array_public.h" */
/* Amalgamated: #include "v7/src/core.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
V7_PRIVATE v7_val_t v7_mk_dense_array(struct v7 *v7);
V7_PRIVATE val_t
v7_array_get2(struct v7 *v7, v7_val_t arr, unsigned long index, int *has);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_ARRAY_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/conversion_public.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/*
* === Conversion
*/
#ifndef CS_V7_SRC_CONVERSION_PUBLIC_H_
#define CS_V7_SRC_CONVERSION_PUBLIC_H_
/* Amalgamated: #include "v7/src/core_public.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
/* Stringify mode, see `v7_stringify()` and `v7_stringify_throwing()` */
enum v7_stringify_mode {
V7_STRINGIFY_DEFAULT,
V7_STRINGIFY_JSON,
V7_STRINGIFY_DEBUG,
};
/*
* Generate string representation of the JavaScript value `val` into a buffer
* `buf`, `len`. If `len` is too small to hold a generated string,
* `v7_stringify()` allocates required memory. In that case, it is caller's
* responsibility to free the allocated buffer. Generated string is guaranteed
* to be 0-terminated.
*
* Available stringification modes are:
*
* - `V7_STRINGIFY_DEFAULT`:
* Convert JS value to string, using common JavaScript semantics:
* - If value is an object:
* - call `toString()`;
* - If `toString()` returned non-primitive value, call `valueOf()`;
* - If `valueOf()` returned non-primitive value, throw `TypeError`.
* - Now we have a primitive, and if it's not a string, then stringify it.
*
* - `V7_STRINGIFY_JSON`:
* Generate JSON output
*
* - `V7_STRINGIFY_DEBUG`:
* Mostly like JSON, but will not omit non-JSON objects like functions.
*
* Example code:
*
* char buf[100], *p;
* p = v7_stringify(v7, obj, buf, sizeof(buf), V7_STRINGIFY_DEFAULT);
* printf("JSON string: [%s]\n", p);
* if (p != buf) {
* free(p);
* }
*/
char *v7_stringify(struct v7 *v7, v7_val_t v, char *buf, size_t len,
enum v7_stringify_mode mode);
/*
* Like `v7_stringify()`, but "returns" value through the `res` pointer
* argument. `res` must not be `NULL`.
*
* Caller should check the error code returned, and if it's something other
* than `V7_OK`, perform cleanup and return this code further.
*/
WARN_UNUSED_RESULT
enum v7_err v7_stringify_throwing(struct v7 *v7, v7_val_t v, char *buf,
size_t size, enum v7_stringify_mode mode,
char **res);
/*
* A shortcut for `v7_stringify()` with `V7_STRINGIFY_JSON`
*/
#define v7_to_json(a, b, c, d) v7_stringify(a, b, c, d, V7_STRINGIFY_JSON)
/* Returns true if given value evaluates to true, as in `if (v)` statement. */
int v7_is_truthy(struct v7 *v7, v7_val_t v);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_CONVERSION_PUBLIC_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/conversion.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_CONVERSION_H_
#define CS_V7_SRC_CONVERSION_H_
/* Amalgamated: #include "v7/src/conversion_public.h" */
/* Amalgamated: #include "v7/src/core.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
/*
* Conversion API
* ==============
*
* - If you need to convert any JS value to string using common JavaScript
* semantics, use `to_string()`, which can convert to both `v7_val_t` or your
* C buffer.
*
* - If you need to convert any JS value to number using common JavaScript
* semantics, use `to_number_v()`;
*
* - If you need to convert any JS value to primitive, without forcing it to
* string or number, use `to_primitive()` (see comments for this function for
* details);
*
* - If you have a primitive value, and you want to convert it to either string
* or number, you can still use functions above: `to_string()` and
* `to_number_v()`. But if you want to save a bit of work, use:
* - `primitive_to_str()`
* - `primitive_to_number()`
*
* In fact, these are a bit lower level functions, which are used by
* `to_string()` and `to_number_v()` after converting value to
* primitive.
*
* - If you want to call `valueOf()` on the object, use `obj_value_of()`;
* - If you want to call `toString()` on the object, use `obj_to_string()`;
*
* - If you need to convert any JS value to boolean using common JavaScript
* semantics (as in the expression `if (v)` or `Boolean(v)`), use
* `to_boolean_v()`.
*
* - If you want to get the JSON representation of a value, use
* `to_json_or_debug()`, passing `0` as `is_debug` : writes data to your C
* buffer;
*
* - There is one more kind of representation: `DEBUG`. It's very similar to
* JSON, but it will not omit non-JSON values, such as functions. Again, use
* `to_json_or_debug()`, but pass `1` as `is_debug` this time: writes data to
* your C buffer;
*
* Additionally, for any kind of to-string conversion into C buffer, you can
* use a convenience wrapper function (mostly for public API), which can
* allocate the buffer for you:
*
* - `v7_stringify_throwing()`;
* - `v7_stringify()` : the same as above, but doesn't throw.
*
* There are a couple of more specific conversions, which I'd like to probably
* refactor or remove in the future:
*
* - `to_long()` : if given value is `undefined`, returns provided default
* value; otherwise, converts value to number, and then truncates to `long`.
* - `str_to_ulong()` : converts the value to string, and tries to parse it as
* an integer. Use it if only you need strong conformity ov the value to an
* integer (currently, it's used only when examining keys of array object)
*
* ----------------------------------------------------------------------------
*
* TODO(dfrank):
* - Rename functions like `v7_get_double(v7, )`, `get_object_struct()` to
*something
* that will clearly identify that they convert to some C entity, not
* `v7_val_t`
* - Maybe make `to_string()` private? But then, there will be no way
* in public API to convert value to `v7_val_t` string, so, for now
* it's here.
* - When we agree on what goes to public API, and what does not, write
* similar conversion guide for public API (in `conversion_public.h`)
*/
/*
* Convert any JS value to number, using common JavaScript semantics:
*
* - If value is an object:
* - call `valueOf()`;
* - If `valueOf()` returned non-primitive value, call `toString()`;
* - If `toString()` returned non-primitive value, throw `TypeError`.
* - Now we have a primitive, and if it's not a number, then:
* - If `undefined`, return `NaN`
* - If `null`, return 0.0
* - If boolean, return either 1 or 0
* - If string, try to parse it.
*/
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err to_number_v(struct v7 *v7, v7_val_t v, v7_val_t *res);
/*
* Convert any JS value to string, using common JavaScript semantics,
* see `v7_stringify()` and `V7_STRINGIFY_DEFAULT`.
*
* This function can return multiple things:
*
* - String as a `v7_val_t` (if `res` is not `NULL`)
* - String copied to buffer `buf` with max size `buf_size` (if `buf` is not
* `NULL`)
* - Length of actual string, independently of `buf_size` (if `res_len` is not
* `NULL`)
*
* The rationale of having multiple formats of returned value is the following:
*
* Initially, to-string conversion always returned `v7_val_t`. But it turned
* out that there are situations where such an approach adds useless pressure
* on GC: e.g. when converting `undefined` to string, and the caller actually
* needs a C buffer, not a `v7_val_t`.
*
* Always returning string through `buf`+`buf_size` is bad as well: if we
* convert from object to string, and either `toString()` or `valueOf()`
* returned string, then we'd have to get string data from it, write to buffer,
* and if caller actually need `v7_val_t`, then it will have to create new
* instance of the same string: again, useless GC pressure.
*
* So, we have to use the combined approach. This function will make minimal
* work depending on give `res` and `buf`.
*/
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err to_string(struct v7 *v7, v7_val_t v, v7_val_t *res,
char *buf, size_t buf_size, size_t *res_len);
/*
* Convert value to primitive, if it's not already.
*
* For object-to-primitive conversion, each object in JavaScript has two
* methods: `toString()` and `valueOf()`.
*
* When converting object to string, JavaScript does the following:
* - call `toString()`;
* - If `toString()` returned non-primitive value, call `valueOf()`;
* - If `valueOf()` returned non-primitive value, throw `TypeError`.
*
* When converting object to number, JavaScript calls the same functions,
* but in reverse:
* - call `valueOf()`;
* - If `valueOf()` returned non-primitive value, call `toString()`;
* - If `toString()` returned non-primitive value, throw `TypeError`.
*
* This function `to_primitive()` performs either type of conversion,
* depending on the `hint` argument (see `enum to_primitive_hint`).
*/
enum to_primitive_hint {
/* Call `valueOf()` first, then `toString()` if needed */
V7_TO_PRIMITIVE_HINT_NUMBER,
/* Call `toString()` first, then `valueOf()` if needed */
V7_TO_PRIMITIVE_HINT_STRING,
/* STRING for Date, NUMBER for everything else */
V7_TO_PRIMITIVE_HINT_AUTO,
};
WARN_UNUSED_RESULT
enum v7_err to_primitive(struct v7 *v7, v7_val_t v, enum to_primitive_hint hint,
v7_val_t *res);
/*
* Convert primitive value to string, using common JavaScript semantics. If
* you need to convert any value to string (either object or primitive),
* see `to_string()` or `v7_stringify_throwing()`.
*
* This function can return multiple things:
*
* - String as a `v7_val_t` (if `res` is not `NULL`)
* - String copied to buffer `buf` with max size `buf_size` (if `buf` is not
* `NULL`)
* - Length of actual string, independently of `buf_size` (if `res_len` is not
* `NULL`)
*/
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err primitive_to_str(struct v7 *v7, val_t v, val_t *res,
char *buf, size_t buf_size,
size_t *res_len);
/*
* Convert primitive value to number, using common JavaScript semantics. If you
* need to convert any value to number (either object or primitive), see
* `to_number_v()`
*/
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err primitive_to_number(struct v7 *v7, val_t v, val_t *res);
/*
* Convert value to JSON or "debug" representation, depending on whether
* `is_debug` is non-zero. The "debug" is the same as JSON, but non-JSON values
* (functions, `undefined`, etc) will not be omitted.
*
* See also `v7_stringify()`, `v7_stringify_throwing()`.
*/
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err to_json_or_debug(struct v7 *v7, val_t v, char *buf,
size_t size, size_t *res_len,
uint8_t is_debug);
/*
* Calls `valueOf()` on given object `v`
*/
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err obj_value_of(struct v7 *v7, val_t v, val_t *res);
/*
* Calls `toString()` on given object `v`
*/
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err obj_to_string(struct v7 *v7, val_t v, val_t *res);
/*
* If given value is `undefined`, returns `default_value`; otherwise,
* converts value to number, and then truncates to `long`.
*/
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err to_long(struct v7 *v7, val_t v, long default_value,
long *res);
/*
* Converts value to boolean as in the expression `if (v)` or `Boolean(v)`.
*
* NOTE: it can't throw (even if the given value is an object with `valueOf()`
* that throws), so it returns `val_t` directly.
*/
WARN_UNUSED_RESULT
V7_PRIVATE val_t to_boolean_v(struct v7 *v7, val_t v);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_CONVERSION_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/varint.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_VARINT_H_
#define CS_V7_SRC_VARINT_H_
/* Amalgamated: #include "v7/src/internal.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
V7_PRIVATE int encode_varint(size_t len, unsigned char *p);
V7_PRIVATE size_t decode_varint(const unsigned char *p, int *llen);
V7_PRIVATE int calc_llen(size_t len);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_VARINT_H_ */
#ifdef V7_MODULE_LINES
#line 1 "common/cs_strtod.h"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_COMMON_CS_STRTOD_H_
#define CS_COMMON_CS_STRTOD_H_
#ifdef __cplusplus
extern "C" {
#endif
double cs_strtod(const char *str, char **endptr);
#ifdef __cplusplus
}
#endif
#endif /* CS_COMMON_CS_STRTOD_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/ast.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_AST_H_
#define CS_V7_SRC_AST_H_
#include
/* Amalgamated: #include "common/mbuf.h" */
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/core.h" */
#if !defined(V7_NO_COMPILER)
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
#define BIN_AST_SIGNATURE "V\007ASTV10"
enum ast_tag {
AST_NOP,
AST_SCRIPT,
AST_VAR,
AST_VAR_DECL,
AST_FUNC_DECL,
AST_IF,
AST_FUNC,
AST_ASSIGN,
AST_REM_ASSIGN,
AST_MUL_ASSIGN,
AST_DIV_ASSIGN,
AST_XOR_ASSIGN,
AST_PLUS_ASSIGN,
AST_MINUS_ASSIGN,
AST_OR_ASSIGN,
AST_AND_ASSIGN,
AST_LSHIFT_ASSIGN,
AST_RSHIFT_ASSIGN,
AST_URSHIFT_ASSIGN,
AST_NUM,
AST_IDENT,
AST_STRING,
AST_REGEX,
AST_LABEL,
AST_SEQ,
AST_WHILE,
AST_DOWHILE,
AST_FOR,
AST_FOR_IN,
AST_COND,
AST_DEBUGGER,
AST_BREAK,
AST_LABELED_BREAK,
AST_CONTINUE,
AST_LABELED_CONTINUE,
AST_RETURN,
AST_VALUE_RETURN,
AST_THROW,
AST_TRY,
AST_SWITCH,
AST_CASE,
AST_DEFAULT,
AST_WITH,
AST_LOGICAL_OR,
AST_LOGICAL_AND,
AST_OR,
AST_XOR,
AST_AND,
AST_EQ,
AST_EQ_EQ,
AST_NE,
AST_NE_NE,
AST_LE,
AST_LT,
AST_GE,
AST_GT,
AST_IN,
AST_INSTANCEOF,
AST_LSHIFT,
AST_RSHIFT,
AST_URSHIFT,
AST_ADD,
AST_SUB,
AST_REM,
AST_MUL,
AST_DIV,
AST_POSITIVE,
AST_NEGATIVE,
AST_NOT,
AST_LOGICAL_NOT,
AST_VOID,
AST_DELETE,
AST_TYPEOF,
AST_PREINC,
AST_PREDEC,
AST_POSTINC,
AST_POSTDEC,
AST_MEMBER,
AST_INDEX,
AST_CALL,
AST_NEW,
AST_ARRAY,
AST_OBJECT,
AST_PROP,
AST_GETTER,
AST_SETTER,
AST_THIS,
AST_TRUE,
AST_FALSE,
AST_NULL,
AST_UNDEFINED,
AST_USE_STRICT,
AST_MAX_TAG
};
struct ast {
struct mbuf mbuf;
int refcnt;
int has_overflow;
};
typedef unsigned long ast_off_t;
#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 8
#define GCC_HAS_PRAGMA_DIAGNOSTIC
#endif
#ifdef GCC_HAS_PRAGMA_DIAGNOSTIC
/*
* TODO(mkm): GCC complains that bitfields on char are not standard
*/
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#endif
struct ast_node_def {
#if !V7_DISABLE_AST_TAG_NAMES
const char *name; /* tag name, for debugging and serialization */
#endif
unsigned char has_varint : 1; /* has a varint body */
unsigned char has_inlined : 1; /* inlined data whose size is in varint fld */
unsigned char num_skips : 3; /* number of skips */
unsigned char num_subtrees : 3; /* number of fixed subtrees */
};
extern const struct ast_node_def ast_node_defs[];
#if V7_ENABLE_FOOTPRINT_REPORT
extern const size_t ast_node_defs_size;
extern const size_t ast_node_defs_count;
#endif
#ifdef GCC_HAS_PRAGMA_DIAGNOSTIC
#pragma GCC diagnostic pop
#endif
enum ast_which_skip {
AST_END_SKIP = 0,
AST_VAR_NEXT_SKIP = 1,
AST_SCRIPT_FIRST_VAR_SKIP = AST_VAR_NEXT_SKIP,
AST_FOR_BODY_SKIP = 1,
AST_DO_WHILE_COND_SKIP = 1,
AST_END_IF_TRUE_SKIP = 1,
AST_TRY_CATCH_SKIP = 1,
AST_TRY_FINALLY_SKIP = 2,
AST_FUNC_FIRST_VAR_SKIP = AST_VAR_NEXT_SKIP,
AST_FUNC_BODY_SKIP = 2,
AST_SWITCH_DEFAULT_SKIP = 1
};
V7_PRIVATE void ast_init(struct ast *, size_t);
V7_PRIVATE void ast_optimize(struct ast *);
V7_PRIVATE void ast_free(struct ast *);
/*
* Begins an AST node by inserting a tag to the AST at the given offset.
*
* It also allocates space for the fixed_size payload and the space for
* the skips.
*
* The caller is responsible for appending children.
*
* Returns the offset of the node payload (one byte after the tag).
* This offset can be passed to `ast_set_skip`.
*/
V7_PRIVATE ast_off_t
ast_insert_node(struct ast *a, ast_off_t pos, enum ast_tag tag);
/*
* Modify tag which is already added to buffer. Keeps `AST_TAG_LINENO_PRESENT`
* flag.
*/
V7_PRIVATE void ast_modify_tag(struct ast *a, ast_off_t tag_off,
enum ast_tag tag);
#if !V7_DISABLE_LINE_NUMBERS
/*
* Add line_no varint after all skips of the tag at the offset `tag_off`, and
* marks the tag byte.
*
* Byte at the offset `tag_off` should be a valid tag.
*/
V7_PRIVATE void ast_add_line_no(struct ast *a, ast_off_t tag_off, int line_no);
#endif
/*
* Patches a given skip slot for an already emitted node with the
* current write cursor position (e.g. AST length).
*
* This is intended to be invoked when a node with a variable number
* of child subtrees is closed, or when the consumers need a shortcut
* to the next sibling.
*
* Each node type has a different number and semantic for skips,
* all of them defined in the `ast_which_skip` enum.
* All nodes having a variable number of child subtrees must define
* at least the `AST_END_SKIP` skip, which effectively skips a node
* boundary.
*
* Every tree reader can assume this and safely skip unknown nodes.
*
* `pos` should be an offset of the byte right after a tag.
*/
V7_PRIVATE ast_off_t
ast_set_skip(struct ast *a, ast_off_t pos, enum ast_which_skip skip);
/*
* Patches a given skip slot for an already emitted node with the value
* (stored as delta relative to the `pos` node) of the `where` argument.
*/
V7_PRIVATE ast_off_t ast_modify_skip(struct ast *a, ast_off_t pos,
ast_off_t where, enum ast_which_skip skip);
/*
* Returns the offset in AST to which the given `skip` points.
*
* `pos` should be an offset of the byte right after a tag.
*/
V7_PRIVATE ast_off_t
ast_get_skip(struct ast *a, ast_off_t pos, enum ast_which_skip skip);
/*
* Returns the tag from the offset `ppos`, and shifts `ppos` by 1.
*/
V7_PRIVATE enum ast_tag ast_fetch_tag(struct ast *a, ast_off_t *ppos);
/*
* Moves the cursor to the tag's varint and inlined data (if there are any, see
* `struct ast_node_def::has_varint` and `struct ast_node_def::has_inlined`).
*
* Technically, it skips node's "skips" and line number data, if either is
* present.
*
* Assumes a cursor (`ppos`) positioned right after a tag.
*/
V7_PRIVATE void ast_move_to_inlined_data(struct ast *a, ast_off_t *ppos);
/*
* Moves the cursor to the tag's subtrees (if there are any,
* see `struct ast_node_def::num_subtrees`), or to the next node in case the
* current node has no subtrees.
*
* Technically, it skips node's "skips", line number data, and inlined data, if
* either is present.
*
* Assumes a cursor (`ppos`) positioned right after a tag.
*/
V7_PRIVATE void ast_move_to_children(struct ast *a, ast_off_t *ppos);
/* Helper to add a node with inlined data. */
V7_PRIVATE ast_off_t ast_insert_inlined_node(struct ast *a, ast_off_t pos,
enum ast_tag tag, const char *name,
size_t len);
/*
* Returns the line number encoded in the node, or `0` in case of none is
* encoded.
*
* `pos` should be an offset of the byte right after a tag.
*/
V7_PRIVATE int ast_get_line_no(struct ast *a, ast_off_t pos);
/*
* `pos` should be an offset of the byte right after a tag
*/
V7_PRIVATE char *ast_get_inlined_data(struct ast *a, ast_off_t pos, size_t *n);
/*
* Returns the `double` number inlined in the node
*/
V7_PRIVATE double ast_get_num(struct ast *a, ast_off_t pos);
/*
* Skips the node and all its subnodes.
*
* Cursor (`ppos`) should be at the tag byte
*/
V7_PRIVATE void ast_skip_tree(struct ast *a, ast_off_t *ppos);
V7_PRIVATE void ast_dump_tree(FILE *fp, struct ast *a, ast_off_t *ppos,
int depth);
V7_PRIVATE void release_ast(struct v7 *v7, struct ast *a);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* V7_NO_COMPILER */
#endif /* CS_V7_SRC_AST_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/bcode.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_BCODE_H_
#define CS_V7_SRC_BCODE_H_
#define BIN_BCODE_SIGNATURE "V\007BCODE:"
#if !defined(V7_NAMES_CNT_WIDTH)
#define V7_NAMES_CNT_WIDTH 10
#endif
#if !defined(V7_ARGS_CNT_WIDTH)
#define V7_ARGS_CNT_WIDTH 8
#endif
#define V7_NAMES_CNT_MAX ((1 << V7_NAMES_CNT_WIDTH) - 1)
#define V7_ARGS_CNT_MAX ((1 << V7_ARGS_CNT_WIDTH) - 1)
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* Amalgamated: #include "v7/src/opcodes.h" */
/* Amalgamated: #include "v7/src/string.h" */
/* Amalgamated: #include "v7/src/object.h" */
/* Amalgamated: #include "v7/src/primitive.h" */
/* Amalgamated: #include "common/mbuf.h" */
enum bcode_inline_lit_type_tag {
BCODE_INLINE_STRING_TYPE_TAG = 0,
BCODE_INLINE_NUMBER_TYPE_TAG,
BCODE_INLINE_FUNC_TYPE_TAG,
BCODE_INLINE_REGEXP_TYPE_TAG,
BCODE_MAX_INLINE_TYPE_TAG
};
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
typedef uint32_t bcode_off_t;
/*
* Each JS function will have one bcode structure
* containing the instruction stream, a literal table, and function
* metadata.
* Instructions contain references to literals (strings, constants, etc)
*
* The bcode struct can be shared between function instances
* and keeps a reference count used to free it in the function destructor.
*/
struct bcode {
/*
* Names + instruction opcode.
* Names are null-terminates strings. For function's bcode, there are:
* - function name (for anonymous function, the name is still present: an
* empty string);
* - arg names (a number of args is determined by `args_cnt`, see below);
* - local names (a number or locals can be calculated:
* `(names_cnt - args_cnt - 1)`).
*
* For script's bcode, there are just local names.
*/
struct v7_vec ops;
/* Literal table */
struct v7_vec lit;
#if !V7_DISABLE_FILENAMES
/* Name of the file from which this bcode was generated (used for debug) */
void *filename;
#endif
/* Reference count */
uint8_t refcnt;
/* Total number of null-terminated strings in the beginning of `ops` */
unsigned int names_cnt : V7_NAMES_CNT_WIDTH;
/* Number of args (should be <= `(names_cnt + 1)`) */
unsigned int args_cnt : V7_ARGS_CNT_WIDTH;
unsigned int strict_mode : 1;
/*
* If true this structure lives on read only memory, either
* mmapped or constant data section.
*/
unsigned int frozen : 1;
/* If set, `ops.buf` points to ROM, so we shouldn't free it */
unsigned int ops_in_rom : 1;
/* Set for deserialized bcode. Used for metrics only */
unsigned int deserialized : 1;
/* Set when `ops` contains function name as the first `name` */
unsigned int func_name_present : 1;
#if !V7_DISABLE_FILENAMES
/* If set, `filename` points to ROM, so we shouldn't free it */
unsigned int filename_in_rom : 1;
#endif
};
/*
* Bcode builder context: it contains mutable mbufs for opcodes and literals,
* whereas the bcode itself contains just vectors (`struct v7_vec`).
*/
struct bcode_builder {
struct v7 *v7;
struct bcode *bcode;
struct mbuf ops; /* names + instruction opcode */
struct mbuf lit; /* literal table */
};
V7_PRIVATE void bcode_builder_init(struct v7 *v7,
struct bcode_builder *bbuilder,
struct bcode *bcode);
V7_PRIVATE void bcode_builder_finalize(struct bcode_builder *bbuilder);
/*
* Note: `filename` must be either:
*
* - `NULL`. In this case, `filename_in_rom` is ignored.
* - A pointer to ROM (or to any other unmanaged memory). `filename_in_rom`
* must be set to 1.
* - A pointer returned by `shdata_create()`, i.e. a pointer to shared data.
*
* If you need to copy filename from another bcode, just pass NULL initially,
* and after bcode is initialized, call `bcode_copy_filename_from()`.
*
* To get later a proper null-terminated filename string from bcode, use
* `bcode_get_filename()`.
*/
V7_PRIVATE void bcode_init(struct bcode *bcode, uint8_t strict_mode,
void *filename, uint8_t filename_in_rom);
V7_PRIVATE void bcode_free(struct v7 *v7, struct bcode *bcode);
V7_PRIVATE void release_bcode(struct v7 *v7, struct bcode *bcode);
V7_PRIVATE void retain_bcode(struct v7 *v7, struct bcode *bcode);
#if !V7_DISABLE_FILENAMES
/*
* Return a pointer to null-terminated filename string
*/
V7_PRIVATE const char *bcode_get_filename(struct bcode *bcode);
#endif
/*
* Copy filename from `src` to `dst`. If source filename is a pointer to ROM,
* then just the pointer is copied; otherwise, appropriate shdata pointer is
* retained.
*/
V7_PRIVATE void bcode_copy_filename_from(struct bcode *dst, struct bcode *src);
/*
* Serialize a bcode structure.
*
* All literals, including functions, are inlined into `ops` data; see
* the serialization logic in `bcode_op_lit()`.
*
* The root bcode looks just like a regular function.
*
* This function is used only internally, but used in a complicated mix of
* configurations, hence the commented V7_PRIVATE
*/
/*V7_PRIVATE*/ void bcode_serialize(struct v7 *v7, struct bcode *bcode,
FILE *f);
V7_PRIVATE void bcode_deserialize(struct v7 *v7, struct bcode *bcode,
const char *data);
#ifdef V7_BCODE_DUMP
V7_PRIVATE void dump_bcode(struct v7 *v7, FILE *, struct bcode *);
#endif
/* mode of literal storage: in literal table or inlined in `ops` */
enum lit_mode {
/* literal stored in table, index is in `lit_t::lit_idx` */
LIT_MODE__TABLE,
/* literal should be inlined in `ops`, value is in `lit_t::inline_val` */
LIT_MODE__INLINED,
};
/*
* Result of the addition of literal value to bcode (see `bcode_add_lit()`).
* There are two possible cases:
*
* - Literal is added to the literal table. In this case, `mode ==
* LIT_MODE__TABLE`, and the index of the literal is stored in `lit_idx`
* - Literal is not added anywhere, and should be inlined into `ops`. In this
* case, `mode == LIT_MODE__INLINED`, and the value to inline is stored in
* `inline_val`.
*
* It's `bcode_op_lit()` who handles both of these cases.
*/
typedef struct {
union {
/*
* index in literal table;
* NOTE: valid if only `mode == LIT_MODE__TABLE`
*/
size_t lit_idx;
/*
* value to be inlined into `ops`;
* NOTE: valid if only `mode == LIT_MODE__INLINED`
*/
v7_val_t inline_val;
} v; /* anonymous unions are a c11 feature */
/*
* mode of literal storage (see `enum lit_mode`)
* NOTE: we need one more bit, because enum can be signed
* on some compilers (e.g. msvc) and thus will get signextended
* when moved to a `enum lit_mode` variable basically corrupting
* the value. See https://github.com/cesanta/v7/issues/551
*/
enum lit_mode mode : 2;
} lit_t;
V7_PRIVATE void bcode_op(struct bcode_builder *bbuilder, uint8_t op);
#if !V7_DISABLE_LINE_NUMBERS
V7_PRIVATE void bcode_append_lineno(struct bcode_builder *bbuilder,
int line_no);
#endif
/*
* Add a literal to the bcode literal table, or just decide that the literal
* should be inlined into `ops`. See `lit_t` for details.
*/
V7_PRIVATE
lit_t bcode_add_lit(struct bcode_builder *bbuilder, v7_val_t val);
/* disabled because of short lits */
#if 0
V7_PRIVATE v7_val_t bcode_get_lit(struct bcode *bcode, size_t idx);
#endif
/*
* Emit an opcode `op`, and handle the literal `lit` (see `bcode_add_lit()`,
* `lit_t`). Depending on the literal storage mode (see `enum lit_mode`), this
* function either emits literal table index or inlines the literal directly
* into `ops.`
*/
V7_PRIVATE void bcode_op_lit(struct bcode_builder *bbuilder, enum opcode op,
lit_t lit);
/* Helper function, equivalent of `bcode_op_lit(bbuilder, OP_PUSH_LIT, lit)` */
V7_PRIVATE void bcode_push_lit(struct bcode_builder *bbuilder, lit_t lit);
/*
* Add name to bcode. If `idx` is null, a name is appended to the end of the
* `bcode->ops.buf`. If `idx` is provided, it should point to the index at
* which new name should be inserted; and it is updated by the
* `bcode_add_name()` to point right after newly added name.
*
* This function is used only internally, but used in a complicated mix of
* configurations, hence the commented V7_PRIVATE
*/
WARN_UNUSED_RESULT
/*V7_PRIVATE*/ enum v7_err
bcode_add_name(struct bcode_builder *bbuilder, const char *p, size_t len,
size_t *idx);
/*
* Takes a pointer to the beginning of `ops` buffer and names count, returns
* a pointer where actual opcodes begin (i.e. skips names).
*
* It takes two distinct arguments instead of just `struct bcode` pointer,
* because during bcode building `ops` is stored in builder.
*
* This function is used only internally, but used in a complicated mix of
* configurations, hence the commented V7_PRIVATE
*/
/*V7_PRIVATE*/ char *bcode_end_names(char *ops, size_t names_cnt);
/*
* Given a pointer to `ops` (which should be `bcode->ops` or a pointer returned
* from previous invocation of `bcode_next_name()`), yields a name string via
* arguments `pname`, `plen`.
*
* Returns a pointer that should be given to `bcode_next_name()` to get a next
* string (Whether there is a next string should be determined via the
* `names_cnt`; since if there are no more names, this function will return an
* invalid non-null pointer as next name pointer)
*/
V7_PRIVATE char *bcode_next_name(char *ops, char **pname, size_t *plen);
/*
* Like `bcode_next_name()`, but instead of yielding a C string, it yields a
* `val_t` value (via `res`).
*/
V7_PRIVATE char *bcode_next_name_v(struct v7 *v7, struct bcode *bcode,
char *ops, val_t *res);
V7_PRIVATE bcode_off_t bcode_pos(struct bcode_builder *bbuilder);
V7_PRIVATE bcode_off_t bcode_add_target(struct bcode_builder *bbuilder);
/*
* This function is used only internally, but used in a complicated mix of
* configurations, hence the commented V7_PRIVATE
*/
/*V7_PRIVATE*/ bcode_off_t bcode_op_target(struct bcode_builder *bbuilder,
uint8_t op);
/*V7_PRIVATE*/ void bcode_patch_target(struct bcode_builder *bbuilder,
bcode_off_t label, bcode_off_t target);
V7_PRIVATE void bcode_add_varint(struct bcode_builder *bbuilder, size_t value);
/*
* Reads varint-encoded integer from the provided pointer, and adjusts
* the pointer appropriately
*/
V7_PRIVATE size_t bcode_get_varint(char **ops);
/*
* Decode a literal value from a string of opcodes and update the cursor to
* point past it
*/
V7_PRIVATE
v7_val_t bcode_decode_lit(struct v7 *v7, struct bcode *bcode, char **ops);
#if defined(V7_BCODE_DUMP) || defined(V7_BCODE_TRACE)
V7_PRIVATE void dump_op(struct v7 *v7, FILE *f, struct bcode *bcode,
char **ops);
#endif
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_BCODE_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/gc_public.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/*
* === Garbage Collector
*/
#ifndef CS_V7_SRC_GC_PUBLIC_H_
#define CS_V7_SRC_GC_PUBLIC_H_
/* Amalgamated: #include "v7/src/core_public.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
#if V7_ENABLE__Memory__stats
/* Heap metric id, see `v7_heap_stat()` */
enum v7_heap_stat_what {
V7_HEAP_STAT_HEAP_SIZE,
V7_HEAP_STAT_HEAP_USED,
V7_HEAP_STAT_STRING_HEAP_RESERVED,
V7_HEAP_STAT_STRING_HEAP_USED,
V7_HEAP_STAT_OBJ_HEAP_MAX,
V7_HEAP_STAT_OBJ_HEAP_FREE,
V7_HEAP_STAT_OBJ_HEAP_CELL_SIZE,
V7_HEAP_STAT_FUNC_HEAP_MAX,
V7_HEAP_STAT_FUNC_HEAP_FREE,
V7_HEAP_STAT_FUNC_HEAP_CELL_SIZE,
V7_HEAP_STAT_PROP_HEAP_MAX,
V7_HEAP_STAT_PROP_HEAP_FREE,
V7_HEAP_STAT_PROP_HEAP_CELL_SIZE,
V7_HEAP_STAT_FUNC_AST_SIZE,
V7_HEAP_STAT_BCODE_OPS_SIZE,
V7_HEAP_STAT_BCODE_LIT_TOTAL_SIZE,
V7_HEAP_STAT_BCODE_LIT_DESER_SIZE,
V7_HEAP_STAT_FUNC_OWNED,
V7_HEAP_STAT_FUNC_OWNED_MAX
};
/* Returns a given heap statistics */
int v7_heap_stat(struct v7 *v7, enum v7_heap_stat_what what);
#endif
/*
* Perform garbage collection.
* Pass true to full in order to reclaim unused heap back to the OS.
*/
void v7_gc(struct v7 *v7, int full);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_GC_PUBLIC_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/gc.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_GC_H_
#define CS_V7_SRC_GC_H_
/* Amalgamated: #include "v7/src/gc_public.h" */
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/core.h" */
/*
* Macros for marking reachable things: use bit 0.
*/
#define MARK(p) (((struct gc_cell *) (p))->head.word |= 1)
#define UNMARK(p) (((struct gc_cell *) (p))->head.word &= ~1)
#define MARKED(p) (((struct gc_cell *) (p))->head.word & 1)
/*
* Similar to `MARK()` / `UNMARK()` / `MARKED()`, but `.._FREE` counterparts
* are intended to mark free cells (as opposed to used ones), so they use
* bit 1.
*/
#define MARK_FREE(p) (((struct gc_cell *) (p))->head.word |= 2)
#define UNMARK_FREE(p) (((struct gc_cell *) (p))->head.word &= ~2)
#define MARKED_FREE(p) (((struct gc_cell *) (p))->head.word & 2)
/*
* performs arithmetics on gc_cell pointers as if they were arena->cell_size
* bytes wide
*/
#define GC_CELL_OP(arena, cell, op, arg) \
((struct gc_cell *) (((char *) (cell)) op((arg) * (arena)->cell_size)))
struct gc_tmp_frame {
struct v7 *v7;
size_t pos;
};
struct gc_cell {
union {
struct gc_cell *link;
uintptr_t word;
} head;
};
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
V7_PRIVATE struct v7_generic_object *new_generic_object(struct v7 *);
V7_PRIVATE struct v7_property *new_property(struct v7 *);
V7_PRIVATE struct v7_js_function *new_function(struct v7 *);
V7_PRIVATE void gc_mark(struct v7 *, val_t);
V7_PRIVATE void gc_arena_init(struct gc_arena *, size_t, size_t, size_t,
const char *);
V7_PRIVATE void gc_arena_destroy(struct v7 *, struct gc_arena *a);
V7_PRIVATE void gc_sweep(struct v7 *, struct gc_arena *, size_t);
V7_PRIVATE void *gc_alloc_cell(struct v7 *, struct gc_arena *);
V7_PRIVATE struct gc_tmp_frame new_tmp_frame(struct v7 *);
V7_PRIVATE void tmp_frame_cleanup(struct gc_tmp_frame *);
V7_PRIVATE void tmp_stack_push(struct gc_tmp_frame *, val_t *);
V7_PRIVATE void compute_need_gc(struct v7 *);
/* perform gc if not inhibited */
V7_PRIVATE int maybe_gc(struct v7 *);
#if !V7_DISABLE_STR_ALLOC_SEQ
V7_PRIVATE uint16_t
gc_next_allocation_seqn(struct v7 *v7, const char *str, size_t len);
V7_PRIVATE int gc_is_valid_allocation_seqn(struct v7 *v7, uint16_t n);
V7_PRIVATE void gc_check_valid_allocation_seqn(struct v7 *v7, uint16_t n);
#endif
V7_PRIVATE uint64_t gc_string_val_to_offset(val_t v);
/* return 0 if v is an object/function with a bad pointer */
V7_PRIVATE int gc_check_val(struct v7 *v7, val_t v);
/* checks whether a pointer is within the ranges of an arena */
V7_PRIVATE int gc_check_ptr(const struct gc_arena *a, const void *p);
#if V7_ENABLE__Memory__stats
V7_PRIVATE size_t gc_arena_size(struct gc_arena *);
#endif
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_GC_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/regexp_public.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/*
* === RegExp
*/
#ifndef CS_V7_SRC_REGEXP_PUBLIC_H_
#define CS_V7_SRC_REGEXP_PUBLIC_H_
/* Amalgamated: #include "v7/src/core_public.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
/*
* Make RegExp object.
* `regex`, `regex_len` specify a pattern, `flags` and `flags_len` specify
* flags. Both utf8 encoded. For example, `regex` is `(.+)`, `flags` is `gi`.
* If `regex_len` is ~0, `regex` is assumed to be NUL-terminated and
* `strlen(regex)` is used.
*/
WARN_UNUSED_RESULT
enum v7_err v7_mk_regexp(struct v7 *v7, const char *regex, size_t regex_len,
const char *flags, size_t flags_len, v7_val_t *res);
/* Returns true if given value is a JavaScript RegExp object*/
int v7_is_regexp(struct v7 *v7, v7_val_t v);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_REGEXP_PUBLIC_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/regexp.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_REGEXP_H_
#define CS_V7_SRC_REGEXP_H_
/* Amalgamated: #include "v7/src/regexp_public.h" */
/* Amalgamated: #include "v7/src/core.h" */
#if V7_ENABLE__RegExp
/*
* Maximum number of flags returned by get_regexp_flags_str().
* NOTE: does not include null-terminate byte.
*/
#define _V7_REGEXP_MAX_FLAGS_LEN 3
struct v7_regexp;
V7_PRIVATE struct v7_regexp *v7_get_regexp_struct(struct v7 *, v7_val_t);
/*
* Generates a string containing regexp flags, e.g. "gi".
*
* `buf` should point to a buffer of minimum `_V7_REGEXP_MAX_FLAGS_LEN` bytes.
* Returns length of the resulted string (saved into `buf`)
*/
V7_PRIVATE size_t
get_regexp_flags_str(struct v7 *v7, struct v7_regexp *rp, char *buf);
#endif /* V7_ENABLE__RegExp */
#endif /* CS_V7_SRC_REGEXP_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/function_public.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/*
* === Functions
*/
#ifndef CS_V7_SRC_FUNCTION_PUBLIC_H_
#define CS_V7_SRC_FUNCTION_PUBLIC_H_
/* Amalgamated: #include "v7/src/core_public.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
/*
* Make a JS function object backed by a cfunction.
*
* `func` is a C callback.
*
* A function object is JS object having the Function prototype that holds a
* cfunction value in a hidden property.
*
* The function object will have a `prototype` property holding an object that
* will be used as the prototype of objects created when calling the function
* with the `new` operator.
*/
v7_val_t v7_mk_function(struct v7 *, v7_cfunction_t *func);
/*
* Make f a JS function with specified prototype `proto`, so that the resulting
* function is better suited for the usage as a constructor.
*/
v7_val_t v7_mk_function_with_proto(struct v7 *v7, v7_cfunction_t *f,
v7_val_t proto);
/*
* Make a JS value that holds C/C++ callback pointer.
*
* CAUTION: This is a low-level function value. It's not a real object and
* cannot hold user defined properties. You should use `v7_mk_function` unless
* you know what you're doing.
*/
v7_val_t v7_mk_cfunction(v7_cfunction_t *func);
/*
* Returns true if given value is callable (i.e. it's either a JS function or
* cfunction)
*/
int v7_is_callable(struct v7 *v7, v7_val_t v);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_FUNCTION_PUBLIC_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/function.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_FUNCTION_H_
#define CS_V7_SRC_FUNCTION_H_
/* Amalgamated: #include "v7/src/function_public.h" */
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/core.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
V7_PRIVATE struct v7_js_function *get_js_function_struct(val_t v);
V7_PRIVATE val_t
mk_js_function(struct v7 *v7, struct v7_generic_object *scope, val_t proto);
V7_PRIVATE int is_js_function(val_t v);
V7_PRIVATE v7_val_t mk_cfunction_lite(v7_cfunction_t *f);
/* Returns true if given value holds a pointer to C callback */
V7_PRIVATE int is_cfunction_lite(v7_val_t v);
/* Returns true if given value holds an object which represents C callback */
V7_PRIVATE int is_cfunction_obj(struct v7 *v7, v7_val_t v);
/*
* Returns `v7_cfunction_t *` callback pointer stored in `v7_val_t`, or NULL
* if given value is neither cfunction pointer nor cfunction object.
*/
V7_PRIVATE v7_cfunction_t *get_cfunction_ptr(struct v7 *v7, v7_val_t v);
/*
* Like v7_mk_function but also sets the function's `length` property.
*
* The `length` property is useful for introspection and the stdlib defines it
* for many core functions mostly because the ECMA test suite requires it and we
* don't want to skip otherwise useful tests just because the `length` property
* check fails early in the test. User defined functions don't need to specify
* the length and passing -1 is a safe choice, as it will also reduce the
* footprint.
*
* The subtle difference between set `length` explicitly to 0 rather than
* just defaulting the `0` value from the prototype is that in the former case
* the property cannot be change since it's read only. This again, is important
* only for ecma compliance and your user code might or might not find this
* relevant.
*
* NODO(lsm): please don't combine v7_mk_function_arg and v7_mk_function
* into one function. Currently `num_args` is useful only internally. External
* users can just use `v7_def` to set the length.
*/
V7_PRIVATE
v7_val_t mk_cfunction_obj(struct v7 *v7, v7_cfunction_t *func, int num_args);
/*
* Like v7_mk_function_with_proto but also sets the function's `length`
*property.
*
* NODO(lsm): please don't combine mk_cfunction_obj_with_proto and
* v7_mk_function_with_proto.
* into one function. Currently `num_args` is useful only internally. External
* users can just use `v7_def` to set the length.
*/
V7_PRIVATE
v7_val_t mk_cfunction_obj_with_proto(struct v7 *v7, v7_cfunction_t *f,
int num_args, v7_val_t proto);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_FUNCTION_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/util_public.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/*
* === Utility functions
*/
#ifndef CS_V7_SRC_UTIL_PUBLIC_H_
#define CS_V7_SRC_UTIL_PUBLIC_H_
/* Amalgamated: #include "v7/src/core_public.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
/* Output a string representation of the value to stdout.
* V7_STRINGIFY_DEBUG mode is used. */
void v7_print(struct v7 *v7, v7_val_t v);
/* Output a string representation of the value to stdout followed by a newline.
* V7_STRINGIFY_DEBUG mode is used. */
void v7_println(struct v7 *v7, v7_val_t v);
/* Output a string representation of the value to a file.
* V7_STRINGIFY_DEBUG mode is used. */
void v7_fprint(FILE *f, struct v7 *v7, v7_val_t v);
/* Output a string representation of the value to a file followed by a newline.
* V7_STRINGIFY_DEBUG mode is used. */
void v7_fprintln(FILE *f, struct v7 *v7, v7_val_t v);
/* Output stack trace recorded in the exception `e` to file `f` */
void v7_fprint_stack_trace(FILE *f, struct v7 *v7, v7_val_t e);
/* Output error object message and possibly stack trace to f */
void v7_print_error(FILE *f, struct v7 *v7, const char *ctx, v7_val_t e);
#if V7_ENABLE__Proxy
struct v7_property;
/*
* C callback, analogue of JS callback `getOwnPropertyDescriptor()`.
* Callbacks of this type are used for C API only, see `m7_mk_proxy()`.
*
* `name` is the name of the property, and the function should fill `attrs` and
* `value` with the property data. Before this callback is called, `attrs` is
* set to 0, and `value` is `V7_UNDEFINED`.
*
* It should return non-zero if the property should be considered existing, or
* zero otherwise.
*
* You can inspect the property attributes with the `V7_PROP_ATTR_IS_*` macros.
*/
typedef int(v7_get_own_prop_desc_cb_t)(struct v7 *v7, v7_val_t target,
v7_val_t name, v7_prop_attr_t *attrs,
v7_val_t *value);
/* Handler for `v7_mk_proxy()`; each item is a cfunction */
typedef struct {
v7_cfunction_t *get;
v7_cfunction_t *set;
v7_cfunction_t *own_keys;
v7_get_own_prop_desc_cb_t *get_own_prop_desc;
} v7_proxy_hnd_t;
/*
* Create a Proxy object, see:
* https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Proxy
*
* Only two traps are implemented so far: `get()` and `set()`. Note that
* `Object.defineProperty()` bypasses the `set()` trap.
*
* If `target` is not an object, the empty object will be used, so it's safe
* to pass `V7_UNDEFINED` as `target`.
*/
v7_val_t v7_mk_proxy(struct v7 *v7, v7_val_t target,
const v7_proxy_hnd_t *handler);
#endif /* V7_ENABLE__Proxy */
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_UTIL_PUBLIC_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/util.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_UTIL_H_
#define CS_V7_SRC_UTIL_H_
/* Amalgamated: #include "v7/src/core.h" */
/* Amalgamated: #include "v7/src/util_public.h" */
struct bcode;
V7_PRIVATE enum v7_type val_type(struct v7 *v7, val_t v);
#if !V7_DISABLE_LINE_NUMBERS
V7_PRIVATE uint8_t msb_lsb_swap(uint8_t b);
#endif
/*
* At the moment, all other utility functions are public, and are declared in
* `util_public.h`
*/
#endif /* CS_V7_SRC_UTIL_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/shdata.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/*
* shdata (stands for "shared data") is a simple module that allows to have
* reference count for an arbitrary payload data, which will be freed as
* necessary. A poor man's shared_ptr.
*/
#ifndef CS_V7_SRC_SHDATA_H_
#define CS_V7_SRC_SHDATA_H_
/* Amalgamated: #include "v7/src/internal.h" */
#if !V7_DISABLE_FILENAMES && !V7_DISABLE_LINE_NUMBERS
struct shdata {
/* Reference count */
uint8_t refcnt;
/*
* Note: we'd use `unsigned char payload[];` here, but we can't, since this
* feature was introduced in C99 only
*/
};
/*
* Allocate memory chunk of appropriate size, copy given `payload` data there,
* retain (`shdata_retain()`), and return it.
*/
V7_PRIVATE struct shdata *shdata_create(const void *payload, size_t size);
V7_PRIVATE struct shdata *shdata_create_from_string(const char *src);
/*
* Increment reference count for the given shared data
*/
V7_PRIVATE void shdata_retain(struct shdata *p);
/*
* Decrement reference count for the given shared data
*/
V7_PRIVATE void shdata_release(struct shdata *p);
/*
* Get payload data
*/
V7_PRIVATE void *shdata_get_payload(struct shdata *p);
#endif
#endif /* CS_V7_SRC_SHDATA_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/eval.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_EVAL_H_
#define CS_V7_SRC_EVAL_H_
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/bcode.h" */
struct v7_call_frame_base;
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err eval_bcode(struct v7 *v7, struct bcode *bcode,
val_t this_object, uint8_t reset_line_no,
val_t *_res);
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err b_apply(struct v7 *v7, v7_val_t func, v7_val_t this_obj,
v7_val_t args, uint8_t is_constructor,
v7_val_t *res);
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err b_exec(struct v7 *v7, const char *src, size_t src_len,
const char *filename, val_t func, val_t args,
val_t this_object, int is_json, int fr,
uint8_t is_constructor, val_t *res);
/*
* Try to find the call frame whose `type_mask` intersects with the given
* `type_mask`.
*
* Start from the top call frame, and go deeper until the matching frame is
* found, or there's no more call frames. If the needed frame was not found,
* returns `NULL`.
*/
V7_PRIVATE struct v7_call_frame_base *find_call_frame(struct v7 *v7,
uint8_t type_mask);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_EVAL_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/compiler.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_COMPILER_H_
#define CS_V7_SRC_COMPILER_H_
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/bcode.h" */
/* Amalgamated: #include "v7/src/ast.h" */
#if !defined(V7_NO_COMPILER)
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
V7_PRIVATE enum v7_err compile_script(struct v7 *v7, struct ast *a,
struct bcode *bcode);
V7_PRIVATE enum v7_err compile_expr(struct v7 *v7, struct ast *a,
ast_off_t *ppos, struct bcode *bcode);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* V7_NO_COMPILER */
#endif /* CS_V7_SRC_COMPILER_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/cyg_profile.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_CYG_PROFILE_H_
#define CS_V7_SRC_CYG_PROFILE_H_
/*
* This file contains GCC/clang instrumentation callbacks, as well as
* accompanying code. The actual code in these callbacks depends on enabled
* features. See cyg_profile.c for some implementation details rationale.
*/
struct v7;
#if V7_ENABLE_STACK_TRACKING
/*
* Stack-tracking functionality:
*
* The idea is that the caller should allocate `struct stack_track_ctx`
* (typically on stack) in the function to track the stack usage of, and call
* `v7_stack_track_start()` in the beginning.
*
* Before quitting current stack frame (for example, before returning from
* function), call `v7_stack_track_end()`, which returns the maximum stack
* consumed size.
*
* These calls can be nested: for example, we may track the stack usage of the
* whole application by using these functions in `main()`, as well as track
* stack usage of any nested functions.
*
* Just to stress: both `v7_stack_track_start()` / `v7_stack_track_end()`
* should be called for the same instance of `struct stack_track_ctx` in the
* same stack frame.
*/
/* stack tracking context */
struct stack_track_ctx {
struct stack_track_ctx *next;
void *start;
void *max;
};
/* see explanation above */
void v7_stack_track_start(struct v7 *v7, struct stack_track_ctx *ctx);
/* see explanation above */
int v7_stack_track_end(struct v7 *v7, struct stack_track_ctx *ctx);
void v7_stack_stat_clean(struct v7 *v7);
#endif /* V7_ENABLE_STACK_TRACKING */
#endif /* CS_V7_SRC_CYG_PROFILE_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/builtin/builtin.h"
#endif
/*
* Copyright (c) 2015 Cesanta Software Limited
* All rights reserved
*/
/*
* === Non-Standard API
*
* V7 has several non-standard extensions for `String.prototype` in
* order to give a compact and fast API to access raw data obtained from
* File, Socket, and hardware input/output such as I2C.
* V7 IO API functions return
* string data as a result of read operations, and that string data is a
* raw byte array. ECMA6 provides `ArrayBuffer` and `DataView` API for dealing
* with raw bytes, because strings in JavaScript are Unicode. That standard
* API is too bloated for the embedded use, and does not allow to use handy
* String API (e.g. `.match()`) against data.
*
* V7 internally stores strings as byte arrays. All strings created by the
* String API are UTF8 encoded. Strings that are the result of
* input/output API calls might not be a valid UTF8 strings, but nevertheless
* they are represented as strings, and the following API allows to access
* underlying byte sequence:
*
* ==== String.prototype.at(position) -> number or NaN
* Return byte at index
* `position`. Byte value is in 0,255 range. If `position` is out of bounds
* (either negative or larger then the byte array length), NaN is returned.
* Example: `"ы".at(0)` returns 0xd1.
*
* ==== String.prototype.blen -> number
* Return string length in bytes.
* Example: `"ы".blen` returns 2. Note that `"ы".length` is 1, since that
* string consists of a single Unicode character (2-byte).
*
* === Builtin API
*
* Builtin API provides additional JavaScript interfaces available for V7
* scripts.
* File API is a wrapper around standard C calls `fopen()`, `fclose()`,
* `fread()`, `fwrite()`, `rename()`, `remove()`.
* Crypto API provides functions for base64, md5, and sha1 encoding/decoding.
* Socket API provides low-level socket API.
*
* ==== File.eval(file_name)
* Parse and run `file_name`.
* Throws an exception if the file doesn't exist, cannot be parsed or if the
* script throws any exception.
*
* ==== File.read(file_name) -> string or undefined
* Read file `file_name` and return a string with a file content.
* On any error, return `undefined` as a result.
*
* ==== File.write(file_name, str) -> true or false
* Write string `str` to a file `file_name`. Return `true` on success,
* `false` on error.
*
* ==== File.open(file_name [, mode]) -> file_object or null
* Open a file `path`. For
* list of valid `mode` values, see `fopen()` documentation. If `mode` is
* not specified, mode `rb` is used, i.e. file is opened in read-only mode.
* Return an opened file object, or null on error. Example:
* `var f = File.open('/etc/passwd'); f.close();`
*
* ==== file_obj.close() -> undefined
* Close opened file object.
* NOTE: it is user's responsibility to close all opened file streams. V7
* does not do that automatically.
*
* ==== file_obj.read() -> string
* Read portion of data from
* an opened file stream. Return string with data, or empty string on EOF
* or error.
*
* ==== file_obj.write(str) -> num_bytes_written
* Write string `str` to the opened file object. Return number of bytes written.
*
* ==== File.rename(old_name, new_name) -> errno
* Rename file `old_name` to
* `new_name`. Return 0 on success, or `errno` value on error.
*
* ==== File.list(dir_name) -> array_of_names
* Return a list of files in a given directory, or `undefined` on error.
*
* ==== File.remove(file_name) -> errno
* Delete file `file_name`.
* Return 0 on success, or `errno` value on error.
*
* ==== Crypto.base64_encode(str)
* Base64-encode input string `str` and return encoded string.
*
* ==== Crypto.base64_decode(str)
* Base64-decode input string `str` and return decoded string.
*
* ==== Crypto.md5(str), Crypto.md5_hex(str)
* Generate MD5 hash from input string `str`. Return 16-byte hash (`md5()`),
* or stringified hexadecimal representation of the hash (`md5_hex`).
*
* ==== Crypto.sha1(str), Crypto.sha1_hex(str)
* Generate SHA1 hash from input string `str`. Return 20-byte hash (`sha1()`),
* or stringified hexadecimal representation of the hash (`sha1_hex`).
*
* ==== Socket.connect(host, port [, is_udp]) -> socket_obj
* Connect to a given host. `host` can be a string IP address, or a host name.
* Optional `is_udp` parameter, if true, indicates that socket should be UDP.
* Return socket object on success, null on error.
*
* ==== Socket.listen(port [, ip_address [,is_udp]]) -> socket_obj
* Create a listening socket on a given port. Optional `ip_address` argument
* specifies and IP address to bind to. Optional `is_udp` parameter, if true,
* indicates that socket should be UDP. Return socket object on success,
* null on error.
*
* ==== socket_obj.accept() -> socket_obj
* Sleep until new incoming connection is arrived. Return accepted socket
* object on success, or `null` on error.
*
* ==== socket_obj.close() -> numeric_errno
* Close socket object. Return 0 on success, or system errno on error.
*
* ==== socket_obj.recv() -> string
* Read data from socket. Return data string, or empty string if peer has
* disconnected, or `null` on error.
*
* ==== socket_obj.recvAll() -> string
* Same as `recv()`, but keeps reading data until socket is closed.
*
* ==== sock.send(string) -> num_bytes_sent
* Send string to the socket. Return number of bytes sent, or 0 on error.
* Simple HTTP client example:
*
* var s = Socket.connect("google.com", 80);
* s.send("GET / HTTP/1.0\n\n");
* var reply = s.recv();
*/
#ifndef CS_V7_BUILTIN_BUILTIN_H_
#define CS_V7_BUILTIN_BUILTIN_H_
struct v7;
void init_file(struct v7 *);
void init_socket(struct v7 *);
void init_crypto(struct v7 *);
#endif /* CS_V7_BUILTIN_BUILTIN_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/slre.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*
* This software is dual-licensed: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. For the terms of this
* license, see .
*
* You are free to use this software under the terms of the GNU General
* Public License, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* Alternatively, you can license this software under a commercial
* license, as set out in .
*/
#ifndef CS_V7_SRC_SLRE_H_
#define CS_V7_SRC_SLRE_H_
/* Return codes for slre_compile() */
enum slre_error {
SLRE_OK,
SLRE_INVALID_DEC_DIGIT,
SLRE_INVALID_HEX_DIGIT,
SLRE_INVALID_ESC_CHAR,
SLRE_UNTERM_ESC_SEQ,
SLRE_SYNTAX_ERROR,
SLRE_UNMATCH_LBR,
SLRE_UNMATCH_RBR,
SLRE_NUM_OVERFLOW,
SLRE_INF_LOOP_M_EMP_STR,
SLRE_TOO_MANY_CHARSETS,
SLRE_INV_CHARSET_RANGE,
SLRE_CHARSET_TOO_LARGE,
SLRE_MALFORMED_CHARSET,
SLRE_INVALID_BACK_REFERENCE,
SLRE_TOO_MANY_CAPTURES,
SLRE_INVALID_QUANTIFIER,
SLRE_BAD_CHAR_AFTER_USD
};
#if V7_ENABLE__RegExp
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* Regex flags */
#define SLRE_FLAG_G 1 /* Global - match in the whole string */
#define SLRE_FLAG_I 2 /* Ignore case */
#define SLRE_FLAG_M 4 /* Multiline */
#define SLRE_FLAG_RE 8 /* flag RegExp/String */
/* Describes single capture */
struct slre_cap {
const char *start; /* points to the beginning of the capture group */
const char *end; /* points to the end of the capture group */
};
/* Describes all captures */
#define SLRE_MAX_CAPS 32
struct slre_loot {
int num_captures;
struct slre_cap caps[SLRE_MAX_CAPS];
};
/* Opaque structure that holds compiled regular expression */
struct slre_prog;
int slre_compile(const char *regexp, size_t regexp_len, const char *flags,
size_t flags_len, struct slre_prog **, int is_regex);
int slre_exec(struct slre_prog *prog, int flag_g, const char *start,
const char *end, struct slre_loot *loot);
void slre_free(struct slre_prog *prog);
int slre_match(const char *, size_t, const char *, size_t, const char *, size_t,
struct slre_loot *);
int slre_replace(struct slre_loot *loot, const char *src, size_t src_len,
const char *replace, size_t rep_len, struct slre_loot *dst);
int slre_get_flags(struct slre_prog *);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* V7_ENABLE__RegExp */
#endif /* CS_V7_SRC_SLRE_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/stdlib.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_STDLIB_H_
#define CS_V7_SRC_STDLIB_H_
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/core.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
/*V7_PRIVATE*/ void init_stdlib(struct v7 *v7);
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err std_eval(struct v7 *v7, v7_val_t arg, v7_val_t this_obj,
int is_json, v7_val_t *res);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_STDLIB_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/heapusage.h"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_HEAPUSAGE_H_
#define CS_V7_SRC_HEAPUSAGE_H_
#if V7_HEAPUSAGE_ENABLE
extern volatile int heap_dont_count;
/*
* Returns total heap-allocated size in bytes (without any overhead of the
* heap implementation)
*/
size_t heapusage_alloc_size(void);
/*
* Returns number of active allocations
*/
size_t heapusage_allocs_cnt(void);
/*
* Must be called before allocating some memory that should not be indicated as
* memory consumed for some particular operation: for example, when we
* preallocate some GC buffer.
*/
#define heapusage_dont_count(a) \
do { \
heap_dont_count = a; \
} while (0)
#else /* V7_HEAPUSAGE_ENABLE */
#define heapusage_alloc_size() (0)
#define heapusage_allocs_cnt() (0)
#define heapusage_dont_count(a)
#endif /* V7_HEAPUSAGE_ENABLE */
#endif /* CS_V7_SRC_HEAPUSAGE_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/std_proxy.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_STD_PROXY_H_
#define CS_V7_SRC_STD_PROXY_H_
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/core.h" */
#if V7_ENABLE__Proxy
#define _V7_PROXY_TARGET_NAME "__tgt"
#define _V7_PROXY_HANDLER_NAME "__hnd"
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
#if V7_ENABLE__Proxy
V7_PRIVATE enum v7_err Proxy_ctor(struct v7 *v7, v7_val_t *res);
V7_PRIVATE void init_proxy(struct v7 *v7);
/*
* Returns whether the given name is one of the special Proxy names
* (_V7_PROXY_TARGET_NAME or _V7_PROXY_HANDLER_NAME)
*/
V7_PRIVATE int is_special_proxy_name(const char *name, size_t name_len);
#endif
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* V7_ENABLE__Proxy */
#endif /* CS_V7_SRC_STD_PROXY_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/freeze.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_FREEZE_H_
#define CS_V7_SRC_FREEZE_H_
#ifdef V7_FREEZE
/* Amalgamated: #include "v7/src/internal.h" */
struct v7_property;
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
V7_PRIVATE void freeze(struct v7 *v7, char *filename);
V7_PRIVATE void freeze_obj(struct v7 *v7, FILE *f, v7_val_t v);
V7_PRIVATE void freeze_prop(struct v7 *v7, FILE *f, struct v7_property *prop);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* V7_FREEZE */
#endif /* CS_V7_SRC_FREEZE_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/std_array.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_STD_ARRAY_H_
#define CS_V7_SRC_STD_ARRAY_H_
/* Amalgamated: #include "v7/src/internal.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
V7_PRIVATE void init_array(struct v7 *v7);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_STD_ARRAY_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/std_boolean.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_STD_BOOLEAN_H_
#define CS_V7_SRC_STD_BOOLEAN_H_
/* Amalgamated: #include "v7/src/internal.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
V7_PRIVATE void init_boolean(struct v7 *v7);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_STD_BOOLEAN_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/std_date.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_STD_DATE_H_
#define CS_V7_SRC_STD_DATE_H_
/* Amalgamated: #include "v7/src/internal.h" */
#if V7_ENABLE__Date
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
V7_PRIVATE void init_date(struct v7 *v7);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* V7_ENABLE__Date */
#endif /* CS_V7_SRC_STD_DATE_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/std_function.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_STD_FUNCTION_H_
#define CS_V7_SRC_STD_FUNCTION_H_
/* Amalgamated: #include "v7/src/internal.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
V7_PRIVATE void init_function(struct v7 *v7);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_STD_FUNCTION_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/std_json.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_STD_JSON_H_
#define CS_V7_SRC_STD_JSON_H_
/* Amalgamated: #include "v7/src/internal.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
V7_PRIVATE void init_json(struct v7 *v7);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_STD_JSON_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/std_math.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_STD_MATH_H_
#define CS_V7_SRC_STD_MATH_H_
/* Amalgamated: #include "v7/src/internal.h" */
#if V7_ENABLE__Math
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
V7_PRIVATE void init_math(struct v7 *v7);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* V7_ENABLE__Math */
#endif /* CS_V7_SRC_STD_MATH_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/std_number.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_STD_NUMBER_H_
#define CS_V7_SRC_STD_NUMBER_H_
/* Amalgamated: #include "v7/src/internal.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
V7_PRIVATE void init_number(struct v7 *v7);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_STD_NUMBER_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/std_object.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_STD_OBJECT_H_
#define CS_V7_SRC_STD_OBJECT_H_
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/core.h" */
struct v7;
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
V7_PRIVATE void init_object(struct v7 *v7);
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err Obj_valueOf(struct v7 *v7, v7_val_t *res);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_STD_OBJECT_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/std_regex.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_STD_REGEX_H_
#define CS_V7_SRC_STD_REGEX_H_
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/core.h" */
#if V7_ENABLE__RegExp
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
V7_PRIVATE enum v7_err Regex_ctor(struct v7 *v7, v7_val_t *res);
V7_PRIVATE enum v7_err rx_exec(struct v7 *v7, v7_val_t rx, v7_val_t vstr,
int lind, v7_val_t *res);
V7_PRIVATE void init_regex(struct v7 *v7);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* V7_ENABLE__RegExp */
#endif /* CS_V7_SRC_STD_REGEX_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/std_string.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_STD_STRING_H_
#define CS_V7_SRC_STD_STRING_H_
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* Max captures for String.replace() */
#define V7_RE_MAX_REPL_SUB 20
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
V7_PRIVATE void init_string(struct v7 *v7);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_STD_STRING_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/js_stdlib.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_JS_STDLIB_H_
#define CS_V7_SRC_JS_STDLIB_H_
/* Amalgamated: #include "v7/src/internal.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
V7_PRIVATE void init_js_stdlib(struct v7 *);
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_JS_STDLIB_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/main_public.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/*
* === v7 main()
*/
#ifndef CS_V7_SRC_MAIN_PUBLIC_H_
#define CS_V7_SRC_MAIN_PUBLIC_H_
/* Amalgamated: #include "v7/src/core_public.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
/*
* V7 executable main function.
*
* There are various callbacks available:
*
* `pre_freeze_init()` and `pre_init()` are optional intialization functions,
* aimed to export any extra functionality into vanilla v7 engine. They are
* called after v7 initialization, before executing given files or inline
* expressions. `pre_freeze_init()` is called before "freezing" v7 state;
* whereas `pre_init` called afterwards.
*
* `post_init()`, if provided, is called after executing files and expressions,
* before destroying v7 instance and exiting.
*/
int v7_main(int argc, char *argv[], void (*pre_freeze_init)(struct v7 *),
void (*pre_init)(struct v7 *), void (*post_init)(struct v7 *));
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif /* CS_V7_SRC_MAIN_PUBLIC_H_ */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/main.h"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_V7_SRC_MAIN_H_
#define CS_V7_SRC_MAIN_H_
/* Amalgamated: #include "v7/src/main_public.h" */
#endif /* CS_V7_SRC_MAIN_H_ */
#ifndef V7_EXPORT_INTERNAL_HEADERS
#ifdef V7_MODULE_LINES
#line 1 "common/mbuf.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef EXCLUDE_COMMON
#include
#include
/* Amalgamated: #include "common/mbuf.h" */
#ifndef MBUF_REALLOC
#define MBUF_REALLOC realloc
#endif
#ifndef MBUF_FREE
#define MBUF_FREE free
#endif
void mbuf_init(struct mbuf *mbuf, size_t initial_size) WEAK;
void mbuf_init(struct mbuf *mbuf, size_t initial_size) {
mbuf->len = mbuf->size = 0;
mbuf->buf = NULL;
mbuf_resize(mbuf, initial_size);
}
void mbuf_free(struct mbuf *mbuf) WEAK;
void mbuf_free(struct mbuf *mbuf) {
if (mbuf->buf != NULL) {
MBUF_FREE(mbuf->buf);
mbuf_init(mbuf, 0);
}
}
void mbuf_resize(struct mbuf *a, size_t new_size) WEAK;
void mbuf_resize(struct mbuf *a, size_t new_size) {
if (new_size > a->size || (new_size < a->size && new_size >= a->len)) {
char *buf = (char *) MBUF_REALLOC(a->buf, new_size);
/*
* In case realloc fails, there's not much we can do, except keep things as
* they are. Note that NULL is a valid return value from realloc when
* size == 0, but that is covered too.
*/
if (buf == NULL && new_size != 0) return;
a->buf = buf;
a->size = new_size;
}
}
void mbuf_trim(struct mbuf *mbuf) WEAK;
void mbuf_trim(struct mbuf *mbuf) {
mbuf_resize(mbuf, mbuf->len);
}
size_t mbuf_insert(struct mbuf *a, size_t off, const void *buf, size_t) WEAK;
size_t mbuf_insert(struct mbuf *a, size_t off, const void *buf, size_t len) {
char *p = NULL;
assert(a != NULL);
assert(a->len <= a->size);
assert(off <= a->len);
/* check overflow */
if (~(size_t) 0 - (size_t) a->buf < len) return 0;
if (a->len + len <= a->size) {
memmove(a->buf + off + len, a->buf + off, a->len - off);
if (buf != NULL) {
memcpy(a->buf + off, buf, len);
}
a->len += len;
} else {
size_t new_size = (size_t)((a->len + len) * MBUF_SIZE_MULTIPLIER);
if ((p = (char *) MBUF_REALLOC(a->buf, new_size)) != NULL) {
a->buf = p;
memmove(a->buf + off + len, a->buf + off, a->len - off);
if (buf != NULL) memcpy(a->buf + off, buf, len);
a->len += len;
a->size = new_size;
} else {
len = 0;
}
}
return len;
}
size_t mbuf_append(struct mbuf *a, const void *buf, size_t len) WEAK;
size_t mbuf_append(struct mbuf *a, const void *buf, size_t len) {
return mbuf_insert(a, a->len, buf, len);
}
void mbuf_remove(struct mbuf *mb, size_t n) WEAK;
void mbuf_remove(struct mbuf *mb, size_t n) {
if (n > 0 && n <= mb->len) {
memmove(mb->buf, mb->buf + n, mb->len - n);
mb->len -= n;
}
}
#endif /* EXCLUDE_COMMON */
#ifdef V7_MODULE_LINES
#line 1 "common/str_util.c"
#endif
/*
* Copyright (c) 2015 Cesanta Software Limited
* All rights reserved
*/
#ifndef EXCLUDE_COMMON
/* Amalgamated: #include "common/mg_mem.h" */
/* Amalgamated: #include "common/platform.h" */
/* Amalgamated: #include "common/str_util.h" */
#ifndef C_DISABLE_BUILTIN_SNPRINTF
#define C_DISABLE_BUILTIN_SNPRINTF 0
#endif
/* Amalgamated: #include "common/mg_mem.h" */
size_t c_strnlen(const char *s, size_t maxlen) WEAK;
size_t c_strnlen(const char *s, size_t maxlen) {
size_t l = 0;
for (; l < maxlen && s[l] != '\0'; l++) {
}
return l;
}
#define C_SNPRINTF_APPEND_CHAR(ch) \
do { \
if (i < (int) buf_size) buf[i] = ch; \
i++; \
} while (0)
#define C_SNPRINTF_FLAG_ZERO 1
#if C_DISABLE_BUILTIN_SNPRINTF
int c_vsnprintf(char *buf, size_t buf_size, const char *fmt, va_list ap) WEAK;
int c_vsnprintf(char *buf, size_t buf_size, const char *fmt, va_list ap) {
return vsnprintf(buf, buf_size, fmt, ap);
}
#else
static int c_itoa(char *buf, size_t buf_size, int64_t num, int base, int flags,
int field_width) {
char tmp[40];
int i = 0, k = 0, neg = 0;
if (num < 0) {
neg++;
num = -num;
}
/* Print into temporary buffer - in reverse order */
do {
int rem = num % base;
if (rem < 10) {
tmp[k++] = '0' + rem;
} else {
tmp[k++] = 'a' + (rem - 10);
}
num /= base;
} while (num > 0);
/* Zero padding */
if (flags && C_SNPRINTF_FLAG_ZERO) {
while (k < field_width && k < (int) sizeof(tmp) - 1) {
tmp[k++] = '0';
}
}
/* And sign */
if (neg) {
tmp[k++] = '-';
}
/* Now output */
while (--k >= 0) {
C_SNPRINTF_APPEND_CHAR(tmp[k]);
}
return i;
}
int c_vsnprintf(char *buf, size_t buf_size, const char *fmt, va_list ap) WEAK;
int c_vsnprintf(char *buf, size_t buf_size, const char *fmt, va_list ap) {
int ch, i = 0, len_mod, flags, precision, field_width;
while ((ch = *fmt++) != '\0') {
if (ch != '%') {
C_SNPRINTF_APPEND_CHAR(ch);
} else {
/*
* Conversion specification:
* zero or more flags (one of: # 0 - + ')
* an optional minimum field width (digits)
* an optional precision (. followed by digits, or *)
* an optional length modifier (one of: hh h l ll L q j z t)
* conversion specifier (one of: d i o u x X e E f F g G a A c s p n)
*/
flags = field_width = precision = len_mod = 0;
/* Flags. only zero-pad flag is supported. */
if (*fmt == '0') {
flags |= C_SNPRINTF_FLAG_ZERO;
}
/* Field width */
while (*fmt >= '0' && *fmt <= '9') {
field_width *= 10;
field_width += *fmt++ - '0';
}
/* Dynamic field width */
if (*fmt == '*') {
field_width = va_arg(ap, int);
fmt++;
}
/* Precision */
if (*fmt == '.') {
fmt++;
if (*fmt == '*') {
precision = va_arg(ap, int);
fmt++;
} else {
while (*fmt >= '0' && *fmt <= '9') {
precision *= 10;
precision += *fmt++ - '0';
}
}
}
/* Length modifier */
switch (*fmt) {
case 'h':
case 'l':
case 'L':
case 'I':
case 'q':
case 'j':
case 'z':
case 't':
len_mod = *fmt++;
if (*fmt == 'h') {
len_mod = 'H';
fmt++;
}
if (*fmt == 'l') {
len_mod = 'q';
fmt++;
}
break;
}
ch = *fmt++;
if (ch == 's') {
const char *s = va_arg(ap, const char *); /* Always fetch parameter */
int j;
int pad = field_width - (precision >= 0 ? c_strnlen(s, precision) : 0);
for (j = 0; j < pad; j++) {
C_SNPRINTF_APPEND_CHAR(' ');
}
/* `s` may be NULL in case of %.*s */
if (s != NULL) {
/* Ignore negative and 0 precisions */
for (j = 0; (precision <= 0 || j < precision) && s[j] != '\0'; j++) {
C_SNPRINTF_APPEND_CHAR(s[j]);
}
}
} else if (ch == 'c') {
ch = va_arg(ap, int); /* Always fetch parameter */
C_SNPRINTF_APPEND_CHAR(ch);
} else if (ch == 'd' && len_mod == 0) {
i += c_itoa(buf + i, buf_size - i, va_arg(ap, int), 10, flags,
field_width);
} else if (ch == 'd' && len_mod == 'l') {
i += c_itoa(buf + i, buf_size - i, va_arg(ap, long), 10, flags,
field_width);
#ifdef SSIZE_MAX
} else if (ch == 'd' && len_mod == 'z') {
i += c_itoa(buf + i, buf_size - i, va_arg(ap, ssize_t), 10, flags,
field_width);
#endif
} else if (ch == 'd' && len_mod == 'q') {
i += c_itoa(buf + i, buf_size - i, va_arg(ap, int64_t), 10, flags,
field_width);
} else if ((ch == 'x' || ch == 'u') && len_mod == 0) {
i += c_itoa(buf + i, buf_size - i, va_arg(ap, unsigned),
ch == 'x' ? 16 : 10, flags, field_width);
} else if ((ch == 'x' || ch == 'u') && len_mod == 'l') {
i += c_itoa(buf + i, buf_size - i, va_arg(ap, unsigned long),
ch == 'x' ? 16 : 10, flags, field_width);
} else if ((ch == 'x' || ch == 'u') && len_mod == 'z') {
i += c_itoa(buf + i, buf_size - i, va_arg(ap, size_t),
ch == 'x' ? 16 : 10, flags, field_width);
} else if (ch == 'p') {
unsigned long num = (unsigned long) (uintptr_t) va_arg(ap, void *);
C_SNPRINTF_APPEND_CHAR('0');
C_SNPRINTF_APPEND_CHAR('x');
i += c_itoa(buf + i, buf_size - i, num, 16, flags, 0);
} else {
#ifndef NO_LIBC
/*
* TODO(lsm): abort is not nice in a library, remove it
* Also, ESP8266 SDK doesn't have it
*/
abort();
#endif
}
}
}
/* Zero-terminate the result */
if (buf_size > 0) {
buf[i < (int) buf_size ? i : (int) buf_size - 1] = '\0';
}
return i;
}
#endif
int c_snprintf(char *buf, size_t buf_size, const char *fmt, ...) WEAK;
int c_snprintf(char *buf, size_t buf_size, const char *fmt, ...) {
int result;
va_list ap;
va_start(ap, fmt);
result = c_vsnprintf(buf, buf_size, fmt, ap);
va_end(ap);
return result;
}
#ifdef _WIN32
int to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) {
int ret;
char buf[MAX_PATH * 2], buf2[MAX_PATH * 2], *p;
strncpy(buf, path, sizeof(buf));
buf[sizeof(buf) - 1] = '\0';
/* Trim trailing slashes. Leave backslash for paths like "X:\" */
p = buf + strlen(buf) - 1;
while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) *p-- = '\0';
memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
ret = MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
/*
* Convert back to Unicode. If doubly-converted string does not match the
* original, something is fishy, reject.
*/
WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2),
NULL, NULL);
if (strcmp(buf, buf2) != 0) {
wbuf[0] = L'\0';
ret = 0;
}
return ret;
}
#endif /* _WIN32 */
/* The simplest O(mn) algorithm. Better implementation are GPLed */
const char *c_strnstr(const char *s, const char *find, size_t slen) WEAK;
const char *c_strnstr(const char *s, const char *find, size_t slen) {
size_t find_length = strlen(find);
size_t i;
for (i = 0; i < slen; i++) {
if (i + find_length > slen) {
return NULL;
}
if (strncmp(&s[i], find, find_length) == 0) {
return &s[i];
}
}
return NULL;
}
#if CS_ENABLE_STRDUP
char *strdup(const char *src) WEAK;
char *strdup(const char *src) {
size_t len = strlen(src) + 1;
char *ret = MG_MALLOC(len);
if (ret != NULL) {
strcpy(ret, src);
}
return ret;
}
#endif
void cs_to_hex(char *to, const unsigned char *p, size_t len) WEAK;
void cs_to_hex(char *to, const unsigned char *p, size_t len) {
static const char *hex = "0123456789abcdef";
for (; len--; p++) {
*to++ = hex[p[0] >> 4];
*to++ = hex[p[0] & 0x0f];
}
*to = '\0';
}
static int fourbit(int ch) {
if (ch >= '0' && ch <= '9') {
return ch - '0';
} else if (ch >= 'a' && ch <= 'f') {
return ch - 'a' + 10;
} else if (ch >= 'A' && ch <= 'F') {
return ch - 'A' + 10;
}
return 0;
}
void cs_from_hex(char *to, const char *p, size_t len) WEAK;
void cs_from_hex(char *to, const char *p, size_t len) {
size_t i;
for (i = 0; i < len; i += 2) {
*to++ = (fourbit(p[i]) << 4) + fourbit(p[i + 1]);
}
*to = '\0';
}
#if CS_ENABLE_TO64
int64_t cs_to64(const char *s) WEAK;
int64_t cs_to64(const char *s) {
int64_t result = 0;
int64_t neg = 1;
while (*s && isspace((unsigned char) *s)) s++;
if (*s == '-') {
neg = -1;
s++;
}
while (isdigit((unsigned char) *s)) {
result *= 10;
result += (*s - '0');
s++;
}
return result * neg;
}
#endif
static int str_util_lowercase(const char *s) {
return tolower(*(const unsigned char *) s);
}
int mg_ncasecmp(const char *s1, const char *s2, size_t len) WEAK;
int mg_ncasecmp(const char *s1, const char *s2, size_t len) {
int diff = 0;
if (len > 0) do {
diff = str_util_lowercase(s1++) - str_util_lowercase(s2++);
} while (diff == 0 && s1[-1] != '\0' && --len > 0);
return diff;
}
int mg_casecmp(const char *s1, const char *s2) WEAK;
int mg_casecmp(const char *s1, const char *s2) {
return mg_ncasecmp(s1, s2, (size_t) ~0);
}
int mg_asprintf(char **buf, size_t size, const char *fmt, ...) WEAK;
int mg_asprintf(char **buf, size_t size, const char *fmt, ...) {
int ret;
va_list ap;
va_start(ap, fmt);
ret = mg_avprintf(buf, size, fmt, ap);
va_end(ap);
return ret;
}
int mg_avprintf(char **buf, size_t size, const char *fmt, va_list ap) WEAK;
int mg_avprintf(char **buf, size_t size, const char *fmt, va_list ap) {
va_list ap_copy;
int len;
va_copy(ap_copy, ap);
len = vsnprintf(*buf, size, fmt, ap_copy);
va_end(ap_copy);
if (len < 0) {
/* eCos and Windows are not standard-compliant and return -1 when
* the buffer is too small. Keep allocating larger buffers until we
* succeed or out of memory. */
*buf = NULL; /* LCOV_EXCL_START */
while (len < 0) {
MG_FREE(*buf);
size *= 2;
if ((*buf = (char *) MG_MALLOC(size)) == NULL) break;
va_copy(ap_copy, ap);
len = vsnprintf(*buf, size, fmt, ap_copy);
va_end(ap_copy);
}
/* LCOV_EXCL_STOP */
} else if (len >= (int) size) {
/* Standard-compliant code path. Allocate a buffer that is large enough. */
if ((*buf = (char *) MG_MALLOC(len + 1)) == NULL) {
len = -1; /* LCOV_EXCL_LINE */
} else { /* LCOV_EXCL_LINE */
va_copy(ap_copy, ap);
len = vsnprintf(*buf, len + 1, fmt, ap_copy);
va_end(ap_copy);
}
}
return len;
}
const char *mg_next_comma_list_entry(const char *, struct mg_str *,
struct mg_str *) WEAK;
const char *mg_next_comma_list_entry(const char *list, struct mg_str *val,
struct mg_str *eq_val) {
struct mg_str ret = mg_next_comma_list_entry_n(mg_mk_str(list), val, eq_val);
return ret.p;
}
struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val,
struct mg_str *eq_val) WEAK;
struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val,
struct mg_str *eq_val) {
if (list.len == 0) {
/* End of the list */
list = mg_mk_str(NULL);
} else {
const char *chr = NULL;
*val = list;
if ((chr = mg_strchr(*val, ',')) != NULL) {
/* Comma found. Store length and shift the list ptr */
val->len = chr - val->p;
chr++;
list.len -= (chr - list.p);
list.p = chr;
} else {
/* This value is the last one */
list = mg_mk_str_n(list.p + list.len, 0);
}
if (eq_val != NULL) {
/* Value has form "x=y", adjust pointers and lengths */
/* so that val points to "x", and eq_val points to "y". */
eq_val->len = 0;
eq_val->p = (const char *) memchr(val->p, '=', val->len);
if (eq_val->p != NULL) {
eq_val->p++; /* Skip over '=' character */
eq_val->len = val->p + val->len - eq_val->p;
val->len = (eq_val->p - val->p) - 1;
}
}
}
return list;
}
int mg_match_prefix_n(const struct mg_str, const struct mg_str) WEAK;
int mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str) {
const char *or_str;
size_t len, i = 0, j = 0;
int res;
if ((or_str = (const char *) memchr(pattern.p, '|', pattern.len)) != NULL ||
(or_str = (const char *) memchr(pattern.p, ',', pattern.len)) != NULL) {
struct mg_str pstr = {pattern.p, (size_t)(or_str - pattern.p)};
res = mg_match_prefix_n(pstr, str);
if (res > 0) return res;
pstr.p = or_str + 1;
pstr.len = (pattern.p + pattern.len) - (or_str + 1);
return mg_match_prefix_n(pstr, str);
}
for (; i < pattern.len; i++, j++) {
if (pattern.p[i] == '?' && j != str.len) {
continue;
} else if (pattern.p[i] == '$') {
return j == str.len ? (int) j : -1;
} else if (pattern.p[i] == '*') {
i++;
if (i < pattern.len && pattern.p[i] == '*') {
i++;
len = str.len - j;
} else {
len = 0;
while (j + len != str.len && str.p[j + len] != '/') {
len++;
}
}
if (i == pattern.len) {
return j + len;
}
do {
const struct mg_str pstr = {pattern.p + i, pattern.len - i};
const struct mg_str sstr = {str.p + j + len, str.len - j - len};
res = mg_match_prefix_n(pstr, sstr);
} while (res == -1 && len-- > 0);
return res == -1 ? -1 : (int) (j + res + len);
} else if (str_util_lowercase(&pattern.p[i]) !=
str_util_lowercase(&str.p[j])) {
return -1;
}
}
return j;
}
int mg_match_prefix(const char *, int, const char *) WEAK;
int mg_match_prefix(const char *pattern, int pattern_len, const char *str) {
const struct mg_str pstr = {pattern, (size_t) pattern_len};
struct mg_str s = {str, 0};
if (str != NULL) s.len = strlen(str);
return mg_match_prefix_n(pstr, s);
}
#endif /* EXCLUDE_COMMON */
#ifdef V7_MODULE_LINES
#line 1 "common/utf.c"
#endif
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
#ifndef EXCLUDE_COMMON
/* clang-format off */
#include
#include
/* Amalgamated: #include "common/platform.h" */
/* Amalgamated: #include "common/str_util.h" */
/* Amalgamated: #include "common/utf.h" */
#ifndef CS_ENABLE_UTF8
#define CS_ENABLE_UTF8 0
#endif
#if CS_ENABLE_UTF8
enum {
Bit1 = 7,
Bitx = 6,
Bit2 = 5,
Bit3 = 4,
Bit4 = 3,
Bit5 = 2,
T1 = ((1 << (Bit1 + 1)) - 1) ^ 0xFF, /* 0000 0000 */
Tx = ((1 << (Bitx + 1)) - 1) ^ 0xFF, /* 1000 0000 */
T2 = ((1 << (Bit2 + 1)) - 1) ^ 0xFF, /* 1100 0000 */
T3 = ((1 << (Bit3 + 1)) - 1) ^ 0xFF, /* 1110 0000 */
T4 = ((1 << (Bit4 + 1)) - 1) ^ 0xFF, /* 1111 0000 */
T5 = ((1 << (Bit5 + 1)) - 1) ^ 0xFF, /* 1111 1000 */
Rune1 = (1 << (Bit1 + 0 * Bitx)) - 1, /* 0000 0000 0000 0000 0111 1111 */
Rune2 = (1 << (Bit2 + 1 * Bitx)) - 1, /* 0000 0000 0000 0111 1111 1111 */
Rune3 = (1 << (Bit3 + 2 * Bitx)) - 1, /* 0000 0000 1111 1111 1111 1111 */
Rune4 = (1 << (Bit4 + 3 * Bitx)) - 1, /* 0011 1111 1111 1111 1111 1111 */
Maskx = (1 << Bitx) - 1, /* 0011 1111 */
Testx = Maskx ^ 0xFF, /* 1100 0000 */
Bad = Runeerror
};
int chartorune(Rune *rune, const char *str) {
int c, c1, c2 /* , c3 */;
unsigned short l;
/*
* one character sequence
* 00000-0007F => T1
*/
c = *(uchar *) str;
if (c < Tx) {
*rune = c;
return 1;
}
/*
* two character sequence
* 0080-07FF => T2 Tx
*/
c1 = *(uchar *) (str + 1) ^ Tx;
if (c1 & Testx) goto bad;
if (c < T3) {
if (c < T2) goto bad;
l = ((c << Bitx) | c1) & Rune2;
if (l <= Rune1) goto bad;
*rune = l;
return 2;
}
/*
* three character sequence
* 0800-FFFF => T3 Tx Tx
*/
c2 = *(uchar *) (str + 2) ^ Tx;
if (c2 & Testx) goto bad;
if (c < T4) {
l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3;
if (l <= Rune2) goto bad;
*rune = l;
return 3;
}
/*
* four character sequence
* 10000-10FFFF => T4 Tx Tx Tx
*/
/* if(UTFmax >= 4) {
c3 = *(uchar*)(str+3) ^ Tx;
if(c3 & Testx)
goto bad;
if(c < T5) {
l = ((((((c << Bitx) | c1) << Bitx) | c2) << Bitx) | c3) &
Rune4;
if(l <= Rune3)
goto bad;
if(l > Runemax)
goto bad;
*rune = l;
return 4;
}
} */
/*
* bad decoding
*/
bad:
*rune = Bad;
return 1;
}
int runetochar(char *str, Rune *rune) {
unsigned short c;
/*
* one character sequence
* 00000-0007F => 00-7F
*/
c = *rune;
if (c <= Rune1) {
str[0] = c;
return 1;
}
/*
* two character sequence
* 00080-007FF => T2 Tx
*/
if (c <= Rune2) {
str[0] = T2 | (c >> 1 * Bitx);
str[1] = Tx | (c & Maskx);
return 2;
}
/*
* three character sequence
* 00800-0FFFF => T3 Tx Tx
*/
/* if(c > Runemax)
c = Runeerror; */
/* if(c <= Rune3) { */
str[0] = T3 | (c >> 2 * Bitx);
str[1] = Tx | ((c >> 1 * Bitx) & Maskx);
str[2] = Tx | (c & Maskx);
return 3;
/* } */
/*
* four character sequence
* 010000-1FFFFF => T4 Tx Tx Tx
*/
/* str[0] = T4 | (c >> 3*Bitx);
str[1] = Tx | ((c >> 2*Bitx) & Maskx);
str[2] = Tx | ((c >> 1*Bitx) & Maskx);
str[3] = Tx | (c & Maskx);
return 4; */
}
int fullrune(const char *str, int n) {
int c;
if (n <= 0) return 0;
c = *(uchar *) str;
if (c < Tx) return 1;
if (c < T3) return n >= 2;
if (UTFmax == 3 || c < T4) return n >= 3;
return n >= 4;
}
int utfnlen(const char *s, long m) {
int c;
long n;
Rune rune;
const char *es;
es = s + m;
for (n = 0; s < es; n++) {
c = *(uchar *) s;
if (c < Runeself) {
s++;
continue;
}
if (!fullrune(s, es - s)) break;
s += chartorune(&rune, s);
}
return n;
}
const char *utfnshift(const char *s, long m) {
int c;
long n;
Rune rune;
for (n = 0; n < m; n++) {
c = *(uchar *) s;
if (c < Runeself) {
s++;
continue;
}
s += chartorune(&rune, s);
}
return s;
}
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
#include
#include
/* Amalgamated: #include "common/utf.h" */
/*
* alpha ranges -
* only covers ranges not in lower||upper
*/
static Rune __alpha2[] = {
0x00d8, 0x00f6, /* Ø - ö */
0x00f8, 0x01f5, /* ø - ǵ */
0x0250, 0x02a8, /* ɐ - ʨ */
0x038e, 0x03a1, /* Ύ - Ρ */
0x03a3, 0x03ce, /* Σ - ώ */
0x03d0, 0x03d6, /* ϐ - ϖ */
0x03e2, 0x03f3, /* Ϣ - ϳ */
0x0490, 0x04c4, /* Ґ - ӄ */
0x0561, 0x0587, /* ա - և */
0x05d0, 0x05ea, /* א - ת */
0x05f0, 0x05f2, /* װ - ײ */
0x0621, 0x063a, /* ء - غ */
0x0640, 0x064a, /* ـ - ي */
0x0671, 0x06b7, /* ٱ - ڷ */
0x06ba, 0x06be, /* ں - ھ */
0x06c0, 0x06ce, /* ۀ - ێ */
0x06d0, 0x06d3, /* ې - ۓ */
0x0905, 0x0939, /* अ - ह */
0x0958, 0x0961, /* क़ - ॡ */
0x0985, 0x098c, /* অ - ঌ */
0x098f, 0x0990, /* এ - ঐ */
0x0993, 0x09a8, /* ও - ন */
0x09aa, 0x09b0, /* প - র */
0x09b6, 0x09b9, /* শ - হ */
0x09dc, 0x09dd, /* ড় - ঢ় */
0x09df, 0x09e1, /* য় - ৡ */
0x09f0, 0x09f1, /* ৰ - ৱ */
0x0a05, 0x0a0a, /* ਅ - ਊ */
0x0a0f, 0x0a10, /* ਏ - ਐ */
0x0a13, 0x0a28, /* ਓ - ਨ */
0x0a2a, 0x0a30, /* ਪ - ਰ */
0x0a32, 0x0a33, /* ਲ - ਲ਼ */
0x0a35, 0x0a36, /* ਵ - ਸ਼ */
0x0a38, 0x0a39, /* ਸ - ਹ */
0x0a59, 0x0a5c, /* ਖ਼ - ੜ */
0x0a85, 0x0a8b, /* અ - ઋ */
0x0a8f, 0x0a91, /* એ - ઑ */
0x0a93, 0x0aa8, /* ઓ - ન */
0x0aaa, 0x0ab0, /* પ - ર */
0x0ab2, 0x0ab3, /* લ - ળ */
0x0ab5, 0x0ab9, /* વ - હ */
0x0b05, 0x0b0c, /* ଅ - ଌ */
0x0b0f, 0x0b10, /* ଏ - ଐ */
0x0b13, 0x0b28, /* ଓ - ନ */
0x0b2a, 0x0b30, /* ପ - ର */
0x0b32, 0x0b33, /* ଲ - ଳ */
0x0b36, 0x0b39, /* ଶ - ହ */
0x0b5c, 0x0b5d, /* ଡ଼ - ଢ଼ */
0x0b5f, 0x0b61, /* ୟ - ୡ */
0x0b85, 0x0b8a, /* அ - ஊ */
0x0b8e, 0x0b90, /* எ - ஐ */
0x0b92, 0x0b95, /* ஒ - க */
0x0b99, 0x0b9a, /* ங - ச */
0x0b9e, 0x0b9f, /* ஞ - ட */
0x0ba3, 0x0ba4, /* ண - த */
0x0ba8, 0x0baa, /* ந - ப */
0x0bae, 0x0bb5, /* ம - வ */
0x0bb7, 0x0bb9, /* ஷ - ஹ */
0x0c05, 0x0c0c, /* అ - ఌ */
0x0c0e, 0x0c10, /* ఎ - ఐ */
0x0c12, 0x0c28, /* ఒ - న */
0x0c2a, 0x0c33, /* ప - ళ */
0x0c35, 0x0c39, /* వ - హ */
0x0c60, 0x0c61, /* ౠ - ౡ */
0x0c85, 0x0c8c, /* ಅ - ಌ */
0x0c8e, 0x0c90, /* ಎ - ಐ */
0x0c92, 0x0ca8, /* ಒ - ನ */
0x0caa, 0x0cb3, /* ಪ - ಳ */
0x0cb5, 0x0cb9, /* ವ - ಹ */
0x0ce0, 0x0ce1, /* ೠ - ೡ */
0x0d05, 0x0d0c, /* അ - ഌ */
0x0d0e, 0x0d10, /* എ - ഐ */
0x0d12, 0x0d28, /* ഒ - ന */
0x0d2a, 0x0d39, /* പ - ഹ */
0x0d60, 0x0d61, /* ൠ - ൡ */
0x0e01, 0x0e30, /* ก - ะ */
0x0e32, 0x0e33, /* า - ำ */
0x0e40, 0x0e46, /* เ - ๆ */
0x0e5a, 0x0e5b, /* ๚ - ๛ */
0x0e81, 0x0e82, /* ກ - ຂ */
0x0e87, 0x0e88, /* ງ - ຈ */
0x0e94, 0x0e97, /* ດ - ທ */
0x0e99, 0x0e9f, /* ນ - ຟ */
0x0ea1, 0x0ea3, /* ມ - ຣ */
0x0eaa, 0x0eab, /* ສ - ຫ */
0x0ead, 0x0eae, /* ອ - ຮ */
0x0eb2, 0x0eb3, /* າ - ຳ */
0x0ec0, 0x0ec4, /* ເ - ໄ */
0x0edc, 0x0edd, /* ໜ - ໝ */
0x0f18, 0x0f19, /* ༘ - ༙ */
0x0f40, 0x0f47, /* ཀ - ཇ */
0x0f49, 0x0f69, /* ཉ - ཀྵ */
0x10d0, 0x10f6, /* ა - ჶ */
0x1100, 0x1159, /* ᄀ - ᅙ */
0x115f, 0x11a2, /* ᅟ - ᆢ */
0x11a8, 0x11f9, /* ᆨ - ᇹ */
0x1e00, 0x1e9b, /* Ḁ - ẛ */
0x1f50, 0x1f57, /* ὐ - ὗ */
0x1f80, 0x1fb4, /* ᾀ - ᾴ */
0x1fb6, 0x1fbc, /* ᾶ - ᾼ */
0x1fc2, 0x1fc4, /* ῂ - ῄ */
0x1fc6, 0x1fcc, /* ῆ - ῌ */
0x1fd0, 0x1fd3, /* ῐ - ΐ */
0x1fd6, 0x1fdb, /* ῖ - Ί */
0x1fe0, 0x1fec, /* ῠ - Ῥ */
0x1ff2, 0x1ff4, /* ῲ - ῴ */
0x1ff6, 0x1ffc, /* ῶ - ῼ */
0x210a, 0x2113, /* ℊ - ℓ */
0x2115, 0x211d, /* ℕ - ℝ */
0x2120, 0x2122, /* ℠ - ™ */
0x212a, 0x2131, /* K - ℱ */
0x2133, 0x2138, /* ℳ - ℸ */
0x3041, 0x3094, /* ぁ - ゔ */
0x30a1, 0x30fa, /* ァ - ヺ */
0x3105, 0x312c, /* ㄅ - ㄬ */
0x3131, 0x318e, /* ㄱ - ㆎ */
0x3192, 0x319f, /* ㆒ - ㆟ */
0x3260, 0x327b, /* ㉠ - ㉻ */
0x328a, 0x32b0, /* ㊊ - ㊰ */
0x32d0, 0x32fe, /* ㋐ - ㋾ */
0x3300, 0x3357, /* ㌀ - ㍗ */
0x3371, 0x3376, /* ㍱ - ㍶ */
0x337b, 0x3394, /* ㍻ - ㎔ */
0x3399, 0x339e, /* ㎙ - ㎞ */
0x33a9, 0x33ad, /* ㎩ - ㎭ */
0x33b0, 0x33c1, /* ㎰ - ㏁ */
0x33c3, 0x33c5, /* ㏃ - ㏅ */
0x33c7, 0x33d7, /* ㏇ - ㏗ */
0x33d9, 0x33dd, /* ㏙ - ㏝ */
0x4e00, 0x9fff, /* 一 - 鿿 */
0xac00, 0xd7a3, /* 가 - 힣 */
0xf900, 0xfb06, /* 豈 - st */
0xfb13, 0xfb17, /* ﬓ - ﬗ */
0xfb1f, 0xfb28, /* ײַ - ﬨ */
0xfb2a, 0xfb36, /* שׁ - זּ */
0xfb38, 0xfb3c, /* טּ - לּ */
0xfb40, 0xfb41, /* נּ - סּ */
0xfb43, 0xfb44, /* ףּ - פּ */
0xfb46, 0xfbb1, /* צּ - ﮱ */
0xfbd3, 0xfd3d, /* ﯓ - ﴽ */
0xfd50, 0xfd8f, /* ﵐ - ﶏ */
0xfd92, 0xfdc7, /* ﶒ - ﷇ */
0xfdf0, 0xfdf9, /* ﷰ - ﷹ */
0xfe70, 0xfe72, /* ﹰ - ﹲ */
0xfe76, 0xfefc, /* ﹶ - ﻼ */
0xff66, 0xff6f, /* ヲ - ッ */
0xff71, 0xff9d, /* ア - ン */
0xffa0, 0xffbe, /* ᅠ - ᄒ */
0xffc2, 0xffc7, /* ᅡ - ᅦ */
0xffca, 0xffcf, /* ᅧ - ᅬ */
0xffd2, 0xffd7, /* ᅭ - ᅲ */
0xffda, 0xffdc, /* ᅳ - ᅵ */
};
/*
* alpha singlets -
* only covers ranges not in lower||upper
*/
static Rune __alpha1[] = {
0x00aa, /* ª */
0x00b5, /* µ */
0x00ba, /* º */
0x03da, /* Ϛ */
0x03dc, /* Ϝ */
0x03de, /* Ϟ */
0x03e0, /* Ϡ */
0x06d5, /* ە */
0x09b2, /* ল */
0x0a5e, /* ਫ਼ */
0x0a8d, /* ઍ */
0x0ae0, /* ૠ */
0x0b9c, /* ஜ */
0x0cde, /* ೞ */
0x0e4f, /* ๏ */
0x0e84, /* ຄ */
0x0e8a, /* ຊ */
0x0e8d, /* ຍ */
0x0ea5, /* ລ */
0x0ea7, /* ວ */
0x0eb0, /* ະ */
0x0ebd, /* ຽ */
0x1fbe, /* ι */
0x207f, /* ⁿ */
0x20a8, /* ₨ */
0x2102, /* ℂ */
0x2107, /* ℇ */
0x2124, /* ℤ */
0x2126, /* Ω */
0x2128, /* ℨ */
0xfb3e, /* מּ */
0xfe74, /* ﹴ */
};
/*
* space ranges
*/
static Rune __space2[] = {
0x0009, 0x000a, /* tab and newline */
0x0020, 0x0020, /* space */
0x00a0, 0x00a0, /* */
0x2000, 0x200b, /* - */
0x2028, 0x2029, /*
-
*/
0x3000, 0x3000, /* */
0xfeff, 0xfeff, /* */
};
/*
* lower case ranges
* 3rd col is conversion excess 500
*/
static Rune __toupper2[] = {
0x0061, 0x007a, 468, /* a-z A-Z */
0x00e0, 0x00f6, 468, /* à-ö À-Ö */
0x00f8, 0x00fe, 468, /* ø-þ Ø-Þ */
0x0256, 0x0257, 295, /* ɖ-ɗ Ɖ-Ɗ */
0x0258, 0x0259, 298, /* ɘ-ə Ǝ-Ə */
0x028a, 0x028b, 283, /* ʊ-ʋ Ʊ-Ʋ */
0x03ad, 0x03af, 463, /* έ-ί Έ-Ί */
0x03b1, 0x03c1, 468, /* α-ρ Α-Ρ */
0x03c3, 0x03cb, 468, /* σ-ϋ Σ-Ϋ */
0x03cd, 0x03ce, 437, /* ύ-ώ Ύ-Ώ */
0x0430, 0x044f, 468, /* а-я А-Я */
0x0451, 0x045c, 420, /* ё-ќ Ё-Ќ */
0x045e, 0x045f, 420, /* ў-џ Ў-Џ */
0x0561, 0x0586, 452, /* ա-ֆ Ա-Ֆ */
0x1f00, 0x1f07, 508, /* ἀ-ἇ Ἀ-Ἇ */
0x1f10, 0x1f15, 508, /* ἐ-ἕ Ἐ-Ἕ */
0x1f20, 0x1f27, 508, /* ἠ-ἧ Ἠ-Ἧ */
0x1f30, 0x1f37, 508, /* ἰ-ἷ Ἰ-Ἷ */
0x1f40, 0x1f45, 508, /* ὀ-ὅ Ὀ-Ὅ */
0x1f60, 0x1f67, 508, /* ὠ-ὧ Ὠ-Ὧ */
0x1f70, 0x1f71, 574, /* ὰ-ά Ὰ-Ά */
0x1f72, 0x1f75, 586, /* ὲ-ή Ὲ-Ή */
0x1f76, 0x1f77, 600, /* ὶ-ί Ὶ-Ί */
0x1f78, 0x1f79, 628, /* ὸ-ό Ὸ-Ό */
0x1f7a, 0x1f7b, 612, /* ὺ-ύ Ὺ-Ύ */
0x1f7c, 0x1f7d, 626, /* ὼ-ώ Ὼ-Ώ */
0x1f80, 0x1f87, 508, /* ᾀ-ᾇ ᾈ-ᾏ */
0x1f90, 0x1f97, 508, /* ᾐ-ᾗ ᾘ-ᾟ */
0x1fa0, 0x1fa7, 508, /* ᾠ-ᾧ ᾨ-ᾯ */
0x1fb0, 0x1fb1, 508, /* ᾰ-ᾱ Ᾰ-Ᾱ */
0x1fd0, 0x1fd1, 508, /* ῐ-ῑ Ῐ-Ῑ */
0x1fe0, 0x1fe1, 508, /* ῠ-ῡ Ῠ-Ῡ */
0x2170, 0x217f, 484, /* ⅰ-ⅿ Ⅰ-Ⅿ */
0x24d0, 0x24e9, 474, /* ⓐ-ⓩ Ⓐ-Ⓩ */
0xff41, 0xff5a, 468, /* a-z A-Z */
};
/*
* lower case singlets
* 2nd col is conversion excess 500
*/
static Rune __toupper1[] = {
0x00ff, 621, /* ÿ Ÿ */
0x0101, 499, /* ā Ā */
0x0103, 499, /* ă Ă */
0x0105, 499, /* ą Ą */
0x0107, 499, /* ć Ć */
0x0109, 499, /* ĉ Ĉ */
0x010b, 499, /* ċ Ċ */
0x010d, 499, /* č Č */
0x010f, 499, /* ď Ď */
0x0111, 499, /* đ Đ */
0x0113, 499, /* ē Ē */
0x0115, 499, /* ĕ Ĕ */
0x0117, 499, /* ė Ė */
0x0119, 499, /* ę Ę */
0x011b, 499, /* ě Ě */
0x011d, 499, /* ĝ Ĝ */
0x011f, 499, /* ğ Ğ */
0x0121, 499, /* ġ Ġ */
0x0123, 499, /* ģ Ģ */
0x0125, 499, /* ĥ Ĥ */
0x0127, 499, /* ħ Ħ */
0x0129, 499, /* ĩ Ĩ */
0x012b, 499, /* ī Ī */
0x012d, 499, /* ĭ Ĭ */
0x012f, 499, /* į Į */
0x0131, 268, /* ı I */
0x0133, 499, /* ij IJ */
0x0135, 499, /* ĵ Ĵ */
0x0137, 499, /* ķ Ķ */
0x013a, 499, /* ĺ Ĺ */
0x013c, 499, /* ļ Ļ */
0x013e, 499, /* ľ Ľ */
0x0140, 499, /* ŀ Ŀ */
0x0142, 499, /* ł Ł */
0x0144, 499, /* ń Ń */
0x0146, 499, /* ņ Ņ */
0x0148, 499, /* ň Ň */
0x014b, 499, /* ŋ Ŋ */
0x014d, 499, /* ō Ō */
0x014f, 499, /* ŏ Ŏ */
0x0151, 499, /* ő Ő */
0x0153, 499, /* œ Œ */
0x0155, 499, /* ŕ Ŕ */
0x0157, 499, /* ŗ Ŗ */
0x0159, 499, /* ř Ř */
0x015b, 499, /* ś Ś */
0x015d, 499, /* ŝ Ŝ */
0x015f, 499, /* ş Ş */
0x0161, 499, /* š Š */
0x0163, 499, /* ţ Ţ */
0x0165, 499, /* ť Ť */
0x0167, 499, /* ŧ Ŧ */
0x0169, 499, /* ũ Ũ */
0x016b, 499, /* ū Ū */
0x016d, 499, /* ŭ Ŭ */
0x016f, 499, /* ů Ů */
0x0171, 499, /* ű Ű */
0x0173, 499, /* ų Ų */
0x0175, 499, /* ŵ Ŵ */
0x0177, 499, /* ŷ Ŷ */
0x017a, 499, /* ź Ź */
0x017c, 499, /* ż Ż */
0x017e, 499, /* ž Ž */
0x017f, 200, /* ſ S */
0x0183, 499, /* ƃ Ƃ */
0x0185, 499, /* ƅ Ƅ */
0x0188, 499, /* ƈ Ƈ */
0x018c, 499, /* ƌ Ƌ */
0x0192, 499, /* ƒ Ƒ */
0x0199, 499, /* ƙ Ƙ */
0x01a1, 499, /* ơ Ơ */
0x01a3, 499, /* ƣ Ƣ */
0x01a5, 499, /* ƥ Ƥ */
0x01a8, 499, /* ƨ Ƨ */
0x01ad, 499, /* ƭ Ƭ */
0x01b0, 499, /* ư Ư */
0x01b4, 499, /* ƴ Ƴ */
0x01b6, 499, /* ƶ Ƶ */
0x01b9, 499, /* ƹ Ƹ */
0x01bd, 499, /* ƽ Ƽ */
0x01c5, 499, /* Dž DŽ */
0x01c6, 498, /* dž DŽ */
0x01c8, 499, /* Lj LJ */
0x01c9, 498, /* lj LJ */
0x01cb, 499, /* Nj NJ */
0x01cc, 498, /* nj NJ */
0x01ce, 499, /* ǎ Ǎ */
0x01d0, 499, /* ǐ Ǐ */
0x01d2, 499, /* ǒ Ǒ */
0x01d4, 499, /* ǔ Ǔ */
0x01d6, 499, /* ǖ Ǖ */
0x01d8, 499, /* ǘ Ǘ */
0x01da, 499, /* ǚ Ǚ */
0x01dc, 499, /* ǜ Ǜ */
0x01df, 499, /* ǟ Ǟ */
0x01e1, 499, /* ǡ Ǡ */
0x01e3, 499, /* ǣ Ǣ */
0x01e5, 499, /* ǥ Ǥ */
0x01e7, 499, /* ǧ Ǧ */
0x01e9, 499, /* ǩ Ǩ */
0x01eb, 499, /* ǫ Ǫ */
0x01ed, 499, /* ǭ Ǭ */
0x01ef, 499, /* ǯ Ǯ */
0x01f2, 499, /* Dz DZ */
0x01f3, 498, /* dz DZ */
0x01f5, 499, /* ǵ Ǵ */
0x01fb, 499, /* ǻ Ǻ */
0x01fd, 499, /* ǽ Ǽ */
0x01ff, 499, /* ǿ Ǿ */
0x0201, 499, /* ȁ Ȁ */
0x0203, 499, /* ȃ Ȃ */
0x0205, 499, /* ȅ Ȅ */
0x0207, 499, /* ȇ Ȇ */
0x0209, 499, /* ȉ Ȉ */
0x020b, 499, /* ȋ Ȋ */
0x020d, 499, /* ȍ Ȍ */
0x020f, 499, /* ȏ Ȏ */
0x0211, 499, /* ȑ Ȑ */
0x0213, 499, /* ȓ Ȓ */
0x0215, 499, /* ȕ Ȕ */
0x0217, 499, /* ȗ Ȗ */
0x0253, 290, /* ɓ Ɓ */
0x0254, 294, /* ɔ Ɔ */
0x025b, 297, /* ɛ Ɛ */
0x0260, 295, /* ɠ Ɠ */
0x0263, 293, /* ɣ Ɣ */
0x0268, 291, /* ɨ Ɨ */
0x0269, 289, /* ɩ Ɩ */
0x026f, 289, /* ɯ Ɯ */
0x0272, 287, /* ɲ Ɲ */
0x0283, 282, /* ʃ Ʃ */
0x0288, 282, /* ʈ Ʈ */
0x0292, 281, /* ʒ Ʒ */
0x03ac, 462, /* ά Ά */
0x03cc, 436, /* ό Ό */
0x03d0, 438, /* ϐ Β */
0x03d1, 443, /* ϑ Θ */
0x03d5, 453, /* ϕ Φ */
0x03d6, 446, /* ϖ Π */
0x03e3, 499, /* ϣ Ϣ */
0x03e5, 499, /* ϥ Ϥ */
0x03e7, 499, /* ϧ Ϧ */
0x03e9, 499, /* ϩ Ϩ */
0x03eb, 499, /* ϫ Ϫ */
0x03ed, 499, /* ϭ Ϭ */
0x03ef, 499, /* ϯ Ϯ */
0x03f0, 414, /* ϰ Κ */
0x03f1, 420, /* ϱ Ρ */
0x0461, 499, /* ѡ Ѡ */
0x0463, 499, /* ѣ Ѣ */
0x0465, 499, /* ѥ Ѥ */
0x0467, 499, /* ѧ Ѧ */
0x0469, 499, /* ѩ Ѩ */
0x046b, 499, /* ѫ Ѫ */
0x046d, 499, /* ѭ Ѭ */
0x046f, 499, /* ѯ Ѯ */
0x0471, 499, /* ѱ Ѱ */
0x0473, 499, /* ѳ Ѳ */
0x0475, 499, /* ѵ Ѵ */
0x0477, 499, /* ѷ Ѷ */
0x0479, 499, /* ѹ Ѹ */
0x047b, 499, /* ѻ Ѻ */
0x047d, 499, /* ѽ Ѽ */
0x047f, 499, /* ѿ Ѿ */
0x0481, 499, /* ҁ Ҁ */
0x0491, 499, /* ґ Ґ */
0x0493, 499, /* ғ Ғ */
0x0495, 499, /* ҕ Ҕ */
0x0497, 499, /* җ Җ */
0x0499, 499, /* ҙ Ҙ */
0x049b, 499, /* қ Қ */
0x049d, 499, /* ҝ Ҝ */
0x049f, 499, /* ҟ Ҟ */
0x04a1, 499, /* ҡ Ҡ */
0x04a3, 499, /* ң Ң */
0x04a5, 499, /* ҥ Ҥ */
0x04a7, 499, /* ҧ Ҧ */
0x04a9, 499, /* ҩ Ҩ */
0x04ab, 499, /* ҫ Ҫ */
0x04ad, 499, /* ҭ Ҭ */
0x04af, 499, /* ү Ү */
0x04b1, 499, /* ұ Ұ */
0x04b3, 499, /* ҳ Ҳ */
0x04b5, 499, /* ҵ Ҵ */
0x04b7, 499, /* ҷ Ҷ */
0x04b9, 499, /* ҹ Ҹ */
0x04bb, 499, /* һ Һ */
0x04bd, 499, /* ҽ Ҽ */
0x04bf, 499, /* ҿ Ҿ */
0x04c2, 499, /* ӂ Ӂ */
0x04c4, 499, /* ӄ Ӄ */
0x04c8, 499, /* ӈ Ӈ */
0x04cc, 499, /* ӌ Ӌ */
0x04d1, 499, /* ӑ Ӑ */
0x04d3, 499, /* ӓ Ӓ */
0x04d5, 499, /* ӕ Ӕ */
0x04d7, 499, /* ӗ Ӗ */
0x04d9, 499, /* ә Ә */
0x04db, 499, /* ӛ Ӛ */
0x04dd, 499, /* ӝ Ӝ */
0x04df, 499, /* ӟ Ӟ */
0x04e1, 499, /* ӡ Ӡ */
0x04e3, 499, /* ӣ Ӣ */
0x04e5, 499, /* ӥ Ӥ */
0x04e7, 499, /* ӧ Ӧ */
0x04e9, 499, /* ө Ө */
0x04eb, 499, /* ӫ Ӫ */
0x04ef, 499, /* ӯ Ӯ */
0x04f1, 499, /* ӱ Ӱ */
0x04f3, 499, /* ӳ Ӳ */
0x04f5, 499, /* ӵ Ӵ */
0x04f9, 499, /* ӹ Ӹ */
0x1e01, 499, /* ḁ Ḁ */
0x1e03, 499, /* ḃ Ḃ */
0x1e05, 499, /* ḅ Ḅ */
0x1e07, 499, /* ḇ Ḇ */
0x1e09, 499, /* ḉ Ḉ */
0x1e0b, 499, /* ḋ Ḋ */
0x1e0d, 499, /* ḍ Ḍ */
0x1e0f, 499, /* ḏ Ḏ */
0x1e11, 499, /* ḑ Ḑ */
0x1e13, 499, /* ḓ Ḓ */
0x1e15, 499, /* ḕ Ḕ */
0x1e17, 499, /* ḗ Ḗ */
0x1e19, 499, /* ḙ Ḙ */
0x1e1b, 499, /* ḛ Ḛ */
0x1e1d, 499, /* ḝ Ḝ */
0x1e1f, 499, /* ḟ Ḟ */
0x1e21, 499, /* ḡ Ḡ */
0x1e23, 499, /* ḣ Ḣ */
0x1e25, 499, /* ḥ Ḥ */
0x1e27, 499, /* ḧ Ḧ */
0x1e29, 499, /* ḩ Ḩ */
0x1e2b, 499, /* ḫ Ḫ */
0x1e2d, 499, /* ḭ Ḭ */
0x1e2f, 499, /* ḯ Ḯ */
0x1e31, 499, /* ḱ Ḱ */
0x1e33, 499, /* ḳ Ḳ */
0x1e35, 499, /* ḵ Ḵ */
0x1e37, 499, /* ḷ Ḷ */
0x1e39, 499, /* ḹ Ḹ */
0x1e3b, 499, /* ḻ Ḻ */
0x1e3d, 499, /* ḽ Ḽ */
0x1e3f, 499, /* ḿ Ḿ */
0x1e41, 499, /* ṁ Ṁ */
0x1e43, 499, /* ṃ Ṃ */
0x1e45, 499, /* ṅ Ṅ */
0x1e47, 499, /* ṇ Ṇ */
0x1e49, 499, /* ṉ Ṉ */
0x1e4b, 499, /* ṋ Ṋ */
0x1e4d, 499, /* ṍ Ṍ */
0x1e4f, 499, /* ṏ Ṏ */
0x1e51, 499, /* ṑ Ṑ */
0x1e53, 499, /* ṓ Ṓ */
0x1e55, 499, /* ṕ Ṕ */
0x1e57, 499, /* ṗ Ṗ */
0x1e59, 499, /* ṙ Ṙ */
0x1e5b, 499, /* ṛ Ṛ */
0x1e5d, 499, /* ṝ Ṝ */
0x1e5f, 499, /* ṟ Ṟ */
0x1e61, 499, /* ṡ Ṡ */
0x1e63, 499, /* ṣ Ṣ */
0x1e65, 499, /* ṥ Ṥ */
0x1e67, 499, /* ṧ Ṧ */
0x1e69, 499, /* ṩ Ṩ */
0x1e6b, 499, /* ṫ Ṫ */
0x1e6d, 499, /* ṭ Ṭ */
0x1e6f, 499, /* ṯ Ṯ */
0x1e71, 499, /* ṱ Ṱ */
0x1e73, 499, /* ṳ Ṳ */
0x1e75, 499, /* ṵ Ṵ */
0x1e77, 499, /* ṷ Ṷ */
0x1e79, 499, /* ṹ Ṹ */
0x1e7b, 499, /* ṻ Ṻ */
0x1e7d, 499, /* ṽ Ṽ */
0x1e7f, 499, /* ṿ Ṿ */
0x1e81, 499, /* ẁ Ẁ */
0x1e83, 499, /* ẃ Ẃ */
0x1e85, 499, /* ẅ Ẅ */
0x1e87, 499, /* ẇ Ẇ */
0x1e89, 499, /* ẉ Ẉ */
0x1e8b, 499, /* ẋ Ẋ */
0x1e8d, 499, /* ẍ Ẍ */
0x1e8f, 499, /* ẏ Ẏ */
0x1e91, 499, /* ẑ Ẑ */
0x1e93, 499, /* ẓ Ẓ */
0x1e95, 499, /* ẕ Ẕ */
0x1ea1, 499, /* ạ Ạ */
0x1ea3, 499, /* ả Ả */
0x1ea5, 499, /* ấ Ấ */
0x1ea7, 499, /* ầ Ầ */
0x1ea9, 499, /* ẩ Ẩ */
0x1eab, 499, /* ẫ Ẫ */
0x1ead, 499, /* ậ Ậ */
0x1eaf, 499, /* ắ Ắ */
0x1eb1, 499, /* ằ Ằ */
0x1eb3, 499, /* ẳ Ẳ */
0x1eb5, 499, /* ẵ Ẵ */
0x1eb7, 499, /* ặ Ặ */
0x1eb9, 499, /* ẹ Ẹ */
0x1ebb, 499, /* ẻ Ẻ */
0x1ebd, 499, /* ẽ Ẽ */
0x1ebf, 499, /* ế Ế */
0x1ec1, 499, /* ề Ề */
0x1ec3, 499, /* ể Ể */
0x1ec5, 499, /* ễ Ễ */
0x1ec7, 499, /* ệ Ệ */
0x1ec9, 499, /* ỉ Ỉ */
0x1ecb, 499, /* ị Ị */
0x1ecd, 499, /* ọ Ọ */
0x1ecf, 499, /* ỏ Ỏ */
0x1ed1, 499, /* ố Ố */
0x1ed3, 499, /* ồ Ồ */
0x1ed5, 499, /* ổ Ổ */
0x1ed7, 499, /* ỗ Ỗ */
0x1ed9, 499, /* ộ Ộ */
0x1edb, 499, /* ớ Ớ */
0x1edd, 499, /* ờ Ờ */
0x1edf, 499, /* ở Ở */
0x1ee1, 499, /* ỡ Ỡ */
0x1ee3, 499, /* ợ Ợ */
0x1ee5, 499, /* ụ Ụ */
0x1ee7, 499, /* ủ Ủ */
0x1ee9, 499, /* ứ Ứ */
0x1eeb, 499, /* ừ Ừ */
0x1eed, 499, /* ử Ử */
0x1eef, 499, /* ữ Ữ */
0x1ef1, 499, /* ự Ự */
0x1ef3, 499, /* ỳ Ỳ */
0x1ef5, 499, /* ỵ Ỵ */
0x1ef7, 499, /* ỷ Ỷ */
0x1ef9, 499, /* ỹ Ỹ */
0x1f51, 508, /* ὑ Ὑ */
0x1f53, 508, /* ὓ Ὓ */
0x1f55, 508, /* ὕ Ὕ */
0x1f57, 508, /* ὗ Ὗ */
0x1fb3, 509, /* ᾳ ᾼ */
0x1fc3, 509, /* ῃ ῌ */
0x1fe5, 507, /* ῥ Ῥ */
0x1ff3, 509, /* ῳ ῼ */
};
/*
* upper case ranges
* 3rd col is conversion excess 500
*/
static Rune __tolower2[] = {
0x0041, 0x005a, 532, /* A-Z a-z */
0x00c0, 0x00d6, 532, /* À-Ö à-ö */
0x00d8, 0x00de, 532, /* Ø-Þ ø-þ */
0x0189, 0x018a, 705, /* Ɖ-Ɗ ɖ-ɗ */
0x018e, 0x018f, 702, /* Ǝ-Ə ɘ-ə */
0x01b1, 0x01b2, 717, /* Ʊ-Ʋ ʊ-ʋ */
0x0388, 0x038a, 537, /* Έ-Ί έ-ί */
0x038e, 0x038f, 563, /* Ύ-Ώ ύ-ώ */
0x0391, 0x03a1, 532, /* Α-Ρ α-ρ */
0x03a3, 0x03ab, 532, /* Σ-Ϋ σ-ϋ */
0x0401, 0x040c, 580, /* Ё-Ќ ё-ќ */
0x040e, 0x040f, 580, /* Ў-Џ ў-џ */
0x0410, 0x042f, 532, /* А-Я а-я */
0x0531, 0x0556, 548, /* Ա-Ֆ ա-ֆ */
0x10a0, 0x10c5, 548, /* Ⴀ-Ⴥ ა-ჵ */
0x1f08, 0x1f0f, 492, /* Ἀ-Ἇ ἀ-ἇ */
0x1f18, 0x1f1d, 492, /* Ἐ-Ἕ ἐ-ἕ */
0x1f28, 0x1f2f, 492, /* Ἠ-Ἧ ἠ-ἧ */
0x1f38, 0x1f3f, 492, /* Ἰ-Ἷ ἰ-ἷ */
0x1f48, 0x1f4d, 492, /* Ὀ-Ὅ ὀ-ὅ */
0x1f68, 0x1f6f, 492, /* Ὠ-Ὧ ὠ-ὧ */
0x1f88, 0x1f8f, 492, /* ᾈ-ᾏ ᾀ-ᾇ */
0x1f98, 0x1f9f, 492, /* ᾘ-ᾟ ᾐ-ᾗ */
0x1fa8, 0x1faf, 492, /* ᾨ-ᾯ ᾠ-ᾧ */
0x1fb8, 0x1fb9, 492, /* Ᾰ-Ᾱ ᾰ-ᾱ */
0x1fba, 0x1fbb, 426, /* Ὰ-Ά ὰ-ά */
0x1fc8, 0x1fcb, 414, /* Ὲ-Ή ὲ-ή */
0x1fd8, 0x1fd9, 492, /* Ῐ-Ῑ ῐ-ῑ */
0x1fda, 0x1fdb, 400, /* Ὶ-Ί ὶ-ί */
0x1fe8, 0x1fe9, 492, /* Ῠ-Ῡ ῠ-ῡ */
0x1fea, 0x1feb, 388, /* Ὺ-Ύ ὺ-ύ */
0x1ff8, 0x1ff9, 372, /* Ὸ-Ό ὸ-ό */
0x1ffa, 0x1ffb, 374, /* Ὼ-Ώ ὼ-ώ */
0x2160, 0x216f, 516, /* Ⅰ-Ⅿ ⅰ-ⅿ */
0x24b6, 0x24cf, 526, /* Ⓐ-Ⓩ ⓐ-ⓩ */
0xff21, 0xff3a, 532, /* A-Z a-z */
};
/*
* upper case singlets
* 2nd col is conversion excess 500
*/
static Rune __tolower1[] = {
0x0100, 501, /* Ā ā */
0x0102, 501, /* Ă ă */
0x0104, 501, /* Ą ą */
0x0106, 501, /* Ć ć */
0x0108, 501, /* Ĉ ĉ */
0x010a, 501, /* Ċ ċ */
0x010c, 501, /* Č č */
0x010e, 501, /* Ď ď */
0x0110, 501, /* Đ đ */
0x0112, 501, /* Ē ē */
0x0114, 501, /* Ĕ ĕ */
0x0116, 501, /* Ė ė */
0x0118, 501, /* Ę ę */
0x011a, 501, /* Ě ě */
0x011c, 501, /* Ĝ ĝ */
0x011e, 501, /* Ğ ğ */
0x0120, 501, /* Ġ ġ */
0x0122, 501, /* Ģ ģ */
0x0124, 501, /* Ĥ ĥ */
0x0126, 501, /* Ħ ħ */
0x0128, 501, /* Ĩ ĩ */
0x012a, 501, /* Ī ī */
0x012c, 501, /* Ĭ ĭ */
0x012e, 501, /* Į į */
0x0130, 301, /* İ i */
0x0132, 501, /* IJ ij */
0x0134, 501, /* Ĵ ĵ */
0x0136, 501, /* Ķ ķ */
0x0139, 501, /* Ĺ ĺ */
0x013b, 501, /* Ļ ļ */
0x013d, 501, /* Ľ ľ */
0x013f, 501, /* Ŀ ŀ */
0x0141, 501, /* Ł ł */
0x0143, 501, /* Ń ń */
0x0145, 501, /* Ņ ņ */
0x0147, 501, /* Ň ň */
0x014a, 501, /* Ŋ ŋ */
0x014c, 501, /* Ō ō */
0x014e, 501, /* Ŏ ŏ */
0x0150, 501, /* Ő ő */
0x0152, 501, /* Œ œ */
0x0154, 501, /* Ŕ ŕ */
0x0156, 501, /* Ŗ ŗ */
0x0158, 501, /* Ř ř */
0x015a, 501, /* Ś ś */
0x015c, 501, /* Ŝ ŝ */
0x015e, 501, /* Ş ş */
0x0160, 501, /* Š š */
0x0162, 501, /* Ţ ţ */
0x0164, 501, /* Ť ť */
0x0166, 501, /* Ŧ ŧ */
0x0168, 501, /* Ũ ũ */
0x016a, 501, /* Ū ū */
0x016c, 501, /* Ŭ ŭ */
0x016e, 501, /* Ů ů */
0x0170, 501, /* Ű ű */
0x0172, 501, /* Ų ų */
0x0174, 501, /* Ŵ ŵ */
0x0176, 501, /* Ŷ ŷ */
0x0178, 379, /* Ÿ ÿ */
0x0179, 501, /* Ź ź */
0x017b, 501, /* Ż ż */
0x017d, 501, /* Ž ž */
0x0181, 710, /* Ɓ ɓ */
0x0182, 501, /* Ƃ ƃ */
0x0184, 501, /* Ƅ ƅ */
0x0186, 706, /* Ɔ ɔ */
0x0187, 501, /* Ƈ ƈ */
0x018b, 501, /* Ƌ ƌ */
0x0190, 703, /* Ɛ ɛ */
0x0191, 501, /* Ƒ ƒ */
0x0193, 705, /* Ɠ ɠ */
0x0194, 707, /* Ɣ ɣ */
0x0196, 711, /* Ɩ ɩ */
0x0197, 709, /* Ɨ ɨ */
0x0198, 501, /* Ƙ ƙ */
0x019c, 711, /* Ɯ ɯ */
0x019d, 713, /* Ɲ ɲ */
0x01a0, 501, /* Ơ ơ */
0x01a2, 501, /* Ƣ ƣ */
0x01a4, 501, /* Ƥ ƥ */
0x01a7, 501, /* Ƨ ƨ */
0x01a9, 718, /* Ʃ ʃ */
0x01ac, 501, /* Ƭ ƭ */
0x01ae, 718, /* Ʈ ʈ */
0x01af, 501, /* Ư ư */
0x01b3, 501, /* Ƴ ƴ */
0x01b5, 501, /* Ƶ ƶ */
0x01b7, 719, /* Ʒ ʒ */
0x01b8, 501, /* Ƹ ƹ */
0x01bc, 501, /* Ƽ ƽ */
0x01c4, 502, /* DŽ dž */
0x01c5, 501, /* Dž dž */
0x01c7, 502, /* LJ lj */
0x01c8, 501, /* Lj lj */
0x01ca, 502, /* NJ nj */
0x01cb, 501, /* Nj nj */
0x01cd, 501, /* Ǎ ǎ */
0x01cf, 501, /* Ǐ ǐ */
0x01d1, 501, /* Ǒ ǒ */
0x01d3, 501, /* Ǔ ǔ */
0x01d5, 501, /* Ǖ ǖ */
0x01d7, 501, /* Ǘ ǘ */
0x01d9, 501, /* Ǚ ǚ */
0x01db, 501, /* Ǜ ǜ */
0x01de, 501, /* Ǟ ǟ */
0x01e0, 501, /* Ǡ ǡ */
0x01e2, 501, /* Ǣ ǣ */
0x01e4, 501, /* Ǥ ǥ */
0x01e6, 501, /* Ǧ ǧ */
0x01e8, 501, /* Ǩ ǩ */
0x01ea, 501, /* Ǫ ǫ */
0x01ec, 501, /* Ǭ ǭ */
0x01ee, 501, /* Ǯ ǯ */
0x01f1, 502, /* DZ dz */
0x01f2, 501, /* Dz dz */
0x01f4, 501, /* Ǵ ǵ */
0x01fa, 501, /* Ǻ ǻ */
0x01fc, 501, /* Ǽ ǽ */
0x01fe, 501, /* Ǿ ǿ */
0x0200, 501, /* Ȁ ȁ */
0x0202, 501, /* Ȃ ȃ */
0x0204, 501, /* Ȅ ȅ */
0x0206, 501, /* Ȇ ȇ */
0x0208, 501, /* Ȉ ȉ */
0x020a, 501, /* Ȋ ȋ */
0x020c, 501, /* Ȍ ȍ */
0x020e, 501, /* Ȏ ȏ */
0x0210, 501, /* Ȑ ȑ */
0x0212, 501, /* Ȓ ȓ */
0x0214, 501, /* Ȕ ȕ */
0x0216, 501, /* Ȗ ȗ */
0x0386, 538, /* Ά ά */
0x038c, 564, /* Ό ό */
0x03e2, 501, /* Ϣ ϣ */
0x03e4, 501, /* Ϥ ϥ */
0x03e6, 501, /* Ϧ ϧ */
0x03e8, 501, /* Ϩ ϩ */
0x03ea, 501, /* Ϫ ϫ */
0x03ec, 501, /* Ϭ ϭ */
0x03ee, 501, /* Ϯ ϯ */
0x0460, 501, /* Ѡ ѡ */
0x0462, 501, /* Ѣ ѣ */
0x0464, 501, /* Ѥ ѥ */
0x0466, 501, /* Ѧ ѧ */
0x0468, 501, /* Ѩ ѩ */
0x046a, 501, /* Ѫ ѫ */
0x046c, 501, /* Ѭ ѭ */
0x046e, 501, /* Ѯ ѯ */
0x0470, 501, /* Ѱ ѱ */
0x0472, 501, /* Ѳ ѳ */
0x0474, 501, /* Ѵ ѵ */
0x0476, 501, /* Ѷ ѷ */
0x0478, 501, /* Ѹ ѹ */
0x047a, 501, /* Ѻ ѻ */
0x047c, 501, /* Ѽ ѽ */
0x047e, 501, /* Ѿ ѿ */
0x0480, 501, /* Ҁ ҁ */
0x0490, 501, /* Ґ ґ */
0x0492, 501, /* Ғ ғ */
0x0494, 501, /* Ҕ ҕ */
0x0496, 501, /* Җ җ */
0x0498, 501, /* Ҙ ҙ */
0x049a, 501, /* Қ қ */
0x049c, 501, /* Ҝ ҝ */
0x049e, 501, /* Ҟ ҟ */
0x04a0, 501, /* Ҡ ҡ */
0x04a2, 501, /* Ң ң */
0x04a4, 501, /* Ҥ ҥ */
0x04a6, 501, /* Ҧ ҧ */
0x04a8, 501, /* Ҩ ҩ */
0x04aa, 501, /* Ҫ ҫ */
0x04ac, 501, /* Ҭ ҭ */
0x04ae, 501, /* Ү ү */
0x04b0, 501, /* Ұ ұ */
0x04b2, 501, /* Ҳ ҳ */
0x04b4, 501, /* Ҵ ҵ */
0x04b6, 501, /* Ҷ ҷ */
0x04b8, 501, /* Ҹ ҹ */
0x04ba, 501, /* Һ һ */
0x04bc, 501, /* Ҽ ҽ */
0x04be, 501, /* Ҿ ҿ */
0x04c1, 501, /* Ӂ ӂ */
0x04c3, 501, /* Ӄ ӄ */
0x04c7, 501, /* Ӈ ӈ */
0x04cb, 501, /* Ӌ ӌ */
0x04d0, 501, /* Ӑ ӑ */
0x04d2, 501, /* Ӓ ӓ */
0x04d4, 501, /* Ӕ ӕ */
0x04d6, 501, /* Ӗ ӗ */
0x04d8, 501, /* Ә ә */
0x04da, 501, /* Ӛ ӛ */
0x04dc, 501, /* Ӝ ӝ */
0x04de, 501, /* Ӟ ӟ */
0x04e0, 501, /* Ӡ ӡ */
0x04e2, 501, /* Ӣ ӣ */
0x04e4, 501, /* Ӥ ӥ */
0x04e6, 501, /* Ӧ ӧ */
0x04e8, 501, /* Ө ө */
0x04ea, 501, /* Ӫ ӫ */
0x04ee, 501, /* Ӯ ӯ */
0x04f0, 501, /* Ӱ ӱ */
0x04f2, 501, /* Ӳ ӳ */
0x04f4, 501, /* Ӵ ӵ */
0x04f8, 501, /* Ӹ ӹ */
0x1e00, 501, /* Ḁ ḁ */
0x1e02, 501, /* Ḃ ḃ */
0x1e04, 501, /* Ḅ ḅ */
0x1e06, 501, /* Ḇ ḇ */
0x1e08, 501, /* Ḉ ḉ */
0x1e0a, 501, /* Ḋ ḋ */
0x1e0c, 501, /* Ḍ ḍ */
0x1e0e, 501, /* Ḏ ḏ */
0x1e10, 501, /* Ḑ ḑ */
0x1e12, 501, /* Ḓ ḓ */
0x1e14, 501, /* Ḕ ḕ */
0x1e16, 501, /* Ḗ ḗ */
0x1e18, 501, /* Ḙ ḙ */
0x1e1a, 501, /* Ḛ ḛ */
0x1e1c, 501, /* Ḝ ḝ */
0x1e1e, 501, /* Ḟ ḟ */
0x1e20, 501, /* Ḡ ḡ */
0x1e22, 501, /* Ḣ ḣ */
0x1e24, 501, /* Ḥ ḥ */
0x1e26, 501, /* Ḧ ḧ */
0x1e28, 501, /* Ḩ ḩ */
0x1e2a, 501, /* Ḫ ḫ */
0x1e2c, 501, /* Ḭ ḭ */
0x1e2e, 501, /* Ḯ ḯ */
0x1e30, 501, /* Ḱ ḱ */
0x1e32, 501, /* Ḳ ḳ */
0x1e34, 501, /* Ḵ ḵ */
0x1e36, 501, /* Ḷ ḷ */
0x1e38, 501, /* Ḹ ḹ */
0x1e3a, 501, /* Ḻ ḻ */
0x1e3c, 501, /* Ḽ ḽ */
0x1e3e, 501, /* Ḿ ḿ */
0x1e40, 501, /* Ṁ ṁ */
0x1e42, 501, /* Ṃ ṃ */
0x1e44, 501, /* Ṅ ṅ */
0x1e46, 501, /* Ṇ ṇ */
0x1e48, 501, /* Ṉ ṉ */
0x1e4a, 501, /* Ṋ ṋ */
0x1e4c, 501, /* Ṍ ṍ */
0x1e4e, 501, /* Ṏ ṏ */
0x1e50, 501, /* Ṑ ṑ */
0x1e52, 501, /* Ṓ ṓ */
0x1e54, 501, /* Ṕ ṕ */
0x1e56, 501, /* Ṗ ṗ */
0x1e58, 501, /* Ṙ ṙ */
0x1e5a, 501, /* Ṛ ṛ */
0x1e5c, 501, /* Ṝ ṝ */
0x1e5e, 501, /* Ṟ ṟ */
0x1e60, 501, /* Ṡ ṡ */
0x1e62, 501, /* Ṣ ṣ */
0x1e64, 501, /* Ṥ ṥ */
0x1e66, 501, /* Ṧ ṧ */
0x1e68, 501, /* Ṩ ṩ */
0x1e6a, 501, /* Ṫ ṫ */
0x1e6c, 501, /* Ṭ ṭ */
0x1e6e, 501, /* Ṯ ṯ */
0x1e70, 501, /* Ṱ ṱ */
0x1e72, 501, /* Ṳ ṳ */
0x1e74, 501, /* Ṵ ṵ */
0x1e76, 501, /* Ṷ ṷ */
0x1e78, 501, /* Ṹ ṹ */
0x1e7a, 501, /* Ṻ ṻ */
0x1e7c, 501, /* Ṽ ṽ */
0x1e7e, 501, /* Ṿ ṿ */
0x1e80, 501, /* Ẁ ẁ */
0x1e82, 501, /* Ẃ ẃ */
0x1e84, 501, /* Ẅ ẅ */
0x1e86, 501, /* Ẇ ẇ */
0x1e88, 501, /* Ẉ ẉ */
0x1e8a, 501, /* Ẋ ẋ */
0x1e8c, 501, /* Ẍ ẍ */
0x1e8e, 501, /* Ẏ ẏ */
0x1e90, 501, /* Ẑ ẑ */
0x1e92, 501, /* Ẓ ẓ */
0x1e94, 501, /* Ẕ ẕ */
0x1ea0, 501, /* Ạ ạ */
0x1ea2, 501, /* Ả ả */
0x1ea4, 501, /* Ấ ấ */
0x1ea6, 501, /* Ầ ầ */
0x1ea8, 501, /* Ẩ ẩ */
0x1eaa, 501, /* Ẫ ẫ */
0x1eac, 501, /* Ậ ậ */
0x1eae, 501, /* Ắ ắ */
0x1eb0, 501, /* Ằ ằ */
0x1eb2, 501, /* Ẳ ẳ */
0x1eb4, 501, /* Ẵ ẵ */
0x1eb6, 501, /* Ặ ặ */
0x1eb8, 501, /* Ẹ ẹ */
0x1eba, 501, /* Ẻ ẻ */
0x1ebc, 501, /* Ẽ ẽ */
0x1ebe, 501, /* Ế ế */
0x1ec0, 501, /* Ề ề */
0x1ec2, 501, /* Ể ể */
0x1ec4, 501, /* Ễ ễ */
0x1ec6, 501, /* Ệ ệ */
0x1ec8, 501, /* Ỉ ỉ */
0x1eca, 501, /* Ị ị */
0x1ecc, 501, /* Ọ ọ */
0x1ece, 501, /* Ỏ ỏ */
0x1ed0, 501, /* Ố ố */
0x1ed2, 501, /* Ồ ồ */
0x1ed4, 501, /* Ổ ổ */
0x1ed6, 501, /* Ỗ ỗ */
0x1ed8, 501, /* Ộ ộ */
0x1eda, 501, /* Ớ ớ */
0x1edc, 501, /* Ờ ờ */
0x1ede, 501, /* Ở ở */
0x1ee0, 501, /* Ỡ ỡ */
0x1ee2, 501, /* Ợ ợ */
0x1ee4, 501, /* Ụ ụ */
0x1ee6, 501, /* Ủ ủ */
0x1ee8, 501, /* Ứ ứ */
0x1eea, 501, /* Ừ ừ */
0x1eec, 501, /* Ử ử */
0x1eee, 501, /* Ữ ữ */
0x1ef0, 501, /* Ự ự */
0x1ef2, 501, /* Ỳ ỳ */
0x1ef4, 501, /* Ỵ ỵ */
0x1ef6, 501, /* Ỷ ỷ */
0x1ef8, 501, /* Ỹ ỹ */
0x1f59, 492, /* Ὑ ὑ */
0x1f5b, 492, /* Ὓ ὓ */
0x1f5d, 492, /* Ὕ ὕ */
0x1f5f, 492, /* Ὗ ὗ */
0x1fbc, 491, /* ᾼ ᾳ */
0x1fcc, 491, /* ῌ ῃ */
0x1fec, 493, /* Ῥ ῥ */
0x1ffc, 491, /* ῼ ῳ */
};
static Rune *rune_bsearch(Rune c, Rune *t, int n, int ne) {
Rune *p;
int m;
while (n > 1) {
m = n / 2;
p = t + m * ne;
if (c >= p[0]) {
t = p;
n = n - m;
} else
n = m;
}
if (n && c >= t[0]) return t;
return 0;
}
Rune tolowerrune(Rune c) {
Rune *p;
p = rune_bsearch(c, __tolower2, nelem(__tolower2) / 3, 3);
if (p && c >= p[0] && c <= p[1]) return c + p[2] - 500;
p = rune_bsearch(c, __tolower1, nelem(__tolower1) / 2, 2);
if (p && c == p[0]) return c + p[1] - 500;
return c;
}
Rune toupperrune(Rune c) {
Rune *p;
p = rune_bsearch(c, __toupper2, nelem(__toupper2) / 3, 3);
if (p && c >= p[0] && c <= p[1]) return c + p[2] - 500;
p = rune_bsearch(c, __toupper1, nelem(__toupper1) / 2, 2);
if (p && c == p[0]) return c + p[1] - 500;
return c;
}
int islowerrune(Rune c) {
Rune *p;
p = rune_bsearch(c, __toupper2, nelem(__toupper2) / 3, 3);
if (p && c >= p[0] && c <= p[1]) return 1;
p = rune_bsearch(c, __toupper1, nelem(__toupper1) / 2, 2);
if (p && c == p[0]) return 1;
return 0;
}
int isupperrune(Rune c) {
Rune *p;
p = rune_bsearch(c, __tolower2, nelem(__tolower2) / 3, 3);
if (p && c >= p[0] && c <= p[1]) return 1;
p = rune_bsearch(c, __tolower1, nelem(__tolower1) / 2, 2);
if (p && c == p[0]) return 1;
return 0;
}
int isdigitrune(Rune c) {
return c >= '0' && c <= '9';
}
int isnewline(Rune c) {
return c == 0xA || c == 0xD || c == 0x2028 || c == 0x2029;
}
int iswordchar(Rune c) {
return c == '_' || isdigitrune(c) || (c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z');
}
int isalpharune(Rune c) {
Rune *p;
if (isupperrune(c) || islowerrune(c)) return 1;
p = rune_bsearch(c, __alpha2, nelem(__alpha2) / 2, 2);
if (p && c >= p[0] && c <= p[1]) return 1;
p = rune_bsearch(c, __alpha1, nelem(__alpha1), 1);
if (p && c == p[0]) return 1;
return 0;
}
int isspacerune(Rune c) {
Rune *p;
p = rune_bsearch(c, __space2, nelem(__space2) / 2, 2);
if (p && c >= p[0] && c <= p[1]) return 1;
return 0;
}
#else /* CS_ENABLE_UTF8 */
int chartorune(Rune *rune, const char *str) {
*rune = *(uchar *) str;
return 1;
}
int fullrune(const char *str, int n) {
(void) str;
return (n <= 0) ? 0 : 1;
}
int isdigitrune(Rune c) {
return isdigit(c);
}
int isnewline(Rune c) {
return c == 0xA || c == 0xD || c == 0x2028 || c == 0x2029;
}
int iswordchar(Rune c) {
return c == '_' || isdigitrune(c) || (c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z');
}
int isalpharune(Rune c) {
return isalpha(c);
}
int islowerrune(Rune c) {
return islower(c);
}
int isspacerune(Rune c) {
return isspace(c);
}
int isupperrune(Rune c) {
return isupper(c);
}
int runetochar(char *str, Rune *rune) {
str[0] = (char) *rune;
return 1;
}
Rune tolowerrune(Rune c) {
return tolower(c);
}
Rune toupperrune(Rune c) {
return toupper(c);
}
int utfnlen(const char *s, long m) {
(void) s;
return (int) c_strnlen(s, (size_t) m);
}
const char *utfnshift(const char *s, long m) {
return s + m;
}
#endif /* CS_ENABLE_UTF8 */
#endif /* EXCLUDE_COMMON */
#ifdef V7_MODULE_LINES
#line 1 "common/base64.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef EXCLUDE_COMMON
/* Amalgamated: #include "common/base64.h" */
#include
/* Amalgamated: #include "common/cs_dbg.h" */
/* ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ */
#define NUM_UPPERCASES ('Z' - 'A' + 1)
#define NUM_LETTERS (NUM_UPPERCASES * 2)
#define NUM_DIGITS ('9' - '0' + 1)
/*
* Emit a base64 code char.
*
* Doesn't use memory, thus it's safe to use to safely dump memory in crashdumps
*/
static void cs_base64_emit_code(struct cs_base64_ctx *ctx, int v) {
if (v < NUM_UPPERCASES) {
ctx->b64_putc(v + 'A', ctx->user_data);
} else if (v < (NUM_LETTERS)) {
ctx->b64_putc(v - NUM_UPPERCASES + 'a', ctx->user_data);
} else if (v < (NUM_LETTERS + NUM_DIGITS)) {
ctx->b64_putc(v - NUM_LETTERS + '0', ctx->user_data);
} else {
ctx->b64_putc(v - NUM_LETTERS - NUM_DIGITS == 0 ? '+' : '/',
ctx->user_data);
}
}
static void cs_base64_emit_chunk(struct cs_base64_ctx *ctx) {
int a, b, c;
a = ctx->chunk[0];
b = ctx->chunk[1];
c = ctx->chunk[2];
cs_base64_emit_code(ctx, a >> 2);
cs_base64_emit_code(ctx, ((a & 3) << 4) | (b >> 4));
if (ctx->chunk_size > 1) {
cs_base64_emit_code(ctx, (b & 15) << 2 | (c >> 6));
}
if (ctx->chunk_size > 2) {
cs_base64_emit_code(ctx, c & 63);
}
}
void cs_base64_init(struct cs_base64_ctx *ctx, cs_base64_putc_t b64_putc,
void *user_data) {
ctx->chunk_size = 0;
ctx->b64_putc = b64_putc;
ctx->user_data = user_data;
}
void cs_base64_update(struct cs_base64_ctx *ctx, const char *str, size_t len) {
const unsigned char *src = (const unsigned char *) str;
size_t i;
for (i = 0; i < len; i++) {
ctx->chunk[ctx->chunk_size++] = src[i];
if (ctx->chunk_size == 3) {
cs_base64_emit_chunk(ctx);
ctx->chunk_size = 0;
}
}
}
void cs_base64_finish(struct cs_base64_ctx *ctx) {
if (ctx->chunk_size > 0) {
int i;
memset(&ctx->chunk[ctx->chunk_size], 0, 3 - ctx->chunk_size);
cs_base64_emit_chunk(ctx);
for (i = 0; i < (3 - ctx->chunk_size); i++) {
ctx->b64_putc('=', ctx->user_data);
}
}
}
#define BASE64_ENCODE_BODY \
static const char *b64 = \
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; \
int i, j, a, b, c; \
\
for (i = j = 0; i < src_len; i += 3) { \
a = src[i]; \
b = i + 1 >= src_len ? 0 : src[i + 1]; \
c = i + 2 >= src_len ? 0 : src[i + 2]; \
\
BASE64_OUT(b64[a >> 2]); \
BASE64_OUT(b64[((a & 3) << 4) | (b >> 4)]); \
if (i + 1 < src_len) { \
BASE64_OUT(b64[(b & 15) << 2 | (c >> 6)]); \
} \
if (i + 2 < src_len) { \
BASE64_OUT(b64[c & 63]); \
} \
} \
\
while (j % 4 != 0) { \
BASE64_OUT('='); \
} \
BASE64_FLUSH()
#define BASE64_OUT(ch) \
do { \
dst[j++] = (ch); \
} while (0)
#define BASE64_FLUSH() \
do { \
dst[j++] = '\0'; \
} while (0)
void cs_base64_encode(const unsigned char *src, int src_len, char *dst) {
BASE64_ENCODE_BODY;
}
#undef BASE64_OUT
#undef BASE64_FLUSH
#if CS_ENABLE_STDIO
#define BASE64_OUT(ch) \
do { \
fprintf(f, "%c", (ch)); \
j++; \
} while (0)
#define BASE64_FLUSH()
void cs_fprint_base64(FILE *f, const unsigned char *src, int src_len) {
BASE64_ENCODE_BODY;
}
#undef BASE64_OUT
#undef BASE64_FLUSH
#endif /* CS_ENABLE_STDIO */
/* Convert one byte of encoded base64 input stream to 6-bit chunk */
static unsigned char from_b64(unsigned char ch) {
/* Inverse lookup map */
static const unsigned char tab[128] = {
255, 255, 255, 255,
255, 255, 255, 255, /* 0 */
255, 255, 255, 255,
255, 255, 255, 255, /* 8 */
255, 255, 255, 255,
255, 255, 255, 255, /* 16 */
255, 255, 255, 255,
255, 255, 255, 255, /* 24 */
255, 255, 255, 255,
255, 255, 255, 255, /* 32 */
255, 255, 255, 62,
255, 255, 255, 63, /* 40 */
52, 53, 54, 55,
56, 57, 58, 59, /* 48 */
60, 61, 255, 255,
255, 200, 255, 255, /* 56 '=' is 200, on index 61 */
255, 0, 1, 2,
3, 4, 5, 6, /* 64 */
7, 8, 9, 10,
11, 12, 13, 14, /* 72 */
15, 16, 17, 18,
19, 20, 21, 22, /* 80 */
23, 24, 25, 255,
255, 255, 255, 255, /* 88 */
255, 26, 27, 28,
29, 30, 31, 32, /* 96 */
33, 34, 35, 36,
37, 38, 39, 40, /* 104 */
41, 42, 43, 44,
45, 46, 47, 48, /* 112 */
49, 50, 51, 255,
255, 255, 255, 255, /* 120 */
};
return tab[ch & 127];
}
int cs_base64_decode(const unsigned char *s, int len, char *dst, int *dec_len) {
unsigned char a, b, c, d;
int orig_len = len;
char *orig_dst = dst;
while (len >= 4 && (a = from_b64(s[0])) != 255 &&
(b = from_b64(s[1])) != 255 && (c = from_b64(s[2])) != 255 &&
(d = from_b64(s[3])) != 255) {
s += 4;
len -= 4;
if (a == 200 || b == 200) break; /* '=' can't be there */
*dst++ = a << 2 | b >> 4;
if (c == 200) break;
*dst++ = b << 4 | c >> 2;
if (d == 200) break;
*dst++ = c << 6 | d;
}
*dst = 0;
if (dec_len != NULL) *dec_len = (dst - orig_dst);
return orig_len - len;
}
#endif /* EXCLUDE_COMMON */
#ifdef V7_MODULE_LINES
#line 1 "common/cs_md5.c"
#endif
/*
* This code implements the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
* written by Colin Plumb in 1993, no copyright is claimed.
* This code is in the public domain; do with it what you wish.
*
* Equivalent code is available from RSA Data Security, Inc.
* This code has been tested against that, and is equivalent,
* except that you don't need to include two pages of legalese
* with every copy.
*
* To compute the message digest of a chunk of bytes, declare an
* MD5Context structure, pass it to MD5Init, call MD5Update as
* needed on buffers full of bytes, and then call MD5Final, which
* will fill a supplied 16-byte array with the digest.
*/
/* Amalgamated: #include "common/cs_md5.h" */
/* Amalgamated: #include "common/str_util.h" */
#if !defined(EXCLUDE_COMMON)
#if !CS_DISABLE_MD5
/* Amalgamated: #include "common/cs_endian.h" */
static void byteReverse(unsigned char *buf, unsigned longs) {
/* Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN */
#if BYTE_ORDER == BIG_ENDIAN
do {
uint32_t t = (uint32_t)((unsigned) buf[3] << 8 | buf[2]) << 16 |
((unsigned) buf[1] << 8 | buf[0]);
*(uint32_t *) buf = t;
buf += 4;
} while (--longs);
#else
(void) buf;
(void) longs;
#endif
}
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
#define MD5STEP(f, w, x, y, z, data, s) \
(w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x)
/*
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
* initialization constants.
*/
void cs_md5_init(cs_md5_ctx *ctx) {
ctx->buf[0] = 0x67452301;
ctx->buf[1] = 0xefcdab89;
ctx->buf[2] = 0x98badcfe;
ctx->buf[3] = 0x10325476;
ctx->bits[0] = 0;
ctx->bits[1] = 0;
}
static void cs_md5_transform(uint32_t buf[4], uint32_t const in[16]) {
register uint32_t a, b, c, d;
a = buf[0];
b = buf[1];
c = buf[2];
d = buf[3];
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
}
void cs_md5_update(cs_md5_ctx *ctx, const unsigned char *buf, size_t len) {
uint32_t t;
t = ctx->bits[0];
if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) ctx->bits[1]++;
ctx->bits[1] += (uint32_t) len >> 29;
t = (t >> 3) & 0x3f;
if (t) {
unsigned char *p = (unsigned char *) ctx->in + t;
t = 64 - t;
if (len < t) {
memcpy(p, buf, len);
return;
}
memcpy(p, buf, t);
byteReverse(ctx->in, 16);
cs_md5_transform(ctx->buf, (uint32_t *) ctx->in);
buf += t;
len -= t;
}
while (len >= 64) {
memcpy(ctx->in, buf, 64);
byteReverse(ctx->in, 16);
cs_md5_transform(ctx->buf, (uint32_t *) ctx->in);
buf += 64;
len -= 64;
}
memcpy(ctx->in, buf, len);
}
void cs_md5_final(unsigned char digest[16], cs_md5_ctx *ctx) {
unsigned count;
unsigned char *p;
uint32_t *a;
count = (ctx->bits[0] >> 3) & 0x3F;
p = ctx->in + count;
*p++ = 0x80;
count = 64 - 1 - count;
if (count < 8) {
memset(p, 0, count);
byteReverse(ctx->in, 16);
cs_md5_transform(ctx->buf, (uint32_t *) ctx->in);
memset(ctx->in, 0, 56);
} else {
memset(p, 0, count - 8);
}
byteReverse(ctx->in, 14);
a = (uint32_t *) ctx->in;
a[14] = ctx->bits[0];
a[15] = ctx->bits[1];
cs_md5_transform(ctx->buf, (uint32_t *) ctx->in);
byteReverse((unsigned char *) ctx->buf, 4);
memcpy(digest, ctx->buf, 16);
memset((char *) ctx, 0, sizeof(*ctx));
}
#endif /* CS_DISABLE_MD5 */
#endif /* EXCLUDE_COMMON */
#ifdef V7_MODULE_LINES
#line 1 "common/cs_sha1.c"
#endif
/* Copyright(c) By Steve Reid */
/* 100% Public Domain */
/* Amalgamated: #include "common/cs_sha1.h" */
#if !CS_DISABLE_SHA1 && !defined(EXCLUDE_COMMON)
/* Amalgamated: #include "common/cs_endian.h" */
#define SHA1HANDSOFF
#if defined(__sun)
/* Amalgamated: #include "common/solarisfixes.h" */
#endif
union char64long16 {
unsigned char c[64];
uint32_t l[16];
};
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
static uint32_t blk0(union char64long16 *block, int i) {
/* Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN */
#if BYTE_ORDER == LITTLE_ENDIAN
block->l[i] =
(rol(block->l[i], 24) & 0xFF00FF00) | (rol(block->l[i], 8) & 0x00FF00FF);
#endif
return block->l[i];
}
/* Avoid redefine warning (ARM /usr/include/sys/ucontext.h define R0~R4) */
#undef blk
#undef R0
#undef R1
#undef R2
#undef R3
#undef R4
#define blk(i) \
(block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] ^ \
block->l[(i + 2) & 15] ^ block->l[i & 15], \
1))
#define R0(v, w, x, y, z, i) \
z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + rol(v, 5); \
w = rol(w, 30);
#define R1(v, w, x, y, z, i) \
z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
w = rol(w, 30);
#define R2(v, w, x, y, z, i) \
z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \
w = rol(w, 30);
#define R3(v, w, x, y, z, i) \
z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
w = rol(w, 30);
#define R4(v, w, x, y, z, i) \
z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
w = rol(w, 30);
void cs_sha1_transform(uint32_t state[5], const unsigned char buffer[64]) {
uint32_t a, b, c, d, e;
union char64long16 block[1];
memcpy(block, buffer, 64);
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
R0(a, b, c, d, e, 0);
R0(e, a, b, c, d, 1);
R0(d, e, a, b, c, 2);
R0(c, d, e, a, b, 3);
R0(b, c, d, e, a, 4);
R0(a, b, c, d, e, 5);
R0(e, a, b, c, d, 6);
R0(d, e, a, b, c, 7);
R0(c, d, e, a, b, 8);
R0(b, c, d, e, a, 9);
R0(a, b, c, d, e, 10);
R0(e, a, b, c, d, 11);
R0(d, e, a, b, c, 12);
R0(c, d, e, a, b, 13);
R0(b, c, d, e, a, 14);
R0(a, b, c, d, e, 15);
R1(e, a, b, c, d, 16);
R1(d, e, a, b, c, 17);
R1(c, d, e, a, b, 18);
R1(b, c, d, e, a, 19);
R2(a, b, c, d, e, 20);
R2(e, a, b, c, d, 21);
R2(d, e, a, b, c, 22);
R2(c, d, e, a, b, 23);
R2(b, c, d, e, a, 24);
R2(a, b, c, d, e, 25);
R2(e, a, b, c, d, 26);
R2(d, e, a, b, c, 27);
R2(c, d, e, a, b, 28);
R2(b, c, d, e, a, 29);
R2(a, b, c, d, e, 30);
R2(e, a, b, c, d, 31);
R2(d, e, a, b, c, 32);
R2(c, d, e, a, b, 33);
R2(b, c, d, e, a, 34);
R2(a, b, c, d, e, 35);
R2(e, a, b, c, d, 36);
R2(d, e, a, b, c, 37);
R2(c, d, e, a, b, 38);
R2(b, c, d, e, a, 39);
R3(a, b, c, d, e, 40);
R3(e, a, b, c, d, 41);
R3(d, e, a, b, c, 42);
R3(c, d, e, a, b, 43);
R3(b, c, d, e, a, 44);
R3(a, b, c, d, e, 45);
R3(e, a, b, c, d, 46);
R3(d, e, a, b, c, 47);
R3(c, d, e, a, b, 48);
R3(b, c, d, e, a, 49);
R3(a, b, c, d, e, 50);
R3(e, a, b, c, d, 51);
R3(d, e, a, b, c, 52);
R3(c, d, e, a, b, 53);
R3(b, c, d, e, a, 54);
R3(a, b, c, d, e, 55);
R3(e, a, b, c, d, 56);
R3(d, e, a, b, c, 57);
R3(c, d, e, a, b, 58);
R3(b, c, d, e, a, 59);
R4(a, b, c, d, e, 60);
R4(e, a, b, c, d, 61);
R4(d, e, a, b, c, 62);
R4(c, d, e, a, b, 63);
R4(b, c, d, e, a, 64);
R4(a, b, c, d, e, 65);
R4(e, a, b, c, d, 66);
R4(d, e, a, b, c, 67);
R4(c, d, e, a, b, 68);
R4(b, c, d, e, a, 69);
R4(a, b, c, d, e, 70);
R4(e, a, b, c, d, 71);
R4(d, e, a, b, c, 72);
R4(c, d, e, a, b, 73);
R4(b, c, d, e, a, 74);
R4(a, b, c, d, e, 75);
R4(e, a, b, c, d, 76);
R4(d, e, a, b, c, 77);
R4(c, d, e, a, b, 78);
R4(b, c, d, e, a, 79);
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
/* Erase working structures. The order of operations is important,
* used to ensure that compiler doesn't optimize those out. */
memset(block, 0, sizeof(block));
a = b = c = d = e = 0;
(void) a;
(void) b;
(void) c;
(void) d;
(void) e;
}
void cs_sha1_init(cs_sha1_ctx *context) {
context->state[0] = 0x67452301;
context->state[1] = 0xEFCDAB89;
context->state[2] = 0x98BADCFE;
context->state[3] = 0x10325476;
context->state[4] = 0xC3D2E1F0;
context->count[0] = context->count[1] = 0;
}
void cs_sha1_update(cs_sha1_ctx *context, const unsigned char *data,
uint32_t len) {
uint32_t i, j;
j = context->count[0];
if ((context->count[0] += len << 3) < j) context->count[1]++;
context->count[1] += (len >> 29);
j = (j >> 3) & 63;
if ((j + len) > 63) {
memcpy(&context->buffer[j], data, (i = 64 - j));
cs_sha1_transform(context->state, context->buffer);
for (; i + 63 < len; i += 64) {
cs_sha1_transform(context->state, &data[i]);
}
j = 0;
} else
i = 0;
memcpy(&context->buffer[j], &data[i], len - i);
}
void cs_sha1_final(unsigned char digest[20], cs_sha1_ctx *context) {
unsigned i;
unsigned char finalcount[8], c;
for (i = 0; i < 8; i++) {
finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >>
((3 - (i & 3)) * 8)) &
255);
}
c = 0200;
cs_sha1_update(context, &c, 1);
while ((context->count[0] & 504) != 448) {
c = 0000;
cs_sha1_update(context, &c, 1);
}
cs_sha1_update(context, finalcount, 8);
for (i = 0; i < 20; i++) {
digest[i] =
(unsigned char) ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
}
memset(context, '\0', sizeof(*context));
memset(&finalcount, '\0', sizeof(finalcount));
}
void cs_hmac_sha1(const unsigned char *key, size_t keylen,
const unsigned char *data, size_t datalen,
unsigned char out[20]) {
cs_sha1_ctx ctx;
unsigned char buf1[64], buf2[64], tmp_key[20], i;
if (keylen > sizeof(buf1)) {
cs_sha1_init(&ctx);
cs_sha1_update(&ctx, key, keylen);
cs_sha1_final(tmp_key, &ctx);
key = tmp_key;
keylen = sizeof(tmp_key);
}
memset(buf1, 0, sizeof(buf1));
memset(buf2, 0, sizeof(buf2));
memcpy(buf1, key, keylen);
memcpy(buf2, key, keylen);
for (i = 0; i < sizeof(buf1); i++) {
buf1[i] ^= 0x36;
buf2[i] ^= 0x5c;
}
cs_sha1_init(&ctx);
cs_sha1_update(&ctx, buf1, sizeof(buf1));
cs_sha1_update(&ctx, data, datalen);
cs_sha1_final(out, &ctx);
cs_sha1_init(&ctx);
cs_sha1_update(&ctx, buf2, sizeof(buf2));
cs_sha1_update(&ctx, out, 20);
cs_sha1_final(out, &ctx);
}
#endif /* EXCLUDE_COMMON */
#ifdef V7_MODULE_LINES
#line 1 "common/cs_dirent.c"
#endif
/*
* Copyright (c) 2015 Cesanta Software Limited
* All rights reserved
*/
#ifndef EXCLUDE_COMMON
/* Amalgamated: #include "common/mg_mem.h" */
/* Amalgamated: #include "common/cs_dirent.h" */
/*
* This file contains POSIX opendir/closedir/readdir API implementation
* for systems which do not natively support it (e.g. Windows).
*/
#ifdef _WIN32
struct win32_dir {
DIR d;
HANDLE handle;
WIN32_FIND_DATAW info;
struct dirent result;
};
DIR *opendir(const char *name) {
struct win32_dir *dir = NULL;
wchar_t wpath[MAX_PATH];
DWORD attrs;
if (name == NULL) {
SetLastError(ERROR_BAD_ARGUMENTS);
} else if ((dir = (struct win32_dir *) MG_MALLOC(sizeof(*dir))) == NULL) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
} else {
to_wchar(name, wpath, ARRAY_SIZE(wpath));
attrs = GetFileAttributesW(wpath);
if (attrs != 0xFFFFFFFF && (attrs & FILE_ATTRIBUTE_DIRECTORY)) {
(void) wcscat(wpath, L"\\*");
dir->handle = FindFirstFileW(wpath, &dir->info);
dir->result.d_name[0] = '\0';
} else {
MG_FREE(dir);
dir = NULL;
}
}
return (DIR *) dir;
}
int closedir(DIR *d) {
struct win32_dir *dir = (struct win32_dir *) d;
int result = 0;
if (dir != NULL) {
if (dir->handle != INVALID_HANDLE_VALUE)
result = FindClose(dir->handle) ? 0 : -1;
MG_FREE(dir);
} else {
result = -1;
SetLastError(ERROR_BAD_ARGUMENTS);
}
return result;
}
struct dirent *readdir(DIR *d) {
struct win32_dir *dir = (struct win32_dir *) d;
struct dirent *result = NULL;
if (dir) {
memset(&dir->result, 0, sizeof(dir->result));
if (dir->handle != INVALID_HANDLE_VALUE) {
result = &dir->result;
(void) WideCharToMultiByte(CP_UTF8, 0, dir->info.cFileName, -1,
result->d_name, sizeof(result->d_name), NULL,
NULL);
if (!FindNextFileW(dir->handle, &dir->info)) {
(void) FindClose(dir->handle);
dir->handle = INVALID_HANDLE_VALUE;
}
} else {
SetLastError(ERROR_FILE_NOT_FOUND);
}
} else {
SetLastError(ERROR_BAD_ARGUMENTS);
}
return result;
}
#endif
#endif /* EXCLUDE_COMMON */
/* ISO C requires a translation unit to contain at least one declaration */
typedef int cs_dirent_dummy;
#ifdef V7_MODULE_LINES
#line 1 "common/cs_file.c"
#endif
/*
* Copyright (c) 2015 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "common/cs_file.h" */
#include
#include
#ifdef CS_MMAP
#include
#include
#include
#endif
#ifndef EXCLUDE_COMMON
char *cs_read_file(const char *path, size_t *size) WEAK;
char *cs_read_file(const char *path, size_t *size) {
FILE *fp;
char *data = NULL;
if ((fp = fopen(path, "rb")) == NULL) {
} else if (fseek(fp, 0, SEEK_END) != 0) {
fclose(fp);
} else {
*size = ftell(fp);
data = (char *) malloc(*size + 1);
if (data != NULL) {
fseek(fp, 0, SEEK_SET); /* Some platforms might not have rewind(), Oo */
if (fread(data, 1, *size, fp) != *size) {
free(data);
return NULL;
}
data[*size] = '\0';
}
fclose(fp);
}
return data;
}
#endif /* EXCLUDE_COMMON */
#ifdef CS_MMAP
char *cs_mmap_file(const char *path, size_t *size) WEAK;
char *cs_mmap_file(const char *path, size_t *size) {
char *r;
int fd = open(path, O_RDONLY, 0);
struct stat st;
if (fd < 0) return NULL;
fstat(fd, &st);
*size = (size_t) st.st_size;
r = (char *) mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (r == MAP_FAILED) return NULL;
return r;
}
#endif
#ifdef V7_MODULE_LINES
#line 1 "common/cs_heap_trace.c"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#include
#include
#ifndef MGOS_ENABLE_CALL_TRACE
#define MGOS_ENABLE_CALL_TRACE 0
#endif
#ifndef V7_ENABLE_CALL_TRACE
#define V7_ENABLE_CALL_TRACE 0
#endif
#if MGOS_ENABLE_CALL_TRACE || V7_ENABLE_CALL_TRACE
/*
* If we don't have V7's profiling functions, roll our own.
* This is copy-pasta from v7/src/cyg_profile.c
*/
#ifndef CALL_TRACE_SIZE
#define CALL_TRACE_SIZE 32
#endif
typedef struct {
void *addresses[CALL_TRACE_SIZE];
uint16_t size;
} call_trace_t;
static call_trace_t call_trace;
#if MGOS_ENABLE_CALL_TRACE
void esp_exc_printf(const char *fmt, ...);
#define call_trace_printf esp_exc_printf
#else
#define call_trace_printf printf
#endif
NOINSTR void print_call_trace() {
static void *prev_trace[CALL_TRACE_SIZE];
unsigned int size = call_trace.size;
if (size > CALL_TRACE_SIZE) size = CALL_TRACE_SIZE;
unsigned int i;
uintptr_t pa = 0;
for (i = 0; i < size; i++) {
if (call_trace.addresses[i] != prev_trace[i]) break;
pa = (uintptr_t) call_trace.addresses[i];
}
call_trace_printf("%u %u", size, i);
for (; i < size; i++) {
const uintptr_t a = (uintptr_t) call_trace.addresses[i];
/*
* Perform a rudimentary deduplication: an address is likely to have higher
* bits the same as previous, turn them off.
* Do it in 4-bit nibbles so they fall nicely on hex digit boundary.
*/
uintptr_t mask = ~((uintptr_t) 0);
while (mask != 0 && (a & mask) != (pa & mask)) mask <<= 4;
call_trace_printf(" %lx", (unsigned long) (a & ~mask));
prev_trace[i] = (void *) a;
pa = a;
}
call_trace_printf("\n");
}
#if MGOS_ENABLE_CALL_TRACE && !V7_ENABLE_CALL_TRACE
IRAM NOINSTR void __cyg_profile_func_enter(void *this_fn, void *call_site) {
if (call_trace.size < CALL_TRACE_SIZE) {
call_trace.addresses[call_trace.size] = this_fn;
}
call_trace.size++;
(void) this_fn;
(void) call_site;
}
IRAM NOINSTR void __cyg_profile_func_exit(void *this_fn, void *call_site) {
if (call_trace.size > 0) call_trace.size--;
(void) this_fn;
(void) call_site;
}
#endif
#endif /* MGOS_ENABLE_CALL_TRACE || V7_ENABLE_CALL_TRACE */
#ifdef V7_MODULE_LINES
#line 1 "common/cs_strtod.c"
#endif
#include
#include
#include
int cs_strncasecmp(const char *s1, const char *s2, size_t n) {
if (n == 0) {
return 0;
}
while (n-- != 0 && tolower((int) *s1) == tolower((int) *s2)) {
if (n == 0 || *s1 == '\0' || *s2 == '\0') {
break;
}
s1++;
s2++;
}
return tolower(*(unsigned char *) s1) - tolower(*(unsigned char *) s2);
}
/*
* based on Source:
* https://github.com/anakod/Sming/blob/master/Sming/system/stringconversion.cpp#L93
*/
double cs_strtod(const char *str, char **endptr) {
double result = 0.0;
char c;
const char *str_start;
struct {
unsigned neg : 1; /* result is negative */
unsigned decimals : 1; /* parsing decimal part */
unsigned is_exp : 1; /* parsing exponent like e+5 */
unsigned is_exp_neg : 1; /* exponent is negative */
} flags = {0, 0, 0, 0};
while (isspace((int) *str)) {
str++;
}
if (*str == 0) {
/* only space in str? */
if (endptr != 0) *endptr = (char *) str;
return result;
}
/* Handle leading plus/minus signs */
while (*str == '-' || *str == '+') {
if (*str == '-') {
flags.neg = !flags.neg;
}
str++;
}
if (cs_strncasecmp(str, "NaN", 3) == 0) {
if (endptr != 0) *endptr = (char *) str + 3;
return NAN;
}
if (cs_strncasecmp(str, "INF", 3) == 0) {
str += 3;
if (cs_strncasecmp(str, "INITY", 5) == 0) str += 5;
if (endptr != 0) *endptr = (char *) str;
return flags.neg ? -INFINITY : INFINITY;
}
str_start = str;
if (*str == '0' && (*(str + 1) == 'x' || *(str + 1) == 'X')) {
/* base 16 */
str += 2;
while ((c = tolower((int) *str))) {
int d;
if (c >= '0' && c <= '9') {
d = c - '0';
} else if (c >= 'a' && c <= 'f') {
d = 10 + (c - 'a');
} else {
break;
}
result = 16 * result + d;
str++;
}
} else if (*str == '0' && (*(str + 1) == 'b' || *(str + 1) == 'B')) {
/* base 2 */
str += 2;
while ((c = *str)) {
int d = c - '0';
if (c != '0' && c != '1') break;
result = 2 * result + d;
str++;
}
} else if (*str == '0' && *(str + 1) >= '0' && *(str + 1) <= '7') {
/* base 8 */
while ((c = *str)) {
int d = c - '0';
if (c < '0' || c > '7') {
/* fallback to base 10 */
str = str_start;
break;
}
result = 8 * result + d;
str++;
}
}
if (str == str_start) {
/* base 10 */
/* exponent specified explicitly, like in 3e-5, exponent is -5 */
int exp = 0;
/* exponent calculated from dot, like in 1.23, exponent is -2 */
int exp_dot = 0;
result = 0;
while ((c = *str)) {
int d;
if (c == '.') {
if (!flags.decimals) {
/* going to parse decimal part */
flags.decimals = 1;
str++;
continue;
} else {
/* non-expected dot: assume number data is over */
break;
}
} else if (c == 'e' || c == 'E') {
/* going to parse exponent part */
flags.is_exp = 1;
str++;
c = *str;
/* check sign of the exponent */
if (c == '-' || c == '+') {
if (c == '-') {
flags.is_exp_neg = 1;
}
str++;
}
continue;
}
d = c - '0';
if (d < 0 || d > 9) {
break;
}
if (!flags.is_exp) {
/* apply current digit to the result */
result = 10 * result + d;
if (flags.decimals) {
exp_dot--;
}
} else {
/* apply current digit to the exponent */
if (flags.is_exp_neg) {
if (exp > -1022) {
exp = 10 * exp - d;
}
} else {
if (exp < 1023) {
exp = 10 * exp + d;
}
}
}
str++;
}
exp += exp_dot;
/*
* TODO(dfrank): it probably makes sense not to adjust intermediate `double
* result`, but build double number accordingly to IEEE 754 from taken
* (integer) mantissa, exponent and sign. That would work faster, and we
* can avoid any possible round errors.
*/
/* if exponent is non-zero, apply it */
if (exp != 0) {
if (exp < 0) {
while (exp++ != 0) {
result /= 10;
}
} else {
while (exp-- != 0) {
result *= 10;
}
}
}
}
if (flags.neg) {
result = -result;
}
if (endptr != 0) {
*endptr = (char *) str;
}
return result;
}
#ifdef V7_MODULE_LINES
#line 1 "common/coroutine.c"
#endif
/*
* Copyright (c) 2015 Cesanta Software Limited
* All rights reserved
*/
/*
* Module that provides generic macros and functions to implement "coroutines",
* i.e. C code that uses `mbuf` as a stack for function calls.
*
* More info: see the design doc: https://goo.gl/kfcG61
*/
#include
#include
/* Amalgamated: #include "common/coroutine.h" */
/*
* Unwinds stack by 1 function. Used when we're returning from function and
* when an exception is thrown.
*/
static void _level_up(struct cr_ctx *p_ctx) {
/* get size of current function's stack data */
size_t locals_size = _CR_CURR_FUNC_LOCALS_SIZE(p_ctx);
/* check stacks underflow */
if (_CR_STACK_FID_UND_CHECK(p_ctx, 1 /*fid*/)) {
p_ctx->status = CR_RES__ERR_STACK_CALL_UNDERFLOW;
return;
} else if (_CR_STACK_DATA_UND_CHECK(p_ctx, locals_size)) {
p_ctx->status = CR_RES__ERR_STACK_DATA_UNDERFLOW;
return;
}
/* decrement stacks */
_CR_STACK_DATA_FREE(p_ctx, locals_size);
_CR_STACK_FID_FREE(p_ctx, 1 /*fid*/);
p_ctx->stack_ret.len = p_ctx->cur_fid_idx;
/* if we have exception marker here, adjust cur_fid_idx */
while (CR_CURR_FUNC_C(p_ctx) == CR_FID__TRY_MARKER) {
/* check for stack underflow */
if (_CR_STACK_FID_UND_CHECK(p_ctx, _CR_TRY_SIZE)) {
p_ctx->status = CR_RES__ERR_STACK_CALL_UNDERFLOW;
return;
}
_CR_STACK_FID_FREE(p_ctx, _CR_TRY_SIZE);
}
}
enum cr_status cr_on_iter_begin(struct cr_ctx *p_ctx) {
if (p_ctx->status != CR_RES__OK) {
goto out;
} else if (p_ctx->called_fid != CR_FID__NONE) {
/* need to call new function */
size_t locals_size = p_ctx->p_func_descrs[p_ctx->called_fid].locals_size;
/*
* increment stack pointers
*/
/* make sure this function has correct `struct cr_func_desc` entry */
assert(locals_size == p_ctx->call_locals_size);
/*
* make sure we haven't mistakenly included "zero-sized" `.._arg_t`
* structure in `.._locals_t` struct
*
* By "zero-sized" I mean `cr_zero_size_type_t`.
*/
assert(locals_size < sizeof(cr_zero_size_type_t));
_CR_STACK_DATA_ALLOC(p_ctx, locals_size);
_CR_STACK_RET_ALLOC(p_ctx, 1 /*fid*/);
p_ctx->cur_fid_idx = p_ctx->stack_ret.len;
/* copy arguments to our "stack" (and advance locals stack pointer) */
memcpy(p_ctx->stack_data.buf + p_ctx->stack_data.len - locals_size,
p_ctx->p_arg_retval, p_ctx->call_arg_size);
/* set function id */
CR_CURR_FUNC_C(p_ctx) = p_ctx->called_fid;
/* clear called_fid */
p_ctx->called_fid = CR_FID__NONE;
} else if (p_ctx->need_return) {
/* need to return from the currently running function */
_level_up(p_ctx);
if (p_ctx->status != CR_RES__OK) {
goto out;
}
p_ctx->need_return = 0;
} else if (p_ctx->need_yield) {
/* need to yield */
p_ctx->need_yield = 0;
p_ctx->status = CR_RES__OK_YIELDED;
goto out;
} else if (p_ctx->thrown_exc != CR_EXC_ID__NONE) {
/* exception was thrown */
/* unwind stack until we reach the bottom, or find some try-catch blocks */
do {
_level_up(p_ctx);
if (p_ctx->status != CR_RES__OK) {
goto out;
}
if (_CR_TRY_MARKER(p_ctx) == CR_FID__TRY_MARKER) {
/* we have some try-catch here, go to the first catch */
CR_CURR_FUNC_C(p_ctx) = _CR_TRY_CATCH_FID(p_ctx);
break;
} else if (CR_CURR_FUNC_C(p_ctx) == CR_FID__NONE) {
/* we've reached the bottom of the stack */
p_ctx->status = CR_RES__ERR_UNCAUGHT_EXCEPTION;
break;
}
} while (1);
}
/* remember pointer to current function's locals */
_CR_CUR_FUNC_LOCALS_UPD(p_ctx);
out:
return p_ctx->status;
}
void cr_context_init(struct cr_ctx *p_ctx, union user_arg_ret *p_arg_retval,
size_t arg_retval_size,
const struct cr_func_desc *p_func_descrs) {
/*
* make sure we haven't mistakenly included "zero-sized" `.._arg_t`
* structure in `union user_arg_ret`.
*
* By "zero-sized" I mean `cr_zero_size_type_t`.
*/
assert(arg_retval_size < sizeof(cr_zero_size_type_t));
#ifdef NDEBUG
(void) arg_retval_size;
#endif
memset(p_ctx, 0x00, sizeof(*p_ctx));
p_ctx->p_func_descrs = p_func_descrs;
p_ctx->p_arg_retval = p_arg_retval;
mbuf_init(&p_ctx->stack_data, 0);
mbuf_init(&p_ctx->stack_ret, 0);
mbuf_append(&p_ctx->stack_ret, NULL, 1 /*starting byte for CR_FID__NONE*/);
p_ctx->cur_fid_idx = p_ctx->stack_ret.len;
_CR_CALL_PREPARE(p_ctx, CR_FID__NONE, 0, 0, CR_FID__NONE);
}
void cr_context_free(struct cr_ctx *p_ctx) {
mbuf_free(&p_ctx->stack_data);
mbuf_free(&p_ctx->stack_ret);
}
#ifdef V7_MODULE_LINES
#line 1 "common/platforms/mbed/mbed_libc.c"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "common/platform.h" */
#if CS_PLATFORM == CS_P_MBED
long timezone;
/*
* The GCC ARM toolchain for implements a weak
* gettimeofday stub that should be implemented
* to hook the OS time source. But mbed OS doesn't do it;
* the mbed doc only talks about C date and time functions:
*
* https://docs.mbed.com/docs/mbed-os-api-reference/en/5.1/APIs/tasks/Time/
*
* gettimeof day is a BSD API.
*/
int _gettimeofday(struct timeval *tv, void *tzvp) {
tv->tv_sec = time(NULL);
tv->tv_usec = 0;
return 0;
}
int inet_aton(const char *cp, struct in_addr *inp) {
/* We don't have aton, but have pton in mbed */
return inet_pton(AF_INET, cp, inp);
}
in_addr_t inet_addr(const char *cp) {
in_addr_t ret;
if (inet_pton(AF_INET, cp, &ret) != 1) {
return 0;
}
return ret;
}
#endif /* CS_PLATFORM == CS_P_MBED */
#ifdef V7_MODULE_LINES
#line 1 "v7/builtin/file.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* Amalgamated: #include "v7/src/primitive.h" */
/* Amalgamated: #include "v7/src/string.h" */
/* Amalgamated: #include "v7/src/exceptions.h" */
/* Amalgamated: #include "v7/src/object.h" */
/* Amalgamated: #include "v7/src/exec.h" */
/* Amalgamated: #include "v7/src/array.h" */
/* Amalgamated: #include "common/mbuf.h" */
/* Amalgamated: #include "common/cs_file.h" */
/* Amalgamated: #include "v7/src/v7_features.h" */
/* Amalgamated: #include "common/cs_dirent.h" */
#if V7_ENABLE_FILE && !defined(V7_NO_FS)
static const char s_fd_prop[] = "__fd";
#ifndef NO_LIBC
static FILE *v7_val_to_file(struct v7 *v7, v7_val_t val) {
(void) v7;
return (FILE *) v7_get_ptr(v7, val);
}
static v7_val_t v7_file_to_val(struct v7 *v7, FILE *file) {
(void) v7;
return v7_mk_foreign(v7, file);
}
static int v7_is_file_type(v7_val_t val) {
return v7_is_foreign(val);
}
#else
FILE *v7_val_to_file(struct v7 *v7, v7_val_t val);
v7_val_t v7_file_to_val(struct v7 *v7, FILE *file);
int v7_is_file_type(v7_val_t val);
#endif
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err File_eval(struct v7 *v7, v7_val_t *res) {
enum v7_err rcode = V7_OK;
v7_val_t arg0 = v7_arg(v7, 0);
*res = V7_UNDEFINED;
if (v7_is_string(arg0)) {
const char *s = v7_get_cstring(v7, &arg0);
if (s == NULL) {
rcode = v7_throwf(v7, "TypeError", "Invalid string");
goto clean;
}
v7_set_gc_enabled(v7, 1);
rcode = v7_exec_file(v7, s, res);
if (rcode != V7_OK) {
goto clean;
}
}
clean:
return rcode;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err File_exists(struct v7 *v7, v7_val_t *res) {
enum v7_err rcode = V7_OK;
v7_val_t arg0 = v7_arg(v7, 0);
*res = v7_mk_boolean(v7, 0);
if (v7_is_string(arg0)) {
const char *fname = v7_get_cstring(v7, &arg0);
if (fname != NULL) {
struct stat st;
if (stat(fname, &st) == 0) *res = v7_mk_boolean(v7, 1);
}
}
return rcode;
}
WARN_UNUSED_RESULT
static enum v7_err f_read(struct v7 *v7, int all, v7_val_t *res) {
enum v7_err rcode = V7_OK;
v7_val_t this_obj = v7_get_this(v7);
v7_val_t arg0 = v7_get(v7, this_obj, s_fd_prop, sizeof(s_fd_prop) - 1);
if (v7_is_file_type(arg0)) {
struct mbuf m;
char buf[BUFSIZ];
int n;
FILE *fp = v7_val_to_file(v7, arg0);
/* Read file contents into mbuf */
mbuf_init(&m, 0);
while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
mbuf_append(&m, buf, n);
if (!all) {
break;
}
}
if (m.len > 0) {
*res = v7_mk_string(v7, m.buf, m.len, 1);
mbuf_free(&m);
goto clean;
}
}
*res = v7_mk_string(v7, "", 0, 1);
clean:
return rcode;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err File_obj_read(struct v7 *v7, v7_val_t *res) {
return f_read(v7, 0, res);
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err File_obj_write(struct v7 *v7, v7_val_t *res) {
enum v7_err rcode = V7_OK;
v7_val_t this_obj = v7_get_this(v7);
v7_val_t arg0 = v7_get(v7, this_obj, s_fd_prop, sizeof(s_fd_prop) - 1);
v7_val_t arg1 = v7_arg(v7, 0);
size_t n, sent = 0, len = 0;
if (v7_is_file_type(arg0) && v7_is_string(arg1)) {
const char *s = v7_get_string(v7, &arg1, &len);
FILE *fp = v7_val_to_file(v7, arg0);
while (sent < len && (n = fwrite(s + sent, 1, len - sent, fp)) > 0) {
sent += n;
}
}
*res = v7_mk_number(v7, sent);
return rcode;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err File_obj_close(struct v7 *v7, v7_val_t *res) {
enum v7_err rcode = V7_OK;
v7_val_t this_obj = v7_get_this(v7);
v7_val_t prop = v7_get(v7, this_obj, s_fd_prop, sizeof(s_fd_prop) - 1);
int ires = -1;
if (v7_is_file_type(prop)) {
ires = fclose(v7_val_to_file(v7, prop));
}
*res = v7_mk_number(v7, ires);
return rcode;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err File_open(struct v7 *v7, v7_val_t *res) {
enum v7_err rcode = V7_OK;
v7_val_t arg0 = v7_arg(v7, 0);
v7_val_t arg1 = v7_arg(v7, 1);
FILE *fp = NULL;
if (v7_is_string(arg0)) {
const char *s1 = v7_get_cstring(v7, &arg0);
const char *s2 = "rb"; /* Open files in read mode by default */
if (v7_is_string(arg1)) {
s2 = v7_get_cstring(v7, &arg1);
}
if (s1 == NULL || s2 == NULL) {
*res = V7_NULL;
goto clean;
}
fp = fopen(s1, s2);
if (fp != NULL) {
v7_val_t obj = v7_mk_object(v7);
v7_val_t file_proto = v7_get(
v7, v7_get(v7, v7_get_global(v7), "File", ~0), "prototype", ~0);
v7_set_proto(v7, obj, file_proto);
v7_def(v7, obj, s_fd_prop, sizeof(s_fd_prop) - 1, V7_DESC_ENUMERABLE(0),
v7_file_to_val(v7, fp));
*res = obj;
goto clean;
}
}
*res = V7_NULL;
clean:
return rcode;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err File_read(struct v7 *v7, v7_val_t *res) {
v7_val_t arg0 = v7_arg(v7, 0);
if (v7_is_string(arg0)) {
const char *path = v7_get_cstring(v7, &arg0);
size_t size = 0;
char *data = cs_read_file(path, &size);
if (data != NULL) {
*res = v7_mk_string(v7, data, size, 1);
free(data);
}
}
return V7_OK;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err File_write(struct v7 *v7, v7_val_t *res) {
v7_val_t arg0 = v7_arg(v7, 0);
v7_val_t arg1 = v7_arg(v7, 1);
*res = v7_mk_boolean(v7, 0);
if (v7_is_string(arg0) && v7_is_string(arg1)) {
const char *path = v7_get_cstring(v7, &arg0);
size_t len;
const char *buf = v7_get_string(v7, &arg1, &len);
FILE *fp = fopen(path, "wb+");
if (fp != NULL) {
if (fwrite(buf, 1, len, fp) == len) {
*res = v7_mk_boolean(v7, 1);
}
fclose(fp);
}
}
return V7_OK;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err File_rename(struct v7 *v7, v7_val_t *res) {
enum v7_err rcode = V7_OK;
v7_val_t arg0 = v7_arg(v7, 0);
v7_val_t arg1 = v7_arg(v7, 1);
int ires = -1;
if (v7_is_string(arg0) && v7_is_string(arg1)) {
const char *from = v7_get_cstring(v7, &arg0);
const char *to = v7_get_cstring(v7, &arg1);
if (from == NULL || to == NULL) {
*res = v7_mk_number(v7, ENOENT);
goto clean;
}
ires = rename(from, to);
}
*res = v7_mk_number(v7, ires == 0 ? 0 : errno);
clean:
return rcode;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err File_loadJSON(struct v7 *v7, v7_val_t *res) {
enum v7_err rcode = V7_OK;
v7_val_t arg0 = v7_arg(v7, 0);
*res = V7_UNDEFINED;
if (v7_is_string(arg0)) {
const char *file_name = v7_get_cstring(v7, &arg0);
if (file_name == NULL) {
goto clean;
}
rcode = v7_parse_json_file(v7, file_name, res);
if (rcode != V7_OK) {
/* swallow exception and return undefined */
v7_clear_thrown_value(v7);
rcode = V7_OK;
*res = V7_UNDEFINED;
}
}
clean:
return rcode;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err File_remove(struct v7 *v7, v7_val_t *res) {
enum v7_err rcode = V7_OK;
v7_val_t arg0 = v7_arg(v7, 0);
int ires = -1;
if (v7_is_string(arg0)) {
const char *path = v7_get_cstring(v7, &arg0);
if (path == NULL) {
*res = v7_mk_number(v7, ENOENT);
goto clean;
}
ires = remove(path);
}
*res = v7_mk_number(v7, ires == 0 ? 0 : errno);
clean:
return rcode;
}
#if V7_ENABLE__File__list
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err File_list(struct v7 *v7, v7_val_t *res) {
enum v7_err rcode = V7_OK;
v7_val_t arg0 = v7_arg(v7, 0);
*res = V7_UNDEFINED;
if (v7_is_string(arg0)) {
const char *path = v7_get_cstring(v7, &arg0);
struct dirent *dp;
DIR *dirp;
if (path == NULL) {
goto clean;
}
if ((dirp = (opendir(path))) != NULL) {
*res = v7_mk_array(v7);
while ((dp = readdir(dirp)) != NULL) {
/* Do not show current and parent dirs */
if (strcmp((const char *) dp->d_name, ".") == 0 ||
strcmp((const char *) dp->d_name, "..") == 0) {
continue;
}
/* Add file name to the list */
v7_array_push(v7, *res,
v7_mk_string(v7, (const char *) dp->d_name,
strlen((const char *) dp->d_name), 1));
}
closedir(dirp);
}
}
clean:
return rcode;
}
#endif /* V7_ENABLE__File__list */
void init_file(struct v7 *v7) {
v7_val_t file_obj = v7_mk_object(v7), file_proto = v7_mk_object(v7);
v7_set(v7, v7_get_global(v7), "File", 4, file_obj);
v7_set(v7, file_obj, "prototype", 9, file_proto);
v7_set_method(v7, file_obj, "eval", File_eval);
v7_set_method(v7, file_obj, "exists", File_exists);
v7_set_method(v7, file_obj, "remove", File_remove);
v7_set_method(v7, file_obj, "rename", File_rename);
v7_set_method(v7, file_obj, "open", File_open);
v7_set_method(v7, file_obj, "read", File_read);
v7_set_method(v7, file_obj, "write", File_write);
v7_set_method(v7, file_obj, "loadJSON", File_loadJSON);
#if V7_ENABLE__File__list
v7_set_method(v7, file_obj, "list", File_list);
#endif
v7_set_method(v7, file_proto, "close", File_obj_close);
v7_set_method(v7, file_proto, "read", File_obj_read);
v7_set_method(v7, file_proto, "write", File_obj_write);
#if V7_ENABLE__File__require
v7_def(v7, v7_get_global(v7), "_modcache", ~0, 0, v7_mk_object(v7));
if (v7_exec(v7,
"function require(m) { "
" if (m in _modcache) { return _modcache[m]; }"
" var module = {exports:{}};"
" File.eval(m);"
" return (_modcache[m] = module.exports)"
" }",
NULL) != V7_OK) {
/* TODO(mkm): percolate failure */
}
#endif
}
#else
void init_file(struct v7 *v7) {
(void) v7;
}
#endif /* NO_LIBC */
#ifdef V7_MODULE_LINES
#line 1 "v7/builtin/socket.c"
#endif
/*
* Copyright (c) 2015 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* Amalgamated: #include "v7/src/string.h" */
/* Amalgamated: #include "v7/src/object.h" */
/* Amalgamated: #include "v7/src/primitive.h" */
/* Amalgamated: #include "v7/src/conversion.h" */
/* Amalgamated: #include "common/mbuf.h" */
/* Amalgamated: #include "common/platform.h" */
#if V7_ENABLE_SOCKET
#ifdef __WATCOM__
#define SOMAXCONN 128
#endif
#ifndef RECV_BUF_SIZE
#define RECV_BUF_SIZE 1024
#endif
static const char s_sock_prop[] = "__sock";
static uint32_t s_resolve(struct v7 *v7, v7_val_t ip_address) {
size_t n;
const char *s = v7_get_string(v7, &ip_address, &n);
struct hostent *he = gethostbyname(s);
return he == NULL ? 0 : *(uint32_t *) he->h_addr_list[0];
}
WARN_UNUSED_RESULT
static enum v7_err s_fd_to_sock_obj(struct v7 *v7, sock_t fd, v7_val_t *res) {
enum v7_err rcode = V7_OK;
v7_val_t sock_proto =
v7_get(v7, v7_get(v7, v7_get_global(v7), "Socket", ~0), "prototype", ~0);
*res = v7_mk_object(v7);
v7_set_proto(v7, *res, sock_proto);
v7_def(v7, *res, s_sock_prop, sizeof(s_sock_prop) - 1, V7_DESC_ENUMERABLE(0),
v7_mk_number(v7, fd));
return rcode;
}
/* Socket.connect(host, port [, is_udp]) -> socket_object */
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err Socket_connect(struct v7 *v7, v7_val_t *res) {
enum v7_err rcode = V7_OK;
v7_val_t arg0 = v7_arg(v7, 0);
v7_val_t arg1 = v7_arg(v7, 1);
v7_val_t arg2 = v7_arg(v7, 2);
if (v7_is_number(arg1) && v7_is_string(arg0)) {
struct sockaddr_in sin;
sock_t sock =
socket(AF_INET, v7_is_truthy(v7, arg2) ? SOCK_DGRAM : SOCK_STREAM, 0);
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = s_resolve(v7, arg0);
sin.sin_port = htons((uint16_t) v7_get_double(v7, arg1));
if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
closesocket(sock);
} else {
rcode = s_fd_to_sock_obj(v7, sock, res);
goto clean;
}
}
*res = V7_NULL;
clean:
return rcode;
}
/* Socket.listen(port [, ip_address [,is_udp]]) -> sock */
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err Socket_listen(struct v7 *v7, v7_val_t *res) {
enum v7_err rcode = V7_OK;
v7_val_t arg0 = v7_arg(v7, 0);
v7_val_t arg1 = v7_arg(v7, 1);
v7_val_t arg2 = v7_arg(v7, 2);
if (v7_is_number(arg0)) {
struct sockaddr_in sin;
int on = 1;
sock_t sock =
socket(AF_INET, v7_is_truthy(v7, arg2) ? SOCK_DGRAM : SOCK_STREAM, 0);
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons((uint16_t) v7_get_double(v7, arg0));
if (v7_is_string(arg1)) {
sin.sin_addr.s_addr = s_resolve(v7, arg1);
}
#if defined(_WIN32) && defined(SO_EXCLUSIVEADDRUSE)
/* "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE" http://goo.gl/RmrFTm */
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (void *) &on, sizeof(on));
#endif
#if !defined(_WIN32) || defined(SO_EXCLUSIVEADDRUSE)
/*
* SO_RESUSEADDR is not enabled on Windows because the semantics of
* SO_REUSEADDR on UNIX and Windows is different. On Windows,
* SO_REUSEADDR allows to bind a socket to a port without error even if
* the port is already open by another program. This is not the behavior
* SO_REUSEADDR was designed for, and leads to hard-to-track failure
* scenarios. Therefore, SO_REUSEADDR was disabled on Windows unless
* SO_EXCLUSIVEADDRUSE is supported and set on a socket.
*/
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on));
#endif
if (bind(sock, (struct sockaddr *) &sin, sizeof(sin)) == 0) {
listen(sock, SOMAXCONN);
rcode = s_fd_to_sock_obj(v7, sock, res);
goto clean;
} else {
closesocket(sock);
}
}
*res = V7_NULL;
clean:
return rcode;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err Socket_accept(struct v7 *v7, v7_val_t *res) {
enum v7_err rcode = V7_OK;
v7_val_t this_obj = v7_get_this(v7);
v7_val_t prop = v7_get(v7, this_obj, s_sock_prop, sizeof(s_sock_prop) - 1);
if (v7_is_number(prop)) {
struct sockaddr_in sin;
socklen_t len = sizeof(sin);
sock_t sock = (sock_t) v7_get_double(v7, prop);
sock_t fd = accept(sock, (struct sockaddr *) &sin, &len);
if (fd != INVALID_SOCKET) {
rcode = s_fd_to_sock_obj(v7, fd, res);
if (rcode == V7_OK) {
char *remote_host = inet_ntoa(sin.sin_addr);
v7_set(v7, *res, "remoteHost", ~0,
v7_mk_string(v7, remote_host, ~0, 1));
}
goto clean;
}
}
*res = V7_NULL;
clean:
return rcode;
}
/* sock.close() -> errno */
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err Socket_close(struct v7 *v7, v7_val_t *res) {
enum v7_err rcode = V7_OK;
v7_val_t this_obj = v7_get_this(v7);
v7_val_t prop = v7_get(v7, this_obj, s_sock_prop, sizeof(s_sock_prop) - 1);
*res = v7_mk_number(v7, closesocket((sock_t) v7_get_double(v7, prop)));
return rcode;
}
/* sock.recv() -> string */
WARN_UNUSED_RESULT
static enum v7_err s_recv(struct v7 *v7, int all, v7_val_t *res) {
enum v7_err rcode = V7_OK;
v7_val_t this_obj = v7_get_this(v7);
v7_val_t prop = v7_get(v7, this_obj, s_sock_prop, sizeof(s_sock_prop) - 1);
if (v7_is_number(prop)) {
char buf[RECV_BUF_SIZE];
sock_t sock = (sock_t) v7_get_double(v7, prop);
struct mbuf m;
int n;
mbuf_init(&m, 0);
while ((n = recv(sock, buf, sizeof(buf), 0)) > 0) {
mbuf_append(&m, buf, n);
if (!all) {
break;
}
}
if (n <= 0) {
closesocket(sock);
v7_def(v7, this_obj, s_sock_prop, sizeof(s_sock_prop) - 1,
V7_DESC_ENUMERABLE(0), v7_mk_number(v7, INVALID_SOCKET));
}
if (m.len > 0) {
*res = v7_mk_string(v7, m.buf, m.len, 1);
mbuf_free(&m);
goto clean;
}
}
*res = V7_NULL;
clean:
return rcode;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err Socket_recvAll(struct v7 *v7, v7_val_t *res) {
return s_recv(v7, 1, res);
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err Socket_recv(struct v7 *v7, v7_val_t *res) {
return s_recv(v7, 0, res);
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err Socket_send(struct v7 *v7, v7_val_t *res) {
enum v7_err rcode = V7_OK;
v7_val_t this_obj = v7_get_this(v7);
v7_val_t arg0 = v7_arg(v7, 0);
v7_val_t prop = v7_get(v7, this_obj, s_sock_prop, sizeof(s_sock_prop) - 1);
size_t len, sent = 0;
if (v7_is_number(prop) && v7_is_string(arg0)) {
const char *s = v7_get_string(v7, &arg0, &len);
sock_t sock = (sock_t) v7_get_double(v7, prop);
int n;
while (sent < len && (n = send(sock, s + sent, len - sent, 0)) > 0) {
sent += n;
}
}
*res = v7_mk_number(v7, sent);
return rcode;
}
void init_socket(struct v7 *v7) {
v7_val_t socket_obj = v7_mk_object(v7), sock_proto = v7_mk_object(v7);
v7_set(v7, v7_get_global(v7), "Socket", 6, socket_obj);
sock_proto = v7_mk_object(v7);
v7_set(v7, socket_obj, "prototype", 9, sock_proto);
v7_set_method(v7, socket_obj, "connect", Socket_connect);
v7_set_method(v7, socket_obj, "listen", Socket_listen);
v7_set_method(v7, sock_proto, "accept", Socket_accept);
v7_set_method(v7, sock_proto, "send", Socket_send);
v7_set_method(v7, sock_proto, "recv", Socket_recv);
v7_set_method(v7, sock_proto, "recvAll", Socket_recvAll);
v7_set_method(v7, sock_proto, "close", Socket_close);
#ifdef _WIN32
{
WSADATA data;
WSAStartup(MAKEWORD(2, 2), &data);
/* TODO(alashkin): add WSACleanup call */
}
#else
signal(SIGPIPE, SIG_IGN);
#endif
}
#else
void init_socket(struct v7 *v7) {
(void) v7;
}
#endif
#ifdef V7_MODULE_LINES
#line 1 "v7/builtin/crypto.c"
#endif
/*
* Copyright (c) 2015 Cesanta Software Limited
* All rights reserved
*/
#include
#include
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* Amalgamated: #include "v7/src/primitive.h" */
/* Amalgamated: #include "v7/src/string.h" */
/* Amalgamated: #include "v7/src/object.h" */
/* Amalgamated: #include "common/md5.h" */
/* Amalgamated: #include "common/sha1.h" */
/* Amalgamated: #include "common/str_util.h" */
/* Amalgamated: #include "common/base64.h" */
#if V7_ENABLE_CRYPTO
typedef void (*b64_func_t)(const unsigned char *, int, char *);
WARN_UNUSED_RESULT
static enum v7_err b64_transform(struct v7 *v7, b64_func_t func, double mult,
v7_val_t *res) {
enum v7_err rcode = V7_OK;
v7_val_t arg0 = v7_arg(v7, 0);
*res = V7_UNDEFINED;
if (v7_is_string(arg0)) {
size_t n;
const char *s = v7_get_string(v7, &arg0, &n);
char *buf = (char *) malloc(n * mult + 4);
if (buf != NULL) {
func((const unsigned char *) s, (int) n, buf);
*res = v7_mk_string(v7, buf, strlen(buf), 1);
free(buf);
}
}
return rcode;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err Crypto_base64_decode(struct v7 *v7, v7_val_t *res) {
return b64_transform(v7, (b64_func_t) cs_base64_decode, 0.75, res);
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err Crypto_base64_encode(struct v7 *v7, v7_val_t *res) {
return b64_transform(v7, cs_base64_encode, 1.5, res);
}
static void v7_md5(const char *data, size_t len, char buf[16]) {
cs_md5_ctx ctx;
cs_md5_init(&ctx);
cs_md5_update(&ctx, (unsigned char *) data, len);
cs_md5_final((unsigned char *) buf, &ctx);
}
static void v7_sha1(const char *data, size_t len, char buf[20]) {
cs_sha1_ctx ctx;
cs_sha1_init(&ctx);
cs_sha1_update(&ctx, (unsigned char *) data, len);
cs_sha1_final((unsigned char *) buf, &ctx);
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err Crypto_md5(struct v7 *v7, v7_val_t *res) {
enum v7_err rcode = V7_OK;
v7_val_t arg0 = v7_arg(v7, 0);
if (v7_is_string(arg0)) {
size_t len;
const char *data = v7_get_string(v7, &arg0, &len);
char buf[16];
v7_md5(data, len, buf);
*res = v7_mk_string(v7, buf, sizeof(buf), 1);
goto clean;
}
*res = V7_NULL;
clean:
return rcode;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err Crypto_md5_hex(struct v7 *v7, v7_val_t *res) {
enum v7_err rcode = V7_OK;
v7_val_t arg0 = v7_arg(v7, 0);
if (v7_is_string(arg0)) {
size_t len;
const char *data = v7_get_string(v7, &arg0, &len);
char hash[16], buf[sizeof(hash) * 2 + 1];
v7_md5(data, len, hash);
cs_to_hex(buf, (unsigned char *) hash, sizeof(hash));
*res = v7_mk_string(v7, buf, sizeof(buf) - 1, 1);
goto clean;
}
*res = V7_NULL;
clean:
return rcode;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err Crypto_sha1(struct v7 *v7, v7_val_t *res) {
enum v7_err rcode = V7_OK;
v7_val_t arg0 = v7_arg(v7, 0);
if (v7_is_string(arg0)) {
size_t len;
const char *data = v7_get_string(v7, &arg0, &len);
char buf[20];
v7_sha1(data, len, buf);
*res = v7_mk_string(v7, buf, sizeof(buf), 1);
goto clean;
}
*res = V7_NULL;
clean:
return rcode;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err Crypto_sha1_hex(struct v7 *v7, v7_val_t *res) {
enum v7_err rcode = V7_OK;
v7_val_t arg0 = v7_arg(v7, 0);
if (v7_is_string(arg0)) {
size_t len;
const char *data = v7_get_string(v7, &arg0, &len);
char hash[20], buf[sizeof(hash) * 2 + 1];
v7_sha1(data, len, hash);
cs_to_hex(buf, (unsigned char *) hash, sizeof(hash));
*res = v7_mk_string(v7, buf, sizeof(buf) - 1, 1);
goto clean;
}
*res = V7_NULL;
clean:
return rcode;
}
#endif
void init_crypto(struct v7 *v7) {
#if V7_ENABLE_CRYPTO
v7_val_t obj = v7_mk_object(v7);
v7_set(v7, v7_get_global(v7), "Crypto", 6, obj);
v7_set_method(v7, obj, "md5", Crypto_md5);
v7_set_method(v7, obj, "md5_hex", Crypto_md5_hex);
v7_set_method(v7, obj, "sha1", Crypto_sha1);
v7_set_method(v7, obj, "sha1_hex", Crypto_sha1_hex);
v7_set_method(v7, obj, "base64_encode", Crypto_base64_encode);
v7_set_method(v7, obj, "base64_decode", Crypto_base64_decode);
#else
(void) v7;
#endif
}
#ifdef V7_MODULE_LINES
#line 1 "v7/src/varint.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/varint.h" */
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
/*
* Strings in AST are encoded as tuples (length, string).
* Length is variable-length: if high bit is set in a byte, next byte is used.
* Maximum string length with such encoding is 2 ^ (7 * 4) == 256 MiB,
* assuming that sizeof(size_t) == 4.
* Small string length (less then 128 bytes) is encoded in 1 byte.
*/
V7_PRIVATE size_t decode_varint(const unsigned char *p, int *llen) {
size_t i = 0, string_len = 0;
do {
/*
* Each byte of varint contains 7 bits, in little endian order.
* MSB is a continuation bit: it tells whether next byte is used.
*/
string_len |= (p[i] & 0x7f) << (7 * i);
/*
* First we increment i, then check whether it is within boundary and
* whether decoded byte had continuation bit set.
*/
} while (++i < sizeof(size_t) && (p[i - 1] & 0x80));
*llen = i;
return string_len;
}
/* Return number of bytes to store length */
V7_PRIVATE int calc_llen(size_t len) {
int n = 0;
do {
n++;
} while (len >>= 7);
return n;
}
V7_PRIVATE int encode_varint(size_t len, unsigned char *p) {
int i, llen = calc_llen(len);
for (i = 0; i < llen; i++) {
p[i] = (len & 0x7f) | (i < llen - 1 ? 0x80 : 0);
len >>= 7;
}
return llen;
}
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/tokenizer.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "common/cs_strtod.h" */
/* Amalgamated: #include "common/utf.h" */
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/core.h" */
#if !defined(V7_NO_COMPILER)
/*
* NOTE(lsm): Must be in the same order as enum for keywords. See comment
* for function get_tok() for rationale for that.
*/
static const struct v7_vec_const s_keywords[] = {
V7_VEC("break"), V7_VEC("case"), V7_VEC("catch"),
V7_VEC("continue"), V7_VEC("debugger"), V7_VEC("default"),
V7_VEC("delete"), V7_VEC("do"), V7_VEC("else"),
V7_VEC("false"), V7_VEC("finally"), V7_VEC("for"),
V7_VEC("function"), V7_VEC("if"), V7_VEC("in"),
V7_VEC("instanceof"), V7_VEC("new"), V7_VEC("null"),
V7_VEC("return"), V7_VEC("switch"), V7_VEC("this"),
V7_VEC("throw"), V7_VEC("true"), V7_VEC("try"),
V7_VEC("typeof"), V7_VEC("var"), V7_VEC("void"),
V7_VEC("while"), V7_VEC("with")};
V7_PRIVATE int is_reserved_word_token(enum v7_tok tok) {
return tok >= TOK_BREAK && tok <= TOK_WITH;
}
/*
* Move ptr to the next token, skipping comments and whitespaces.
* Return number of new line characters detected.
*/
V7_PRIVATE int skip_to_next_tok(const char **ptr, const char *src_end) {
const char *s = *ptr, *p = NULL;
int num_lines = 0;
while (s != p && s < src_end && *s != '\0' &&
(isspace((unsigned char) *s) || *s == '/')) {
p = s;
while (s < src_end && *s != '\0' && isspace((unsigned char) *s)) {
if (*s == '\n') num_lines++;
s++;
}
if ((s + 1) < src_end && s[0] == '/' && s[1] == '/') {
s += 2;
while (s < src_end && s[0] != '\0' && s[0] != '\n') s++;
}
if ((s + 1) < src_end && s[0] == '/' && s[1] == '*') {
s += 2;
while (s < src_end && s[0] != '\0' && !(s[-1] == '/' && s[-2] == '*')) {
if (s[0] == '\n') num_lines++;
s++;
}
}
}
*ptr = s;
return num_lines;
}
/* Advance `s` pointer to the end of identifier */
static void ident(const char **s, const char *src_end) {
const unsigned char *p = (unsigned char *) *s;
int n;
Rune r;
while ((const char *) p < src_end && p[0] != '\0') {
if (p[0] == '$' || p[0] == '_' || isalnum(p[0])) {
/* $, _, or any alphanumeric are valid identifier characters */
p++;
} else if ((const char *) (p + 5) < src_end && p[0] == '\\' &&
p[1] == 'u' && isxdigit(p[2]) && isxdigit(p[3]) &&
isxdigit(p[4]) && isxdigit(p[5])) {
/* Unicode escape, \uXXXX . Could be used like "var \u0078 = 1;" */
p += 6;
} else if ((n = chartorune(&r, (char *) p)) > 1 && isalpharune(r)) {
/*
* TODO(dfrank): the chartorune() call above can read `p` past the
* src_end, so it might crash on incorrect code. The solution would be
* to modify `chartorune()` to accept src_end argument as well.
*/
/* Unicode alphanumeric character */
p += n;
} else {
break;
}
}
*s = (char *) p;
}
static enum v7_tok kw(const char *s, size_t len, int ntoks, enum v7_tok tok) {
int i;
for (i = 0; i < ntoks; i++) {
if (s_keywords[(tok - TOK_BREAK) + i].len == len &&
memcmp(s_keywords[(tok - TOK_BREAK) + i].p + 1, s + 1, len - 1) == 0)
break;
}
return i == ntoks ? TOK_IDENTIFIER : (enum v7_tok)(tok + i);
}
static enum v7_tok punct1(const char **s, const char *src_end, int ch1,
enum v7_tok tok1, enum v7_tok tok2) {
(*s)++;
if (*s < src_end && **s == ch1) {
(*s)++;
return tok1;
} else {
return tok2;
}
}
static enum v7_tok punct2(const char **s, const char *src_end, int ch1,
enum v7_tok tok1, int ch2, enum v7_tok tok2,
enum v7_tok tok3) {
if ((*s + 2) < src_end && s[0][1] == ch1 && s[0][2] == ch2) {
(*s) += 3;
return tok2;
}
return punct1(s, src_end, ch1, tok1, tok3);
}
static enum v7_tok punct3(const char **s, const char *src_end, int ch1,
enum v7_tok tok1, int ch2, enum v7_tok tok2,
enum v7_tok tok3) {
(*s)++;
if (*s < src_end) {
if (**s == ch1) {
(*s)++;
return tok1;
} else if (**s == ch2) {
(*s)++;
return tok2;
}
}
return tok3;
}
static void parse_number(const char *s, const char **end, double *num) {
*num = cs_strtod(s, (char **) end);
}
static enum v7_tok parse_str_literal(const char **p, const char *src_end) {
const char *s = *p;
int quote = '\0';
if (s < src_end) {
quote = *s++;
}
/* Scan string literal, handle escape sequences */
for (; s < src_end && *s != '\0' && *s != quote; s++) {
if (*s == '\\') {
switch (s[1]) {
case 'b':
case 'f':
case 'n':
case 'r':
case 't':
case 'v':
case '\\':
s++;
break;
default:
if (s[1] == quote) s++;
break;
}
}
}
if (s < src_end && *s == quote) {
s++;
*p = s;
return TOK_STRING_LITERAL;
} else {
return TOK_END_OF_INPUT;
}
}
/*
* This function is the heart of the tokenizer.
* Organized as a giant switch statement.
*
* Switch statement is by the first character of the input stream. If first
* character begins with a letter, it could be either keyword or identifier.
* get_tok() calls ident() which shifts `s` pointer to the end of the word.
* Now, tokenizer knows that the word begins at `p` and ends at `s`.
* It calls function kw() to scan over the keywords that start with `p[0]`
* letter. Therefore, keyword tokens and keyword strings must be in the
* same order, to let kw() function work properly.
* If kw() finds a keyword match, it returns keyword token.
* Otherwise, it returns TOK_IDENTIFIER.
* NOTE(lsm): `prev_tok` is a previously parsed token. It is needed for
* correctly parsing regex literals.
*/
V7_PRIVATE enum v7_tok get_tok(const char **s, const char *src_end, double *n,
enum v7_tok prev_tok) {
const char *p = *s;
if (p >= src_end) {
return TOK_END_OF_INPUT;
}
switch (*p) {
/* Letters */
case 'a':
ident(s, src_end);
return TOK_IDENTIFIER;
case 'b':
ident(s, src_end);
return kw(p, *s - p, 1, TOK_BREAK);
case 'c':
ident(s, src_end);
return kw(p, *s - p, 3, TOK_CASE);
case 'd':
ident(s, src_end);
return kw(p, *s - p, 4, TOK_DEBUGGER);
case 'e':
ident(s, src_end);
return kw(p, *s - p, 1, TOK_ELSE);
case 'f':
ident(s, src_end);
return kw(p, *s - p, 4, TOK_FALSE);
case 'g':
case 'h':
ident(s, src_end);
return TOK_IDENTIFIER;
case 'i':
ident(s, src_end);
return kw(p, *s - p, 3, TOK_IF);
case 'j':
case 'k':
case 'l':
case 'm':
ident(s, src_end);
return TOK_IDENTIFIER;
case 'n':
ident(s, src_end);
return kw(p, *s - p, 2, TOK_NEW);
case 'o':
case 'p':
case 'q':
ident(s, src_end);
return TOK_IDENTIFIER;
case 'r':
ident(s, src_end);
return kw(p, *s - p, 1, TOK_RETURN);
case 's':
ident(s, src_end);
return kw(p, *s - p, 1, TOK_SWITCH);
case 't':
ident(s, src_end);
return kw(p, *s - p, 5, TOK_THIS);
case 'u':
ident(s, src_end);
return TOK_IDENTIFIER;
case 'v':
ident(s, src_end);
return kw(p, *s - p, 2, TOK_VAR);
case 'w':
ident(s, src_end);
return kw(p, *s - p, 2, TOK_WHILE);
case 'x':
case 'y':
case 'z':
ident(s, src_end);
return TOK_IDENTIFIER;
case '_':
case '$':
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'G':
case 'H':
case 'I':
case 'J':
case 'K':
case 'L':
case 'M':
case 'N':
case 'O':
case 'P':
case 'Q':
case 'R':
case 'S':
case 'T':
case 'U':
case 'V':
case 'W':
case 'X':
case 'Y':
case 'Z':
case '\\': /* Identifier may start with unicode escape sequence */
ident(s, src_end);
return TOK_IDENTIFIER;
/* Numbers */
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
parse_number(p, s, n);
return TOK_NUMBER;
/* String literals */
case '\'':
case '"':
return parse_str_literal(s, src_end);
/* Punctuators */
case '=':
return punct2(s, src_end, '=', TOK_EQ, '=', TOK_EQ_EQ, TOK_ASSIGN);
case '!':
return punct2(s, src_end, '=', TOK_NE, '=', TOK_NE_NE, TOK_NOT);
case '%':
return punct1(s, src_end, '=', TOK_REM_ASSIGN, TOK_REM);
case '*':
return punct1(s, src_end, '=', TOK_MUL_ASSIGN, TOK_MUL);
case '/':
/*
* TOK_DIV, TOK_DIV_ASSIGN, and TOK_REGEX_LITERAL start with `/` char.
* Division can happen after an expression.
* In expressions like this:
* a /= b; c /= d;
* things between slashes is NOT a regex literal.
* The switch below catches all cases where division happens.
*/
switch (prev_tok) {
case TOK_CLOSE_CURLY:
case TOK_CLOSE_PAREN:
case TOK_CLOSE_BRACKET:
case TOK_IDENTIFIER:
case TOK_NUMBER:
return punct1(s, src_end, '=', TOK_DIV_ASSIGN, TOK_DIV);
default:
/* Not a division - this is a regex. Scan until closing slash */
for (p++; p < src_end && *p != '\0' && *p != '\n'; p++) {
if (*p == '\\') {
/* Skip escape sequence */
p++;
} else if (*p == '/') {
/* This is a closing slash */
p++;
/* Skip regex flags */
while (*p == 'g' || *p == 'i' || *p == 'm') {
p++;
}
*s = p;
return TOK_REGEX_LITERAL;
}
}
break;
}
return punct1(s, src_end, '=', TOK_DIV_ASSIGN, TOK_DIV);
case '^':
return punct1(s, src_end, '=', TOK_XOR_ASSIGN, TOK_XOR);
case '+':
return punct3(s, src_end, '+', TOK_PLUS_PLUS, '=', TOK_PLUS_ASSIGN,
TOK_PLUS);
case '-':
return punct3(s, src_end, '-', TOK_MINUS_MINUS, '=', TOK_MINUS_ASSIGN,
TOK_MINUS);
case '&':
return punct3(s, src_end, '&', TOK_LOGICAL_AND, '=', TOK_AND_ASSIGN,
TOK_AND);
case '|':
return punct3(s, src_end, '|', TOK_LOGICAL_OR, '=', TOK_OR_ASSIGN,
TOK_OR);
case '<':
if (*s + 1 < src_end && s[0][1] == '=') {
(*s) += 2;
return TOK_LE;
}
return punct2(s, src_end, '<', TOK_LSHIFT, '=', TOK_LSHIFT_ASSIGN,
TOK_LT);
case '>':
if (*s + 1 < src_end && s[0][1] == '=') {
(*s) += 2;
return TOK_GE;
}
if (*s + 3 < src_end && s[0][1] == '>' && s[0][2] == '>' &&
s[0][3] == '=') {
(*s) += 4;
return TOK_URSHIFT_ASSIGN;
}
if (*s + 2 < src_end && s[0][1] == '>' && s[0][2] == '>') {
(*s) += 3;
return TOK_URSHIFT;
}
return punct2(s, src_end, '>', TOK_RSHIFT, '=', TOK_RSHIFT_ASSIGN,
TOK_GT);
case '{':
(*s)++;
return TOK_OPEN_CURLY;
case '}':
(*s)++;
return TOK_CLOSE_CURLY;
case '(':
(*s)++;
return TOK_OPEN_PAREN;
case ')':
(*s)++;
return TOK_CLOSE_PAREN;
case '[':
(*s)++;
return TOK_OPEN_BRACKET;
case ']':
(*s)++;
return TOK_CLOSE_BRACKET;
case '.':
switch (*(*s + 1)) {
/* Numbers */
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
parse_number(p, s, n);
return TOK_NUMBER;
}
(*s)++;
return TOK_DOT;
case ';':
(*s)++;
return TOK_SEMICOLON;
case ':':
(*s)++;
return TOK_COLON;
case '?':
(*s)++;
return TOK_QUESTION;
case '~':
(*s)++;
return TOK_TILDA;
case ',':
(*s)++;
return TOK_COMMA;
default: {
/* Handle unicode variables */
Rune r;
if (chartorune(&r, *s) > 1 && isalpharune(r)) {
ident(s, src_end);
return TOK_IDENTIFIER;
}
return TOK_END_OF_INPUT;
}
}
}
#ifdef TEST_RUN
int main(void) {
const char *src =
"for (var fo++ = -1; /= <= 1.17; x<<) { == <<=, 'x')} "
"Infinity %=x<<=2";
const char *src_end = src + strlen(src);
enum v7_tok tok;
double num;
const char *p = src;
skip_to_next_tok(&src, src_end);
while ((tok = get_tok(&src, src_end, &num)) != TOK_END_OF_INPUT) {
printf("%d [%.*s]\n", tok, (int) (src - p), p);
skip_to_next_tok(&src, src_end);
p = src;
}
printf("%d [%.*s]\n", tok, (int) (src - p), p);
return 0;
}
#endif
#endif /* V7_NO_COMPILER */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/ast.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "common/cs_strtod.h" */
/* Amalgamated: #include "common/mbuf.h" */
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/varint.h" */
/* Amalgamated: #include "v7/src/ast.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* Amalgamated: #include "v7/src/string.h" */
/* Amalgamated: #include "common/str_util.h" */
#if !defined(V7_NO_COMPILER)
#ifdef V7_LARGE_AST
typedef uint32_t ast_skip_t;
#else
typedef uint16_t ast_skip_t;
#define AST_SKIP_MAX UINT16_MAX
#endif
#if !V7_DISABLE_AST_TAG_NAMES
#define AST_ENTRY(name, has_varint, has_inlined, num_skips, num_subtrees) \
{ (name), (has_varint), (has_inlined), (num_skips), (num_subtrees) }
#else
#define AST_ENTRY(name, has_varint, has_inlined, num_skips, num_subtrees) \
{ (has_varint), (has_inlined), (num_skips), (num_subtrees) }
#endif
/*
* The structure of AST nodes cannot be described in portable ANSI C,
* since they are variable length and packed (unaligned).
*
* Here each node's body is described with a pseudo-C structure notation.
* The pseudo type `child` represents a variable length byte sequence
* representing a fully serialized child node.
*
* `child body[]` represents a sequence of such subtrees.
*
* Pseudo-labels, such as `end:` represent the targets of skip fields
* with the same name (e.g. `ast_skip_t end`).
*
* Skips allow skipping a subtree or sequence of subtrees.
*
* Sequences of subtrees (i.e. `child []`) have to be terminated by a skip:
* they don't have a termination tag; all nodes whose position is before the
* skip are part of the sequence.
*
* Skips are encoded as network-byte-order 16-bit offsets counted from the
* first byte of the node body (i.e. not counting the tag itself).
* This currently limits the the maximum size of a function body to 64k.
*
* Notes:
*
* - Some nodes contain skips just for performance or because it simplifies
* the implementation of the interpreter. For example, technically, the FOR
* node doesn't need the `body` skip in order to be correctly traversed.
* However, being able to quickly skip the `iter` expression is useful
* also because it allows the interpreter to avoid traversing the expression
* subtree without evaluating it, just in order to find the next subtree.
*
* - The name `skip` was chosen because `offset` was too overloaded in general
* and label` is part of our domain model (i.e. JS has a label AST node type).
*
*
* So, each node has a mandatory field: *tag* (see `enum ast_tag`), and a
* number of optional fields. Whether the node has one or another optional
* field is determined by the *node descriptor*: `struct ast_node_def`. For
* each node type (i.e. for each element of `enum ast_tag`) there is a
* corresponding descriptor: see `ast_node_defs`.
*
* Optional fields are:
*
* - *varint*: a varint-encoded number. At the moment, this field is only used
* together with the next field: inlined data, and a varint number determines
* the inlined data length.
* - *inlined data*: a node-specific data. Size of it is determined by the
* previous field: varint.
* - *skips*: as explained above, these are integer offsets, encoded in
* big-endian. The number of skips is determined by the node descriptor
* (`struct ast_node_def`). The size of each skip is either 16 or 32 bits,
* depending on whether the macro `V7_LARGE_AST` is set. The order of skips
* is determined by the `enum ast_which_skip`. See examples below for
* clarity.
* - *subtrees*: child nodes. Some nodes have fixed number of child nodes; in
* this case, the descriptor has non-zero field `num_subtrees`. Otherwise,
* `num_subtrees` is zero, and consumer handles child nodes one by one, until
* the end of the node is reached (end of the node is determined by the `end`
* skip)
*
*
* Examples:
*
* Let's start from the very easy example script: "300;"
*
* Tree looks as follows:
*
* $ ./v7 -e "300;" -t
* SCRIPT
* /- [...] -/
* NUM 300
*
* Binary data is:
*
* $ ./v7 -e "300;" -b | od -A n -t x1
* 56 07 41 53 54 56 31 30 00 01 00 09 00 00 13 03
* 33 30 30
*
* Let's break it down and examine:
*
* - 56 07 41 53 54 56 31 30 00
* Just a format prefix:
* Null-terminated string: `"V\007ASTV10"` (see `BIN_AST_SIGNATURE`)
* - 01
* AST tag: `AST_SCRIPT`. As you see in `ast_node_defs` below, node of
* this type has neither *varint* nor *inlined data* fields, but it has
* 2 skips: `end` and `next`. `end` is a skip to the end of the current
* node (`SCRIPT`), and `next` will be explained below.
*
* The size of each skip depends on whether `V7_LARGE_AST` is defined.
* If it is, then size is 32 bit, otherwise it's 16 bit. In this
* example, we have 16-bit skips.
*
* The order of skips is determined by the `enum ast_which_skip`. If you
* check, you'll see that `AST_END_SKIP` is 0, and `AST_VAR_NEXT_SKIP`
* is 1. So, `end` skip fill be the first, and `next` will be the second:
* - 00 09
* `end` skip: 9 bytes. It's the size of the whole `SCRIPT` data. So, if
* we have an index of the `ASC_SCRIPT` tag, we can just add this skip
* (9) to this index, and therefore skip over the whole node.
* - 00 00
* `next` skip. `next` actually means "next variable node": since
* variables are hoisted in JavaScript, when the interpreter starts
* executing a top-level code or any function, it needs to get a list of
* all defined variables. The `SCRIPT` node has a "skip" to the first
* `var` or `function` declaration, which, in turn, has a "skip" to the
* next one, etc. If there is no next `var` declaration, then 0 is
* stored.
*
* In our super-simple script, we have no `var` neither `function`
* declarations, so, this skip is 0.
*
* Now, the body of our SCRIPT node goes, which contains child nodes:
*
* - 13
* AST tag: `AST_NUM`. Look at the `ast_node_defs`, and we'll see that
* nodes of this type don't have any skips, but they do have the varint
* field and the inlined data. Here we go:
* - 03
* Varint value: 3
* - 33 30 30
* UTF-8 string "300"
*
* ---------------
*
* The next example is a bit more interesting:
*
* var foo,
* bar = 1;
* foo = 3;
* var baz = 4;
*
* Tree:
*
* $ ./v7 -e 'var foo, bar=1; foo=3; var baz = 4;' -t
* SCRIPT
* /- [...] -/
* VAR
* /- [...] -/
* VAR_DECL foo
* NOP
* VAR_DECL bar
* NUM 1
* ASSIGN
* IDENT foo
* NUM 3
* VAR
* /- [...] -/
* VAR_DECL baz
* NUM 4
*
* Binary:
*
* $ ./v7 -e 'var foo, bar=1; foo=3; var baz = 4;' -b | od -A n -t x1
* 56 07 41 53 54 56 31 30 00 01 00 2d 00 05 02 00
* 12 00 1c 03 03 66 6f 6f 00 03 03 62 61 72 13 01
* 31 07 14 03 66 6f 6f 13 01 33 02 00 0c 00 00 03
* 03 62 61 7a 13 01 34
*
* Break it down:
*
* - 56 07 41 53 54 56 31 30 00
* `"V\007ASTV10"`
* - 01: AST tag: `AST_SCRIPT`
* - 00 2d: `end` skip: 0x2d = 45 bytes
* - 00 05: `next` skip: an offset from `AST_SCRIPT` byte to the first
* `var` declaration.
*
* Now, body of the SCRIPT node begins, which contains child nodes,
* and the first node is the var declaration `var foo, bar=1;`:
*
* SCRIPT node body: {{{
* - 02: AST tag: `AST_VAR`
* - 00 12: `end` skip: 18 bytes from tag byte to the end of current node
* - 00 1c: `next` skip: 28 bytes from tag byte to the next `var` node
*
* The VAR node contains arbitrary number of child nodes, so, consumer
* takes advantage of the `end` skip.
*
* VAR node body: {{{
* - 03: AST tag: `AST_VAR_DECL`
* - 03: Varint value: 3 (the length of the inlined data: a variable
*name)
* - 66 6f 6f: UTF-8 string: "foo"
* - 00: AST tag: `AST_NOP`
* Since we haven't provided any value to store into `foo`, NOP
* without any additional data is stored in AST.
*
* - 03: AST tag: `AST_VAR_DECL`
* - 03: Varint value: 3 (the length of the inlined data: a variable
*name)
* - 62 61 72: UTF-8 string: "bar"
* - 13: AST tag: `AST_NUM`
* - 01: Varint value: 1
* - 31: UTF-8 string "1"
* VAR body end }}}
*
* - 07: AST tag: `AST_ASSIGN`
*
* The ASSIGN node has fixed number of subrees: 2 (lvalue and rvalue),
* so there's no `end` skip.
*
* ASSIGN node body: {{{
* - 14: AST tag: `AST_IDENT`
* - 03: Varint value: 3
* - 66 6f 6f: UTF-8 string: "foo"
*
* - 13: AST tag: `AST_NUM`
* - 01: Varint value: 1
* - 33: UTF-8 string: "3"
* ASSIGN body end }}}
*
* - 02: AST tag: `AST_VAR`
* - 00 0c: `end` skip: 12 bytes from tag byte to the end of current node
* - 00 00: `next` skip: no more `var` nodes
*
* VAR node body: {{{
* - 03: AST tag: `AST_VAR_DECL`
* - 03: Varint value: 3 (the length of the inlined data: a variable
*name)
* - 62 61 7a: UTF-8 string: "baz"
* - 13: AST tag: `AST_NUM`
* - 01: Varint value: 1
* - 34: UTF-8 string "4"
* VAR body end }}}
* SCRIPT body end }}}
*
* --------------------------
*/
const struct ast_node_def ast_node_defs[] = {
AST_ENTRY("NOP", 0, 0, 0, 0), /* struct {} */
/*
* struct {
* ast_skip_t end;
* ast_skip_t first_var;
* child body[];
* end:
* }
*/
AST_ENTRY("SCRIPT", 0, 0, 2, 0),
/*
* struct {
* ast_skip_t end;
* ast_skip_t next;
* child decls[];
* end:
* }
*/
AST_ENTRY("VAR", 0, 0, 2, 0),
/*
* struct {
* varint len;
* char name[len];
* child expr;
* }
*/
AST_ENTRY("VAR_DECL", 1, 1, 0, 1),
/*
* struct {
* varint len;
* char name[len];
* child expr;
* }
*/
AST_ENTRY("FUNC_DECL", 1, 1, 0, 1),
/*
* struct {
* ast_skip_t end;
* ast_skip_t end_true;
* child cond;
* child iftrue[];
* end_true:
* child iffalse[];
* end:
* }
*/
AST_ENTRY("IF", 0, 0, 2, 1),
/*
* TODO(mkm) distinguish function expressions
* from function statements.
* Function statements behave like vars and need a
* next field for hoisting.
* We can also ignore the name for function expressions
* if it's only needed for debugging.
*
* struct {
* ast_skip_t end;
* ast_skip_t first_var;
* ast_skip_t body;
* child name;
* child params[];
* body:
* child body[];
* end:
* }
*/
AST_ENTRY("FUNC", 0, 0, 3, 1),
AST_ENTRY("ASSIGN", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("REM_ASSIGN", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("MUL_ASSIGN", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("DIV_ASSIGN", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("XOR_ASSIGN", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("PLUS_ASSIGN", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("MINUS_ASSIGN", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("OR_ASSIGN", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("AND_ASSIGN", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("LSHIFT_ASSIGN", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("RSHIFT_ASSIGN", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("URSHIFT_ASSIGN", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("NUM", 1, 1, 0, 0), /* struct { varint len, char s[len]; } */
AST_ENTRY("IDENT", 1, 1, 0, 0), /* struct { varint len, char s[len]; } */
AST_ENTRY("STRING", 1, 1, 0, 0), /* struct { varint len, char s[len]; } */
AST_ENTRY("REGEX", 1, 1, 0, 0), /* struct { varint len, char s[len]; } */
AST_ENTRY("LABEL", 1, 1, 0, 0), /* struct { varint len, char s[len]; } */
/*
* struct {
* ast_skip_t end;
* child body[];
* end:
* }
*/
AST_ENTRY("SEQ", 0, 0, 1, 0),
/*
* struct {
* ast_skip_t end;
* child cond;
* child body[];
* end:
* }
*/
AST_ENTRY("WHILE", 0, 0, 1, 1),
/*
* struct {
* ast_skip_t end;
* ast_skip_t cond;
* child body[];
* cond:
* child cond;
* end:
* }
*/
AST_ENTRY("DOWHILE", 0, 0, 2, 0),
/*
* struct {
* ast_skip_t end;
* ast_skip_t body;
* child init;
* child cond;
* child iter;
* body:
* child body[];
* end:
* }
*/
AST_ENTRY("FOR", 0, 0, 2, 3),
/*
* struct {
* ast_skip_t end;
* ast_skip_t dummy; // allows to quickly promote a for to a for in
* child var;
* child expr;
* child dummy;
* child body[];
* end:
* }
*/
AST_ENTRY("FOR_IN", 0, 0, 2, 3),
AST_ENTRY("COND", 0, 0, 0, 3), /* struct { child cond, iftrue, iffalse; } */
AST_ENTRY("DEBUGGER", 0, 0, 0, 0), /* struct {} */
AST_ENTRY("BREAK", 0, 0, 0, 0), /* struct {} */
/*
* struct {
* child label; // TODO(mkm): inline
* }
*/
AST_ENTRY("LAB_BREAK", 0, 0, 0, 1),
AST_ENTRY("CONTINUE", 0, 0, 0, 0), /* struct {} */
/*
* struct {
* child label; // TODO(mkm): inline
* }
*/
AST_ENTRY("LAB_CONTINUE", 0, 0, 0, 1),
AST_ENTRY("RETURN", 0, 0, 0, 0), /* struct {} */
AST_ENTRY("VAL_RETURN", 0, 0, 0, 1), /* struct { child expr; } */
AST_ENTRY("THROW", 0, 0, 0, 1), /* struct { child expr; } */
/*
* struct {
* ast_skip_t end;
* ast_skip_t catch;
* ast_skip_t finally;
* child try[];
* catch:
* child var; // TODO(mkm): inline
* child catch[];
* finally:
* child finally[];
* end:
* }
*/
AST_ENTRY("TRY", 0, 0, 3, 1),
/*
* struct {
* ast_skip_t end;
* ast_skip_t def;
* child expr;
* child cases[];
* def:
* child default?; // optional
* end:
* }
*/
AST_ENTRY("SWITCH", 0, 0, 2, 1),
/*
* struct {
* ast_skip_t end;
* child val;
* child stmts[];
* end:
* }
*/
AST_ENTRY("CASE", 0, 0, 1, 1),
/*
* struct {
* ast_skip_t end;
* child stmts[];
* end:
* }
*/
AST_ENTRY("DEFAULT", 0, 0, 1, 0),
/*
* struct {
* ast_skip_t end;
* child expr;
* child body[];
* end:
* }
*/
AST_ENTRY("WITH", 0, 0, 1, 1),
AST_ENTRY("LOG_OR", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("LOG_AND", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("OR", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("XOR", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("AND", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("EQ", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("EQ_EQ", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("NE", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("NE_NE", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("LE", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("LT", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("GE", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("GT", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("IN", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("INSTANCEOF", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("LSHIFT", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("RSHIFT", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("URSHIFT", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("ADD", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("SUB", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("REM", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("MUL", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("DIV", 0, 0, 0, 2), /* struct { child left, right; } */
AST_ENTRY("POS", 0, 0, 0, 1), /* struct { child expr; } */
AST_ENTRY("NEG", 0, 0, 0, 1), /* struct { child expr; } */
AST_ENTRY("NOT", 0, 0, 0, 1), /* struct { child expr; } */
AST_ENTRY("LOGICAL_NOT", 0, 0, 0, 1), /* struct { child expr; } */
AST_ENTRY("VOID", 0, 0, 0, 1), /* struct { child expr; } */
AST_ENTRY("DELETE", 0, 0, 0, 1), /* struct { child expr; } */
AST_ENTRY("TYPEOF", 0, 0, 0, 1), /* struct { child expr; } */
AST_ENTRY("PREINC", 0, 0, 0, 1), /* struct { child expr; } */
AST_ENTRY("PREDEC", 0, 0, 0, 1), /* struct { child expr; } */
AST_ENTRY("POSTINC", 0, 0, 0, 1), /* struct { child expr; } */
AST_ENTRY("POSTDEC", 0, 0, 0, 1), /* struct { child expr; } */
/*
* struct {
* varint len;
* char ident[len];
* child expr;
* }
*/
AST_ENTRY("MEMBER", 1, 1, 0, 1),
/*
* struct {
* child expr;
* child index;
* }
*/
AST_ENTRY("INDEX", 0, 0, 0, 2),
/*
* struct {
* ast_skip_t end;
* child expr;
* child args[];
* end:
* }
*/
AST_ENTRY("CALL", 0, 0, 1, 1),
/*
* struct {
* ast_skip_t end;
* child expr;
* child args[];
* end:
* }
*/
AST_ENTRY("NEW", 0, 0, 1, 1),
/*
* struct {
* ast_skip_t end;
* child elements[];
* end:
* }
*/
AST_ENTRY("ARRAY", 0, 0, 1, 0),
/*
* struct {
* ast_skip_t end;
* child props[];
* end:
* }
*/
AST_ENTRY("OBJECT", 0, 0, 1, 0),
/*
* struct {
* varint len;
* char name[len];
* child expr;
* }
*/
AST_ENTRY("PROP", 1, 1, 0, 1),
/*
* struct {
* child func;
* }
*/
AST_ENTRY("GETTER", 0, 0, 0, 1),
/*
* struct {
* child func;
* end:
* }
*/
AST_ENTRY("SETTER", 0, 0, 0, 1),
AST_ENTRY("THIS", 0, 0, 0, 0), /* struct {} */
AST_ENTRY("TRUE", 0, 0, 0, 0), /* struct {} */
AST_ENTRY("FALSE", 0, 0, 0, 0), /* struct {} */
AST_ENTRY("NULL", 0, 0, 0, 0), /* struct {} */
AST_ENTRY("UNDEF", 0, 0, 0, 0), /* struct {} */
AST_ENTRY("USE_STRICT", 0, 0, 0, 0), /* struct {} */
};
/*
* A flag which is used to mark node's tag byte if the node has line number
* data encoded (varint after skips). See `ast_get_line_no()`.
*/
#define AST_TAG_LINENO_PRESENT 0x80
V7_STATIC_ASSERT(AST_MAX_TAG < 256, ast_tag_should_fit_in_char);
V7_STATIC_ASSERT(AST_MAX_TAG == ARRAY_SIZE(ast_node_defs), bad_node_defs);
V7_STATIC_ASSERT(AST_MAX_TAG <= AST_TAG_LINENO_PRESENT, bad_AST_LINE_NO);
#if V7_ENABLE_FOOTPRINT_REPORT
const size_t ast_node_defs_size = sizeof(ast_node_defs);
const size_t ast_node_defs_count = ARRAY_SIZE(ast_node_defs);
#endif
/*
* Converts a given byte `t` (which should be read from the AST data buffer)
* into `enum ast_tag`. This function is needed because tag might be marked
* with the `AST_TAG_LINENO_PRESENT` flag; the returned tag is always unmarked,
* and if the flag was indeed set, `lineno_present` is set to 1; otherwise
* it is set to 0.
*
* `lineno_present` is allowed to be NULL, if the caller doesn't care of the
* line number presence.
*/
static enum ast_tag uint8_to_tag(uint8_t t, uint8_t *lineno_present) {
if (t & AST_TAG_LINENO_PRESENT) {
t &= ~AST_TAG_LINENO_PRESENT;
if (lineno_present != NULL) {
*lineno_present = 1;
}
} else if (lineno_present != NULL) {
*lineno_present = 0;
}
return (enum ast_tag) t;
}
V7_PRIVATE ast_off_t
ast_insert_node(struct ast *a, ast_off_t pos, enum ast_tag tag) {
uint8_t t = (uint8_t) tag;
const struct ast_node_def *d = &ast_node_defs[tag];
ast_off_t cur = pos;
assert(tag < AST_MAX_TAG);
mbuf_insert(&a->mbuf, cur, (char *) &t, sizeof(t));
cur += sizeof(t);
mbuf_insert(&a->mbuf, cur, NULL, sizeof(ast_skip_t) * d->num_skips);
cur += sizeof(ast_skip_t) * d->num_skips;
if (d->num_skips) {
ast_set_skip(a, pos + 1, AST_END_SKIP);
}
return pos + 1;
}
V7_PRIVATE void ast_modify_tag(struct ast *a, ast_off_t tag_off,
enum ast_tag tag) {
a->mbuf.buf[tag_off] = tag | (a->mbuf.buf[tag_off] & 0x80);
}
#if !V7_DISABLE_LINE_NUMBERS
V7_PRIVATE void ast_add_line_no(struct ast *a, ast_off_t tag_off, int line_no) {
ast_off_t ln_off = tag_off + 1 /* tag byte */;
int llen = calc_llen(line_no);
ast_move_to_inlined_data(a, &ln_off);
mbuf_insert(&a->mbuf, ln_off, NULL, llen);
encode_varint(line_no, (unsigned char *) (a->mbuf.buf + ln_off));
assert(a->mbuf.buf[tag_off] < AST_MAX_TAG);
a->mbuf.buf[tag_off] |= AST_TAG_LINENO_PRESENT;
}
#endif
V7_PRIVATE ast_off_t
ast_set_skip(struct ast *a, ast_off_t pos, enum ast_which_skip skip) {
return ast_modify_skip(a, pos, a->mbuf.len, skip);
}
V7_PRIVATE ast_off_t ast_modify_skip(struct ast *a, ast_off_t pos,
ast_off_t where,
enum ast_which_skip skip) {
uint8_t *p = (uint8_t *) a->mbuf.buf + pos + skip * sizeof(ast_skip_t);
ast_skip_t delta = where - pos;
#ifndef NDEBUG
enum ast_tag tag = uint8_to_tag(*(a->mbuf.buf + pos - 1), NULL);
const struct ast_node_def *def = &ast_node_defs[tag];
#endif
assert(pos <= where);
#ifndef V7_LARGE_AST
/* the value of delta overflowed, therefore the ast is not useable */
if (where - pos > AST_SKIP_MAX) {
a->has_overflow = 1;
}
#endif
/* assertion, to be optimizable out */
assert((int) skip < def->num_skips);
#ifdef V7_LARGE_AST
p[0] = delta >> 24;
p[1] = delta >> 16 & 0xff;
p[2] = delta >> 8 & 0xff;
p[3] = delta & 0xff;
#else
p[0] = delta >> 8;
p[1] = delta & 0xff;
#endif
return where;
}
V7_PRIVATE ast_off_t
ast_get_skip(struct ast *a, ast_off_t pos, enum ast_which_skip skip) {
uint8_t *p;
assert(pos + skip * sizeof(ast_skip_t) < a->mbuf.len);
p = (uint8_t *) a->mbuf.buf + pos + skip * sizeof(ast_skip_t);
#ifdef V7_LARGE_AST
return pos + (p[3] | p[2] << 8 | p[1] << 16 | p[0] << 24);
#else
return pos + (p[1] | p[0] << 8);
#endif
}
V7_PRIVATE enum ast_tag ast_fetch_tag(struct ast *a, ast_off_t *ppos) {
enum ast_tag ret;
assert(*ppos < a->mbuf.len);
ret = uint8_to_tag(*(a->mbuf.buf + (*ppos)++), NULL);
return ret;
}
V7_PRIVATE void ast_move_to_children(struct ast *a, ast_off_t *ppos) {
enum ast_tag tag = uint8_to_tag(*(a->mbuf.buf + *ppos - 1), NULL);
const struct ast_node_def *def = &ast_node_defs[tag];
assert(*ppos - 1 < a->mbuf.len);
ast_move_to_inlined_data(a, ppos);
/* skip varint + inline data, if present */
if (def->has_varint) {
int llen;
size_t slen = decode_varint((unsigned char *) a->mbuf.buf + *ppos, &llen);
*ppos += llen;
if (def->has_inlined) {
*ppos += slen;
}
}
}
V7_PRIVATE ast_off_t ast_insert_inlined_node(struct ast *a, ast_off_t pos,
enum ast_tag tag, const char *name,
size_t len) {
const struct ast_node_def *d = &ast_node_defs[tag];
ast_off_t offset = ast_insert_node(a, pos, tag);
assert(d->has_inlined);
embed_string(&a->mbuf, offset + sizeof(ast_skip_t) * d->num_skips, name, len,
EMBSTR_UNESCAPE);
return offset;
}
V7_PRIVATE int ast_get_line_no(struct ast *a, ast_off_t pos) {
/*
* by default we'll return 0, meaning that the AST node does not contain line
* number data
*/
int ret = 0;
#if !V7_DISABLE_LINE_NUMBERS
uint8_t lineno_present;
enum ast_tag tag = uint8_to_tag(*(a->mbuf.buf + pos - 1), &lineno_present);
if (lineno_present) {
/* line number is present, so, let's decode it */
int llen;
/* skip skips */
pos += ast_node_defs[tag].num_skips * sizeof(ast_skip_t);
/* get line number */
ret = decode_varint((unsigned char *) a->mbuf.buf + pos, &llen);
}
#else
(void) a;
(void) pos;
#endif
return ret;
}
V7_PRIVATE void ast_move_to_inlined_data(struct ast *a, ast_off_t *ppos) {
uint8_t lineno_present = 0;
enum ast_tag tag = uint8_to_tag(*(a->mbuf.buf + *ppos - 1), &lineno_present);
const struct ast_node_def *def = &ast_node_defs[tag];
assert(*ppos - 1 < a->mbuf.len);
/* skip skips */
*ppos += def->num_skips * sizeof(ast_skip_t);
/* skip line_no, if present */
if (lineno_present) {
int llen;
int line_no = decode_varint((unsigned char *) a->mbuf.buf + *ppos, &llen);
*ppos += llen;
(void) line_no;
}
}
V7_PRIVATE char *ast_get_inlined_data(struct ast *a, ast_off_t pos, size_t *n) {
int llen;
assert(pos < a->mbuf.len);
ast_move_to_inlined_data(a, &pos);
*n = decode_varint((unsigned char *) a->mbuf.buf + pos, &llen);
return a->mbuf.buf + pos + llen;
}
V7_PRIVATE double ast_get_num(struct ast *a, ast_off_t pos) {
double ret;
char *str;
size_t str_len;
char buf[12];
char *p = buf;
str = ast_get_inlined_data(a, pos, &str_len);
assert(str + str_len <= a->mbuf.buf + a->mbuf.len);
if (str_len > sizeof(buf) - 1) {
p = (char *) malloc(str_len + 1);
}
strncpy(p, str, str_len);
p[str_len] = '\0';
ret = cs_strtod(p, NULL);
if (p != buf) free(p);
return ret;
}
#ifndef NO_LIBC
static void comment_at_depth(FILE *fp, const char *fmt, int depth, ...) {
int i;
STATIC char buf[256];
va_list ap;
va_start(ap, depth);
c_vsnprintf(buf, sizeof(buf), fmt, ap);
for (i = 0; i < depth; i++) {
fprintf(fp, " ");
}
fprintf(fp, "/* [%s] */\n", buf);
}
#endif
V7_PRIVATE void ast_skip_tree(struct ast *a, ast_off_t *ppos) {
enum ast_tag tag = ast_fetch_tag(a, ppos);
const struct ast_node_def *def = &ast_node_defs[tag];
ast_off_t skips = *ppos;
int i;
ast_move_to_children(a, ppos);
for (i = 0; i < def->num_subtrees; i++) {
ast_skip_tree(a, ppos);
}
if (def->num_skips > AST_END_SKIP) {
ast_off_t end = ast_get_skip(a, skips, AST_END_SKIP);
while (*ppos < end) {
ast_skip_tree(a, ppos);
}
}
}
#ifndef NO_LIBC
V7_PRIVATE void ast_dump_tree(FILE *fp, struct ast *a, ast_off_t *ppos,
int depth) {
enum ast_tag tag = ast_fetch_tag(a, ppos);
const struct ast_node_def *def = &ast_node_defs[tag];
ast_off_t skips = *ppos;
size_t slen;
int i, llen;
for (i = 0; i < depth; i++) {
fprintf(fp, " ");
}
#if !V7_DISABLE_AST_TAG_NAMES
fprintf(fp, "%s", def->name);
#else
fprintf(fp, "TAG_%d", tag);
#endif
if (def->has_inlined) {
ast_off_t pos_tmp = *ppos;
ast_move_to_inlined_data(a, &pos_tmp);
slen = decode_varint((unsigned char *) a->mbuf.buf + pos_tmp, &llen);
fprintf(fp, " %.*s\n", (int) slen, a->mbuf.buf + pos_tmp + llen);
} else {
fprintf(fp, "\n");
}
ast_move_to_children(a, ppos);
for (i = 0; i < def->num_subtrees; i++) {
ast_dump_tree(fp, a, ppos, depth + 1);
}
if (ast_node_defs[tag].num_skips) {
/*
* first skip always encodes end of the last children sequence.
* so unless we care how the subtree sequences are grouped together
* (and we currently don't) we can just read until the end of that skip.
*/
ast_off_t end = ast_get_skip(a, skips, AST_END_SKIP);
comment_at_depth(fp, "...", depth + 1);
while (*ppos < end) {
int s;
for (s = ast_node_defs[tag].num_skips - 1; s > 0; s--) {
if (*ppos == ast_get_skip(a, skips, (enum ast_which_skip) s)) {
comment_at_depth(fp, "%d ->", depth + 1, s);
break;
}
}
ast_dump_tree(fp, a, ppos, depth + 1);
}
}
}
#endif
V7_PRIVATE void ast_init(struct ast *ast, size_t len) {
mbuf_init(&ast->mbuf, len);
ast->refcnt = 0;
ast->has_overflow = 0;
}
V7_PRIVATE void ast_optimize(struct ast *ast) {
/*
* leave one trailing byte so that literals can be
* null terminated on the fly.
*/
mbuf_resize(&ast->mbuf, ast->mbuf.len + 1);
}
V7_PRIVATE void ast_free(struct ast *ast) {
mbuf_free(&ast->mbuf);
ast->refcnt = 0;
ast->has_overflow = 0;
}
V7_PRIVATE void release_ast(struct v7 *v7, struct ast *a) {
(void) v7;
if (a->refcnt != 0) a->refcnt--;
if (a->refcnt == 0) {
#if V7_ENABLE__Memory__stats
v7->function_arena_ast_size -= a->mbuf.size;
#endif
ast_free(a);
free(a);
}
}
#endif /* V7_NO_COMPILER */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/bcode.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/bcode.h" */
/* Amalgamated: #include "v7/src/varint.h" */
/* Amalgamated: #include "v7/src/exceptions.h" */
/* Amalgamated: #include "v7/src/gc.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* Amalgamated: #include "v7/src/regexp.h" */
/* Amalgamated: #include "v7/src/function.h" */
/* Amalgamated: #include "v7/src/util.h" */
/* Amalgamated: #include "v7/src/shdata.h" */
/*
* TODO(dfrank): implement `bcode_serialize_*` more generically, so that they
* can write to buffer instead of a `FILE`. Then, remove a need for mmap here.
*/
#if CS_PLATFORM == CS_P_UNIX
#include
#endif
#if defined(V7_BCODE_DUMP) || defined(V7_BCODE_TRACE)
/* clang-format off */
static const char *op_names[] = {
"DROP",
"DUP",
"2DUP",
"SWAP",
"STASH",
"UNSTASH",
"SWAP_DROP",
"PUSH_UNDEFINED",
"PUSH_NULL",
"PUSH_THIS",
"PUSH_TRUE",
"PUSH_FALSE",
"PUSH_ZERO",
"PUSH_ONE",
"PUSH_LIT",
"NOT",
"LOGICAL_NOT",
"NEG",
"POS",
"ADD",
"SUB",
"REM",
"MUL",
"DIV",
"LSHIFT",
"RSHIFT",
"URSHIFT",
"OR",
"XOR",
"AND",
"EQ_EQ",
"EQ",
"NE",
"NE_NE",
"LT",
"LE",
"GT",
"GE",
"INSTANCEOF",
"TYPEOF",
"IN",
"GET",
"SET",
"SET_VAR",
"GET_VAR",
"SAFE_GET_VAR",
"JMP",
"JMP_TRUE",
"JMP_FALSE",
"JMP_TRUE_DROP",
"JMP_IF_CONTINUE",
"CREATE_OBJ",
"CREATE_ARR",
"PUSH_PROP_ITER_CTX",
"NEXT_PROP",
"FUNC_LIT",
"CALL",
"NEW",
"CHECK_CALL",
"RET",
"DELETE",
"DELETE_VAR",
"TRY_PUSH_CATCH",
"TRY_PUSH_FINALLY",
"TRY_PUSH_LOOP",
"TRY_PUSH_SWITCH",
"TRY_POP",
"AFTER_FINALLY",
"THROW",
"BREAK",
"CONTINUE",
"ENTER_CATCH",
"EXIT_CATCH",
};
/* clang-format on */
V7_STATIC_ASSERT(OP_MAX == ARRAY_SIZE(op_names), bad_op_names);
V7_STATIC_ASSERT(OP_MAX <= _OP_LINE_NO, bad_OP_LINE_NO);
#endif
static void bcode_serialize_func(struct v7 *v7, struct bcode *bcode, FILE *out);
static size_t bcode_ops_append(struct bcode_builder *bbuilder, const void *buf,
size_t len) {
size_t ret;
#if V7_ENABLE__Memory__stats
bbuilder->v7->bcode_ops_size -= bbuilder->ops.len;
#endif
ret = mbuf_append(&bbuilder->ops, buf, len);
#if V7_ENABLE__Memory__stats
bbuilder->v7->bcode_ops_size += bbuilder->ops.len;
#endif
return ret;
}
/*
* Initialize bcode builder. The `bcode` should be already initialized by the
* caller, and should be empty (i.e. should not own any ops, literals, etc)
*
* TODO(dfrank) : probably make `bcode_builder_init()` to initialize `bcode`
* as well
*/
V7_PRIVATE void bcode_builder_init(struct v7 *v7,
struct bcode_builder *bbuilder,
struct bcode *bcode) {
memset(bbuilder, 0x00, sizeof(*bbuilder));
bbuilder->v7 = v7;
bbuilder->bcode = bcode;
mbuf_init(&bbuilder->ops, 0);
mbuf_init(&bbuilder->lit, 0);
}
/*
* Finalize bcode builder: propagate data to the bcode and transfer the
* ownership from builder to bcode
*/
V7_PRIVATE void bcode_builder_finalize(struct bcode_builder *bbuilder) {
mbuf_trim(&bbuilder->ops);
bbuilder->bcode->ops.p = bbuilder->ops.buf;
bbuilder->bcode->ops.len = bbuilder->ops.len;
mbuf_init(&bbuilder->ops, 0);
mbuf_trim(&bbuilder->lit);
bbuilder->bcode->lit.p = bbuilder->lit.buf;
bbuilder->bcode->lit.len = bbuilder->lit.len;
mbuf_init(&bbuilder->lit, 0);
memset(bbuilder, 0x00, sizeof(*bbuilder));
}
#if defined(V7_BCODE_DUMP) || defined(V7_BCODE_TRACE)
V7_PRIVATE void dump_op(struct v7 *v7, FILE *f, struct bcode *bcode,
char **ops) {
char *p = *ops;
assert(*p < OP_MAX);
fprintf(f, "%zu: %s", (size_t)(p - bcode->ops.p), op_names[(uint8_t) *p]);
switch (*p) {
case OP_PUSH_LIT:
case OP_SAFE_GET_VAR:
case OP_GET_VAR:
case OP_SET_VAR: {
size_t idx = bcode_get_varint(&p);
fprintf(f, "(%lu): ", (unsigned long) idx);
v7_fprint(f, v7, ((val_t *) bcode->lit.p)[idx]);
break;
}
case OP_CALL:
case OP_NEW:
p++;
fprintf(f, "(%d)", *p);
break;
case OP_JMP:
case OP_JMP_FALSE:
case OP_JMP_TRUE:
case OP_JMP_TRUE_DROP:
case OP_JMP_IF_CONTINUE:
case OP_TRY_PUSH_CATCH:
case OP_TRY_PUSH_FINALLY:
case OP_TRY_PUSH_LOOP:
case OP_TRY_PUSH_SWITCH: {
bcode_off_t target;
p++;
memcpy(&target, p, sizeof(target));
fprintf(f, "(%lu)", (unsigned long) target);
p += sizeof(target) - 1;
break;
}
default:
break;
}
fprintf(f, "\n");
*ops = p;
}
#endif
#ifdef V7_BCODE_DUMP
V7_PRIVATE void dump_bcode(struct v7 *v7, FILE *f, struct bcode *bcode) {
char *p = bcode_end_names(bcode->ops.p, bcode->names_cnt);
char *end = bcode->ops.p + bcode->ops.len;
for (; p < end; p++) {
dump_op(v7, f, bcode, &p);
}
}
#endif
V7_PRIVATE void bcode_init(struct bcode *bcode, uint8_t strict_mode,
void *filename, uint8_t filename_in_rom) {
memset(bcode, 0x00, sizeof(*bcode));
bcode->refcnt = 0;
bcode->args_cnt = 0;
bcode->strict_mode = strict_mode;
#if !V7_DISABLE_FILENAMES
bcode->filename = filename;
bcode->filename_in_rom = filename_in_rom;
#else
(void) filename;
(void) filename_in_rom;
#endif
}
V7_PRIVATE void bcode_free(struct v7 *v7, struct bcode *bcode) {
(void) v7;
#if V7_ENABLE__Memory__stats
if (!bcode->ops_in_rom) {
v7->bcode_ops_size -= bcode->ops.len;
}
v7->bcode_lit_total_size -= bcode->lit.len;
if (bcode->deserialized) {
v7->bcode_lit_deser_size -= bcode->lit.len;
}
#endif
if (!bcode->ops_in_rom) {
free(bcode->ops.p);
}
memset(&bcode->ops, 0x00, sizeof(bcode->ops));
free(bcode->lit.p);
memset(&bcode->lit, 0x00, sizeof(bcode->lit));
#if !V7_DISABLE_FILENAMES
if (!bcode->filename_in_rom && bcode->filename != NULL) {
shdata_release((struct shdata *) bcode->filename);
bcode->filename = NULL;
}
#endif
bcode->refcnt = 0;
}
V7_PRIVATE void retain_bcode(struct v7 *v7, struct bcode *b) {
(void) v7;
if (!b->frozen) {
b->refcnt++;
}
}
V7_PRIVATE void release_bcode(struct v7 *v7, struct bcode *b) {
(void) v7;
if (b->frozen) return;
assert(b->refcnt > 0);
if (b->refcnt != 0) b->refcnt--;
if (b->refcnt == 0) {
bcode_free(v7, b);
free(b);
}
}
#if !V7_DISABLE_FILENAMES
V7_PRIVATE const char *bcode_get_filename(struct bcode *bcode) {
const char *ret = NULL;
if (bcode->filename_in_rom) {
ret = (const char *) bcode->filename;
} else if (bcode->filename != NULL) {
ret = (const char *) shdata_get_payload((struct shdata *) bcode->filename);
}
return ret;
}
#endif
V7_PRIVATE void bcode_copy_filename_from(struct bcode *dst, struct bcode *src) {
#if !V7_DISABLE_FILENAMES
dst->filename_in_rom = src->filename_in_rom;
dst->filename = src->filename;
if (src->filename != NULL && !src->filename_in_rom) {
shdata_retain((struct shdata *) dst->filename);
}
#else
(void) dst;
(void) src;
#endif
}
V7_PRIVATE void bcode_op(struct bcode_builder *bbuilder, uint8_t op) {
bcode_ops_append(bbuilder, &op, 1);
}
#if !V7_DISABLE_LINE_NUMBERS
V7_PRIVATE void bcode_append_lineno(struct bcode_builder *bbuilder,
int line_no) {
int offset = bbuilder->ops.len;
bcode_add_varint(bbuilder, (line_no << 1) | 1);
bbuilder->ops.buf[offset] = msb_lsb_swap(bbuilder->ops.buf[offset]);
assert(bbuilder->ops.buf[offset] & _OP_LINE_NO);
}
#endif
/*
* Appends varint-encoded integer to the `ops` mbuf
*/
V7_PRIVATE void bcode_add_varint(struct bcode_builder *bbuilder, size_t value) {
int k = calc_llen(value); /* Calculate how many bytes length takes */
int offset = bbuilder->ops.len;
/* Allocate buffer */
bcode_ops_append(bbuilder, NULL, k);
/* Write value */
encode_varint(value, (unsigned char *) bbuilder->ops.buf + offset);
}
V7_PRIVATE size_t bcode_get_varint(char **ops) {
size_t ret = 0;
int len = 0;
(*ops)++;
ret = decode_varint((unsigned char *) *ops, &len);
*ops += len - 1;
return ret;
}
static int bcode_is_inline_string(struct v7 *v7, val_t val) {
uint64_t tag = val & V7_TAG_MASK;
if (v7->is_precompiling && v7_is_string(val)) {
return 1;
}
return tag == V7_TAG_STRING_I || tag == V7_TAG_STRING_5;
}
static int bcode_is_inline_func(struct v7 *v7, val_t val) {
return (v7->is_precompiling && is_js_function(val));
}
static int bcode_is_inline_regexp(struct v7 *v7, val_t val) {
return (v7->is_precompiling && v7_is_regexp(v7, val));
}
V7_PRIVATE lit_t bcode_add_lit(struct bcode_builder *bbuilder, val_t val) {
lit_t lit;
memset(&lit, 0, sizeof(lit));
if (bcode_is_inline_string(bbuilder->v7, val) ||
bcode_is_inline_func(bbuilder->v7, val) || v7_is_number(val) ||
bcode_is_inline_regexp(bbuilder->v7, val)) {
/* literal should be inlined (it's `bcode_op_lit()` who does this) */
lit.mode = LIT_MODE__INLINED;
lit.v.inline_val = val;
} else {
/* literal will now be added to the literal table */
lit.mode = LIT_MODE__TABLE;
lit.v.lit_idx = bbuilder->lit.len / sizeof(val);
#if V7_ENABLE__Memory__stats
bbuilder->v7->bcode_lit_total_size -= bbuilder->lit.len;
if (bbuilder->bcode->deserialized) {
bbuilder->v7->bcode_lit_deser_size -= bbuilder->lit.len;
}
#endif
mbuf_append(&bbuilder->lit, &val, sizeof(val));
/*
* immediately propagate current lit buffer to the bcode, so that GC will
* be aware of it
*/
bbuilder->bcode->lit.p = bbuilder->lit.buf;
bbuilder->bcode->lit.len = bbuilder->lit.len;
#if V7_ENABLE__Memory__stats
bbuilder->v7->bcode_lit_total_size += bbuilder->lit.len;
if (bbuilder->bcode->deserialized) {
bbuilder->v7->bcode_lit_deser_size += bbuilder->lit.len;
}
#endif
}
return lit;
}
#if 0
V7_PRIVATE v7_val_t bcode_get_lit(struct bcode *bcode, size_t idx) {
val_t ret;
memcpy(&ret, bcode->lit.p + (size_t) idx * sizeof(ret), sizeof(ret));
return ret;
}
#endif
static const char *bcode_deserialize_func(struct v7 *v7, struct bcode *bcode,
const char *data);
V7_PRIVATE v7_val_t
bcode_decode_lit(struct v7 *v7, struct bcode *bcode, char **ops) {
struct v7_vec *vec = &bcode->lit;
size_t idx = bcode_get_varint(ops);
switch (idx) {
case BCODE_INLINE_STRING_TYPE_TAG: {
val_t res;
size_t len = bcode_get_varint(ops);
res = v7_mk_string(
v7, (const char *) *ops + 1 /*skip BCODE_INLINE_STRING_TYPE_TAG*/,
len, !bcode->ops_in_rom);
*ops += len + 1;
return res;
}
case BCODE_INLINE_NUMBER_TYPE_TAG: {
val_t res;
memcpy(&res, *ops + 1 /*skip BCODE_INLINE_NUMBER_TYPE_TAG*/, sizeof(res));
*ops += sizeof(res);
return res;
}
case BCODE_INLINE_FUNC_TYPE_TAG: {
/*
* Create half-done function: without scope but _with_ prototype. Scope
* will be set by `bcode_instantiate_function()`.
*
* The fact that the prototype is already set will make
* `bcode_instantiate_function()` just set scope on this function,
* instead of creating a new one.
*/
val_t res = mk_js_function(v7, NULL, v7_mk_object(v7));
/* Create bcode in this half-done function */
struct v7_js_function *func = get_js_function_struct(res);
func->bcode = (struct bcode *) calloc(1, sizeof(*func->bcode));
bcode_init(func->bcode, bcode->strict_mode, NULL /* will be set below */,
0);
bcode_copy_filename_from(func->bcode, bcode);
retain_bcode(v7, func->bcode);
/* deserialize the function's bcode from `ops` */
*ops = (char *) bcode_deserialize_func(
v7, func->bcode, *ops + 1 /*skip BCODE_INLINE_FUNC_TYPE_TAG*/);
/* decrement *ops, because it will be incremented by `eval_bcode` soon */
*ops -= 1;
return res;
}
case BCODE_INLINE_REGEXP_TYPE_TAG: {
#if V7_ENABLE__RegExp
enum v7_err rcode = V7_OK;
val_t res;
size_t len_src, len_flags;
char *buf_src, *buf_flags;
len_src = bcode_get_varint(ops);
buf_src = *ops + 1;
*ops += len_src + 1 /* nul term */;
len_flags = bcode_get_varint(ops);
buf_flags = *ops + 1;
*ops += len_flags + 1 /* nul term */;
rcode = v7_mk_regexp(v7, buf_src, len_src, buf_flags, len_flags, &res);
assert(rcode == V7_OK);
(void) rcode;
return res;
#else
fprintf(stderr, "Firmware is built without -DV7_ENABLE__RegExp\n");
abort();
#endif
}
default:
return ((val_t *) vec->p)[idx - BCODE_MAX_INLINE_TYPE_TAG];
}
}
V7_PRIVATE void bcode_op_lit(struct bcode_builder *bbuilder, enum opcode op,
lit_t lit) {
bcode_op(bbuilder, op);
switch (lit.mode) {
case LIT_MODE__TABLE:
bcode_add_varint(bbuilder, lit.v.lit_idx + BCODE_MAX_INLINE_TYPE_TAG);
break;
case LIT_MODE__INLINED:
if (v7_is_string(lit.v.inline_val)) {
size_t len;
const char *s = v7_get_string(bbuilder->v7, &lit.v.inline_val, &len);
bcode_add_varint(bbuilder, BCODE_INLINE_STRING_TYPE_TAG);
bcode_add_varint(bbuilder, len);
bcode_ops_append(bbuilder, s, len + 1 /* nul term */);
} else if (v7_is_number(lit.v.inline_val)) {
bcode_add_varint(bbuilder, BCODE_INLINE_NUMBER_TYPE_TAG);
/*
* TODO(dfrank): we can save some memory by storing string
* representation of a number here, instead of wasting 8 bytes for each
* number.
*
* Alternatively, we can add more tags for integers, like
* `BCODE_INLINE_S08_TYPE_TAG`, `BCODE_INLINE_S16_TYPE_TAG`, etc, since
* integers are the most common numbers for sure.
*/
bcode_ops_append(bbuilder, &lit.v.inline_val, sizeof(lit.v.inline_val));
} else if (is_js_function(lit.v.inline_val)) {
/*
* TODO(dfrank): implement `bcode_serialize_*` more generically, so
* that they can write to buffer instead of a `FILE`. Then, remove this
* workaround with `CS_PLATFORM == CS_P_UNIX`, `tmpfile()`, etc.
*/
#if CS_PLATFORM == CS_P_UNIX
struct v7_js_function *func;
FILE *fp = tmpfile();
long len = 0;
char *p;
func = get_js_function_struct(lit.v.inline_val);
/* we inline functions if only we're precompiling */
assert(bbuilder->v7->is_precompiling);
bcode_add_varint(bbuilder, BCODE_INLINE_FUNC_TYPE_TAG);
bcode_serialize_func(bbuilder->v7, func->bcode, fp);
fflush(fp);
len = ftell(fp);
p = (char *) mmap(NULL, len, PROT_WRITE, MAP_PRIVATE, fileno(fp), 0);
bcode_ops_append(bbuilder, p, len);
fclose(fp);
#endif
} else if (v7_is_regexp(bbuilder->v7, lit.v.inline_val)) {
#if V7_ENABLE__RegExp
struct v7_regexp *rp =
v7_get_regexp_struct(bbuilder->v7, lit.v.inline_val);
bcode_add_varint(bbuilder, BCODE_INLINE_REGEXP_TYPE_TAG);
/* append regexp source */
{
size_t len;
const char *buf =
v7_get_string(bbuilder->v7, &rp->regexp_string, &len);
bcode_add_varint(bbuilder, len);
bcode_ops_append(bbuilder, buf, len + 1 /* nul term */);
}
/* append regexp flags */
{
char buf[_V7_REGEXP_MAX_FLAGS_LEN + 1 /* nul term */];
size_t len = get_regexp_flags_str(bbuilder->v7, rp, buf);
bcode_add_varint(bbuilder, len);
bcode_ops_append(bbuilder, buf, len + 1 /* nul term */);
}
#else
fprintf(stderr, "Firmware is built without -DV7_ENABLE__RegExp\n");
abort();
#endif
} else {
/* invalid type of inlined value */
abort();
}
break;
default:
/* invalid literal mode */
abort();
break;
}
}
V7_PRIVATE void bcode_push_lit(struct bcode_builder *bbuilder, lit_t lit) {
bcode_op_lit(bbuilder, OP_PUSH_LIT, lit);
}
WARN_UNUSED_RESULT
/*V7_PRIVATE*/ enum v7_err
bcode_add_name(struct bcode_builder *bbuilder, const char *p, size_t len,
size_t *idx) {
enum v7_err rcode = V7_OK;
int llen;
size_t ops_index;
/*
* if name length is not provided, assume it's null-terminated and calculate
* it
*/
if (len == ~((size_t) 0)) {
len = strlen(p);
}
/* index at which to put name. If not provided, we'll append at the end */
if (idx != NULL) {
ops_index = *idx;
} else {
ops_index = bbuilder->ops.len;
}
/* calculate how much varint len will take */
llen = calc_llen(len);
/* reserve space in `ops` buffer */
mbuf_insert(&bbuilder->ops, ops_index, NULL, llen + len + 1 /*null-term*/);
{
char *ops = bbuilder->ops.buf + ops_index;
/* put varint len */
ops += encode_varint(len, (unsigned char *) ops);
/* put string */
memcpy(ops, p, len);
ops += len;
/* null-terminate */
*ops++ = 0x00;
if (idx != NULL) {
*idx = ops - bbuilder->ops.buf;
}
}
/* maintain total number of names */
if (bbuilder->bcode->names_cnt < V7_NAMES_CNT_MAX) {
bbuilder->bcode->names_cnt++;
} else {
rcode = v7_throwf(bbuilder->v7, SYNTAX_ERROR, "Too many local variables");
}
return rcode;
}
/*V7_PRIVATE*/ char *bcode_end_names(char *ops, size_t names_cnt) {
while (names_cnt--) {
ops = bcode_next_name(ops, NULL, NULL);
}
return ops;
}
V7_PRIVATE char *bcode_next_name(char *ops, char **pname, size_t *plen) {
size_t len;
int llen;
len = decode_varint((unsigned char *) ops, &llen);
ops += llen;
if (pname != NULL) {
*pname = ops;
}
if (plen != NULL) {
*plen = len;
}
ops += len + 1 /*null-terminator*/;
return ops;
}
V7_PRIVATE char *bcode_next_name_v(struct v7 *v7, struct bcode *bcode,
char *ops, val_t *res) {
char *name;
size_t len;
ops = bcode_next_name(ops, &name, &len);
/*
* If `ops` is in RAM, we create owned string, since the string may outlive
* bcode. Otherwise (`ops` is in ROM), we create foreign string.
*/
*res = v7_mk_string(v7, name, len, !bcode->ops_in_rom);
return ops;
}
V7_PRIVATE bcode_off_t bcode_pos(struct bcode_builder *bbuilder) {
return bbuilder->ops.len;
}
/*
* Appends a branch target and returns its location.
* This location can be updated with bcode_patch_target.
* To be issued following a JMP_* bytecode
*/
V7_PRIVATE bcode_off_t bcode_add_target(struct bcode_builder *bbuilder) {
bcode_off_t pos = bcode_pos(bbuilder);
bcode_off_t zero = 0;
bcode_ops_append(bbuilder, &zero, sizeof(bcode_off_t));
return pos;
}
/*
* Appends an op requiring a branch target. See bcode_add_target.
*
* This function is used only internally, but used in a complicated mix of
* configurations, hence the commented V7_PRIVATE
*/
/*V7_PRIVATE*/ bcode_off_t bcode_op_target(struct bcode_builder *bbuilder,
uint8_t op) {
bcode_op(bbuilder, op);
return bcode_add_target(bbuilder);
}
/*V7_PRIVATE*/ void bcode_patch_target(struct bcode_builder *bbuilder,
bcode_off_t label, bcode_off_t target) {
memcpy(bbuilder->ops.buf + label, &target, sizeof(target));
}
/*V7_PRIVATE*/ void bcode_serialize(struct v7 *v7, struct bcode *bcode,
FILE *out) {
(void) v7;
(void) bcode;
fwrite(BIN_BCODE_SIGNATURE, sizeof(BIN_BCODE_SIGNATURE), 1, out);
bcode_serialize_func(v7, bcode, out);
}
static void bcode_serialize_varint(int n, FILE *out) {
unsigned char buf[8];
int k = calc_llen(n);
encode_varint(n, buf);
fwrite(buf, k, 1, out);
}
static void bcode_serialize_func(struct v7 *v7, struct bcode *bcode,
FILE *out) {
struct v7_vec *vec;
(void) v7;
/*
* All literals should be inlined into `ops`, so we expect literals table
* to be empty here
*/
assert(bcode->lit.len == 0);
/* args_cnt */
bcode_serialize_varint(bcode->args_cnt, out);
/* names_cnt */
bcode_serialize_varint(bcode->names_cnt, out);
/* func_name_present */
bcode_serialize_varint(bcode->func_name_present, out);
/*
* bcode:
* // opcodes length
* *
*/
vec = &bcode->ops;
bcode_serialize_varint(vec->len, out);
fwrite(vec->p, vec->len, 1, out);
}
static size_t bcode_deserialize_varint(const char **data) {
size_t ret = 0;
int len = 0;
ret = decode_varint((const unsigned char *) (*data), &len);
*data += len;
return ret;
}
static const char *bcode_deserialize_func(struct v7 *v7, struct bcode *bcode,
const char *data) {
size_t size;
struct bcode_builder bbuilder;
bcode_builder_init(v7, &bbuilder, bcode);
/*
* before deserializing, set the corresponding flag, so that metrics will be
* updated accordingly
*/
bcode->deserialized = 1;
/*
* In serialized functions, all literals are inlined into `ops`, so we don't
* deserialize them here in any way
*/
/* get number of args */
bcode->args_cnt = bcode_deserialize_varint(&data);
/* get number of names */
bcode->names_cnt = bcode_deserialize_varint(&data);
/* get whether the function name is present in `names` */
bcode->func_name_present = bcode_deserialize_varint(&data);
/* get opcode size */
size = bcode_deserialize_varint(&data);
bbuilder.ops.buf = (char *) data;
bbuilder.ops.size = size;
bbuilder.ops.len = size;
bcode->ops_in_rom = 1;
data += size;
bcode_builder_finalize(&bbuilder);
return data;
}
V7_PRIVATE void bcode_deserialize(struct v7 *v7, struct bcode *bcode,
const char *data) {
data = bcode_deserialize_func(v7, bcode, data);
}
#ifdef V7_MODULE_LINES
#line 1 "v7/src/eval.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "common/str_util.h" */
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/eval.h" */
/* Amalgamated: #include "v7/src/string.h" */
/* Amalgamated: #include "v7/src/array.h" */
/* Amalgamated: #include "v7/src/object.h" */
/* Amalgamated: #include "v7/src/gc.h" */
/* Amalgamated: #include "v7/src/compiler.h" */
/* Amalgamated: #include "v7/src/cyg_profile.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* Amalgamated: #include "v7/src/function.h" */
/* Amalgamated: #include "v7/src/util.h" */
/* Amalgamated: #include "v7/src/shdata.h" */
/* Amalgamated: #include "v7/src/exceptions.h" */
/* Amalgamated: #include "v7/src/conversion.h" */
/* Amalgamated: #include "v7/src/varint.h" */
/*
* Bcode offsets in "try stack" are stored in JS numbers, i.e. in `double`s.
* Apart from the offset itself, we also need some additional data:
*
* - type of the block that offset represents (`catch`, `finally`, `switch`,
* or some loop)
* - size of the stack when the block is created (needed when throwing, since
* if exception is thrown from the middle of the expression, the stack may
* have any arbitrary length)
*
* We bake all this data into integer part of the double (53 bits) :
*
* - 32 bits: bcode offset
* - 3 bits: "tag": the type of the block
* - 16 bits: stack size
*/
/*
* Widths of data parts
*/
#define LBLOCK_OFFSET_WIDTH 32
#define LBLOCK_TAG_WIDTH 3
#define LBLOCK_STACK_SIZE_WIDTH 16
/*
* Shifts of data parts
*/
#define LBLOCK_OFFSET_SHIFT (0)
#define LBLOCK_TAG_SHIFT (LBLOCK_OFFSET_SHIFT + LBLOCK_OFFSET_WIDTH)
#define LBLOCK_STACK_SIZE_SHIFT (LBLOCK_TAG_SHIFT + LBLOCK_TAG_WIDTH)
#define LBLOCK_TOTAL_WIDTH (LBLOCK_STACK_SIZE_SHIFT + LBLOCK_STACK_SIZE_WIDTH)
/*
* Masks of data parts
*/
#define LBLOCK_OFFSET_MASK \
((int64_t)(((int64_t) 1 << LBLOCK_OFFSET_WIDTH) - 1) << LBLOCK_OFFSET_SHIFT)
#define LBLOCK_TAG_MASK \
((int64_t)(((int64_t) 1 << LBLOCK_TAG_WIDTH) - 1) << LBLOCK_TAG_SHIFT)
#define LBLOCK_STACK_SIZE_MASK \
((int64_t)(((int64_t) 1 << LBLOCK_STACK_SIZE_WIDTH) - 1) \
<< LBLOCK_STACK_SIZE_SHIFT)
/*
* Self-check: make sure all the data can fit into double's mantissa
*/
#if (LBLOCK_TOTAL_WIDTH > 53)
#error lblock width is too large, it can't fit into double's mantissa
#endif
/*
* Tags that are used for bcode offsets in "try stack"
*/
#define LBLOCK_TAG_CATCH ((int64_t) 0x01 << LBLOCK_TAG_SHIFT)
#define LBLOCK_TAG_FINALLY ((int64_t) 0x02 << LBLOCK_TAG_SHIFT)
#define LBLOCK_TAG_LOOP ((int64_t) 0x03 << LBLOCK_TAG_SHIFT)
#define LBLOCK_TAG_SWITCH ((int64_t) 0x04 << LBLOCK_TAG_SHIFT)
/*
* Yields 32-bit bcode offset value
*/
#define LBLOCK_OFFSET(v) \
((bcode_off_t)(((v) &LBLOCK_OFFSET_MASK) >> LBLOCK_OFFSET_SHIFT))
/*
* Yields tag value (unshifted, to be compared with macros like
* `LBLOCK_TAG_CATCH`, etc)
*/
#define LBLOCK_TAG(v) ((v) &LBLOCK_TAG_MASK)
/*
* Yields stack size
*/
#define LBLOCK_STACK_SIZE(v) \
(((v) &LBLOCK_STACK_SIZE_MASK) >> LBLOCK_STACK_SIZE_SHIFT)
/*
* Yields `int64_t` value to be stored as a JavaScript number
*/
#define LBLOCK_ITEM_CREATE(offset, tag, stack_size) \
((int64_t)(offset) | (tag) | \
(((int64_t)(stack_size)) << LBLOCK_STACK_SIZE_SHIFT))
/*
* make sure `bcode_off_t` is just 32-bit, so that it can fit in double
* with 3-bit tag
*/
V7_STATIC_ASSERT((sizeof(bcode_off_t) * 8) == LBLOCK_OFFSET_WIDTH,
wrong_size_of_bcode_off_t);
#define PUSH(v) stack_push(&v7->stack, v)
#define POP() stack_pop(&v7->stack)
#define TOS() stack_tos(&v7->stack)
#define SP() stack_sp(&v7->stack)
/*
* Local-to-function block types that we might want to consider when unwinding
* stack for whatever reason. see `unwind_local_blocks_stack()`.
*/
enum local_block {
LOCAL_BLOCK_NONE = (0),
LOCAL_BLOCK_CATCH = (1 << 0),
LOCAL_BLOCK_FINALLY = (1 << 1),
LOCAL_BLOCK_LOOP = (1 << 2),
LOCAL_BLOCK_SWITCH = (1 << 3),
};
/*
* Like `V7_TRY()`, but to be used inside `eval_bcode()` only: you should
* wrap all calls to cfunctions into `BTRY()` instead of `V7_TRY()`.
*
* If the provided function returns something other than `V7_OK`, this macro
* calls `bcode_perform_throw`, which performs bcode stack unwinding.
*/
#define BTRY(call) \
do { \
enum v7_err _e = call; \
(void) _you_should_use_BTRY_in_eval_bcode_only; \
if (_e != V7_OK) { \
V7_TRY(bcode_perform_throw(v7, &r, 0 /*don't take value from stack*/)); \
goto op_done; \
} \
} while (0)
V7_PRIVATE void stack_push(struct mbuf *s, val_t v) {
mbuf_append(s, &v, sizeof(v));
}
V7_PRIVATE val_t stack_pop(struct mbuf *s) {
assert(s->len >= sizeof(val_t));
s->len -= sizeof(val_t);
return *(val_t *) (s->buf + s->len);
}
V7_PRIVATE val_t stack_tos(struct mbuf *s) {
assert(s->len >= sizeof(val_t));
return *(val_t *) (s->buf + s->len - sizeof(val_t));
}
#ifdef V7_BCODE_TRACE_STACK
V7_PRIVATE val_t stack_at(struct mbuf *s, size_t idx) {
assert(s->len >= sizeof(val_t) * idx);
return *(val_t *) (s->buf + s->len - sizeof(val_t) - idx * sizeof(val_t));
}
#endif
V7_PRIVATE int stack_sp(struct mbuf *s) {
return s->len / sizeof(val_t);
}
/*
* Delete a property with name `name`, `len` from an object `obj`. If the
* object does not contain own property with the given `name`, moves to `obj`'s
* prototype, and so on.
*
* If the property is eventually found, it is deleted, and `0` is returned.
* Otherwise, `-1` is returned.
*
* If `len` is -1/MAXUINT/~0, then `name` must be 0-terminated.
*
* See `v7_del()` as well.
*/
static int del_property_deep(struct v7 *v7, val_t obj, const char *name,
size_t len) {
if (!v7_is_object(obj)) {
return -1;
}
for (; obj != V7_NULL; obj = v7_get_proto(v7, obj)) {
int del_res;
if ((del_res = v7_del(v7, obj, name, len)) != -1) {
return del_res;
}
}
return -1;
}
/* Visual studio 2012+ has signbit() */
#if defined(_MSC_VER) && _MSC_VER < 1700
static int signbit(double x) {
double s = _copysign(1, x);
return s < 0;
}
#endif
static double b_int_bin_op(enum opcode op, double a, double b) {
int32_t ia = isnan(a) || isinf(a) ? 0 : (int32_t)(int64_t) a;
int32_t ib = isnan(b) || isinf(b) ? 0 : (int32_t)(int64_t) b;
switch (op) {
case OP_LSHIFT:
return (int32_t)((uint32_t) ia << ((uint32_t) ib & 31));
case OP_RSHIFT:
return ia >> ((uint32_t) ib & 31);
case OP_URSHIFT:
return (uint32_t) ia >> ((uint32_t) ib & 31);
case OP_OR:
return ia | ib;
case OP_XOR:
return ia ^ ib;
case OP_AND:
return ia & ib;
default:
assert(0);
}
return 0;
}
static double b_num_bin_op(enum opcode op, double a, double b) {
/*
* For certain operations, the result is always NaN if either of arguments
* is NaN
*/
switch (op) {
case OP_ADD:
case OP_SUB:
case OP_MUL:
case OP_DIV:
case OP_REM:
if (isnan(a) || isnan(b)) {
return NAN;
}
break;
default:
break;
}
switch (op) {
case OP_ADD: /* simple fixed width nodes with no payload */
return a + b;
case OP_SUB:
return a - b;
case OP_REM:
if (b == 0 || isnan(b) || isnan(a) || isinf(b) || isinf(a)) {
return NAN;
}
return (int) a % (int) b;
case OP_MUL:
return a * b;
case OP_DIV:
if (b == 0) {
if (a == 0) return NAN;
return (!signbit(a) == !signbit(b)) ? INFINITY : -INFINITY;
}
return a / b;
case OP_LSHIFT:
case OP_RSHIFT:
case OP_URSHIFT:
case OP_OR:
case OP_XOR:
case OP_AND:
return b_int_bin_op(op, a, b);
default:
assert(0);
}
return 0;
}
static int b_bool_bin_op(enum opcode op, double a, double b) {
#ifdef V7_BROKEN_NAN
if (isnan(a) || isnan(b)) return op == OP_NE || op == OP_NE_NE;
#endif
switch (op) {
case OP_EQ:
case OP_EQ_EQ:
return a == b;
case OP_NE:
case OP_NE_NE:
return a != b;
case OP_LT:
return a < b;
case OP_LE:
return a <= b;
case OP_GT:
return a > b;
case OP_GE:
return a >= b;
default:
assert(0);
}
return 0;
}
static bcode_off_t bcode_get_target(char **ops) {
bcode_off_t target;
(*ops)++;
memcpy(&target, *ops, sizeof(target));
*ops += sizeof(target) - 1;
return target;
}
struct bcode_registers {
/*
* TODO(dfrank): make it contain `struct v7_call_frame_bcode *`
* and use `bcode_ops` in-place, or probably drop the `bcode_registers`
* whatsoever
*/
struct bcode *bcode;
char *ops;
char *end;
unsigned int need_inc_ops : 1;
};
/*
* If returning from function implicitly, then set return value to `undefined`.
*
* And if function was called as a constructor, then make sure returned
* value is an object.
*/
static void bcode_adjust_retval(struct v7 *v7, uint8_t is_explicit_return) {
if (!is_explicit_return) {
/* returning implicitly: set return value to `undefined` */
POP();
PUSH(V7_UNDEFINED);
}
if (v7->call_stack->is_constructor && !v7_is_object(TOS())) {
/* constructor is going to return non-object: replace it with `this` */
POP();
PUSH(v7_get_this(v7));
}
}
static void bcode_restore_registers(struct v7 *v7, struct bcode *bcode,
struct bcode_registers *r) {
r->bcode = bcode;
r->ops = bcode->ops.p;
r->end = r->ops + bcode->ops.len;
(void) v7;
}
V7_PRIVATE struct v7_call_frame_base *find_call_frame(struct v7 *v7,
uint8_t type_mask) {
struct v7_call_frame_base *ret = v7->call_stack;
while (ret != NULL && !(ret->type_mask & type_mask)) {
ret = ret->prev;
}
return ret;
}
static struct v7_call_frame_private *find_call_frame_private(struct v7 *v7) {
return (struct v7_call_frame_private *) find_call_frame(
v7, V7_CALL_FRAME_MASK_PRIVATE);
}
static struct v7_call_frame_bcode *find_call_frame_bcode(struct v7 *v7) {
return (struct v7_call_frame_bcode *) find_call_frame(
v7, V7_CALL_FRAME_MASK_BCODE);
}
#if 0
static struct v7_call_frame_cfunc *find_call_frame_cfunc(struct v7 *v7) {
return (struct v7_call_frame_cfunc *) find_call_frame(
v7, V7_CALL_FRAME_MASK_CFUNC);
}
#endif
static struct v7_call_frame_base *create_call_frame(struct v7 *v7,
size_t size) {
struct v7_call_frame_base *call_frame_base = NULL;
call_frame_base = (struct v7_call_frame_base *) calloc(1, size);
/* save previous call frame */
call_frame_base->prev = v7->call_stack;
/* by default, inherit line_no from the previous frame */
if (v7->call_stack != NULL) {
call_frame_base->line_no = v7->call_stack->line_no;
}
return call_frame_base;
}
static void init_call_frame_private(struct v7 *v7,
struct v7_call_frame_private *call_frame,
val_t scope) {
/* make a snapshot of the current state */
{
struct v7_call_frame_private *cf = find_call_frame_private(v7);
if (cf != NULL) {
cf->stack_size = v7->stack.len;
}
}
/* set a type flag */
call_frame->base.type_mask |= V7_CALL_FRAME_MASK_PRIVATE;
/* fill the new frame with data */
call_frame->vals.scope = scope;
/* `try_stack` will be lazily created in `eval_try_push()`*/
call_frame->vals.try_stack = V7_UNDEFINED;
}
static void init_call_frame_bcode(struct v7 *v7,
struct v7_call_frame_bcode *call_frame,
char *prev_bcode_ops, struct bcode *bcode,
val_t this_obj, val_t scope,
uint8_t is_constructor) {
init_call_frame_private(v7, &call_frame->base, scope);
/* make a snapshot of the current state */
{
struct v7_call_frame_bcode *cf = find_call_frame_bcode(v7);
if (cf != NULL) {
cf->bcode_ops = prev_bcode_ops;
/* remember thrown value */
cf->vals.thrown_error = v7->vals.thrown_error;
cf->base.base.is_thrown = v7->is_thrown;
}
}
/* set a type flag */
call_frame->base.base.type_mask |= V7_CALL_FRAME_MASK_BCODE;
/* fill the new frame with data */
call_frame->bcode = bcode;
call_frame->vals.this_obj = this_obj;
call_frame->base.base.is_constructor = is_constructor;
}
/*
* Create new bcode call frame object and fill it with data
*/
static void append_call_frame_bcode(struct v7 *v7, char *prev_bcode_ops,
struct bcode *bcode, val_t this_obj,
val_t scope, uint8_t is_constructor) {
struct v7_call_frame_bcode *call_frame =
(struct v7_call_frame_bcode *) create_call_frame(v7, sizeof(*call_frame));
init_call_frame_bcode(v7, call_frame, prev_bcode_ops, bcode, this_obj, scope,
is_constructor);
v7->call_stack = &call_frame->base.base;
}
static void append_call_frame_private(struct v7 *v7, val_t scope) {
struct v7_call_frame_private *call_frame =
(struct v7_call_frame_private *) create_call_frame(v7,
sizeof(*call_frame));
init_call_frame_private(v7, call_frame, scope);
v7->call_stack = &call_frame->base;
}
static void append_call_frame_cfunc(struct v7 *v7, val_t this_obj,
v7_cfunction_t *cfunc) {
struct v7_call_frame_cfunc *call_frame =
(struct v7_call_frame_cfunc *) create_call_frame(v7, sizeof(*call_frame));
/* set a type flag */
call_frame->base.type_mask |= V7_CALL_FRAME_MASK_CFUNC;
/* fill the new frame with data */
call_frame->cfunc = cfunc;
call_frame->vals.this_obj = this_obj;
v7->call_stack = &call_frame->base;
}
/*
* The caller's bcode object is needed because we have to restore literals
* and `end` registers.
*
* TODO(mkm): put this state on a return stack
*
* Caller of bcode_perform_call is responsible for owning `call_frame`
*/
static enum v7_err bcode_perform_call(struct v7 *v7, v7_val_t scope_frame,
struct v7_js_function *func,
struct bcode_registers *r,
val_t this_object, char *ops,
uint8_t is_constructor) {
/* new scope_frame will inherit from the function's scope */
obj_prototype_set(v7, get_object_struct(scope_frame), &func->scope->base);
/* create new `call_frame` which will replace `v7->call_stack` */
append_call_frame_bcode(v7, r->ops + 1, func->bcode, this_object, scope_frame,
is_constructor);
bcode_restore_registers(v7, func->bcode, r);
/* adjust `ops` since names were already read from it */
r->ops = ops;
/* `ops` already points to the needed instruction, no need to increment it */
r->need_inc_ops = 0;
return V7_OK;
}
/*
* Apply data from the "private" call frame, typically after some other frame
* was just unwound.
*
* The `call_frame` may actually be `NULL`, if the top frame was unwound.
*/
static void apply_frame_private(struct v7 *v7,
struct v7_call_frame_private *call_frame) {
/*
* Adjust data stack length (restore saved).
*
* If `call_frame` is NULL, it means that the last call frame was just
* unwound, and hence the data stack size should be 0.
*/
size_t stack_size = (call_frame != NULL ? call_frame->stack_size : 0);
assert(stack_size <= v7->stack.len);
v7->stack.len = stack_size;
}
/*
* Apply data from the "bcode" call frame, typically after some other frame
* was just unwound.
*
* The `call_frame` may actually be `NULL`, if the top frame was unwound; but
* in this case, `r` must be `NULL` too, by design. See inline comment below.
*/
static void apply_frame_bcode(struct v7 *v7,
struct v7_call_frame_bcode *call_frame,
struct bcode_registers *r) {
if (r != NULL) {
/*
* Note: if `r` is non-NULL, then `call_frame` should be non-NULL as well,
* by design. If this invariant is violated, it means that
* `unwind_stack_1level()` is misused.
*/
assert(call_frame != NULL);
bcode_restore_registers(v7, call_frame->bcode, r);
r->ops = call_frame->bcode_ops;
/*
* restore thrown value if only there's no new thrown value
* (otherwise, the new one overrides the previous one)
*/
if (!v7->is_thrown) {
v7->vals.thrown_error = call_frame->vals.thrown_error;
v7->is_thrown = call_frame->base.base.is_thrown;
}
}
}
/*
* Unwinds `call_stack` by 1 frame.
*
* Returns the type of the unwound frame
*/
static v7_call_frame_mask_t unwind_stack_1level(struct v7 *v7,
struct bcode_registers *r) {
v7_call_frame_mask_t type_mask;
#ifdef V7_BCODE_TRACE
fprintf(stderr, "unwinding stack by 1 level\n");
#endif
type_mask = v7->call_stack->type_mask;
/* drop the top frame */
{
struct v7_call_frame_base *tmp = v7->call_stack;
v7->call_stack = v7->call_stack->prev;
free(tmp);
}
/*
* depending on the unwound frame type, apply data from the top call frame(s)
* which are still alive (if any)
*/
if (type_mask & V7_CALL_FRAME_MASK_PRIVATE) {
apply_frame_private(v7, find_call_frame_private(v7));
}
if (type_mask & V7_CALL_FRAME_MASK_BCODE) {
apply_frame_bcode(v7, find_call_frame_bcode(v7), r);
}
if (type_mask & V7_CALL_FRAME_MASK_CFUNC) {
/* Nothing to do here at the moment */
}
return type_mask;
}
/*
* Unwinds local "try stack" (i.e. local-to-current-function stack of nested
* `try` blocks), looking for local-to-function blocks.
*
* Block types of interest are specified with `wanted_blocks_mask`: it's a
* bitmask of `enum local_block` values.
*
* Only blocks of specified types will be considered, others will be dropped.
*
* If `restore_stack_size` is non-zero, the `v7->stack.len` will be restored
* to the value saved when the block was created. This is useful when throwing,
* since if we throw from the middle of the expression, the stack could have
* any size. But you probably shouldn't set this flag when breaking and
* returning, since it may hide real bugs in the opcode.
*
* Returns id of the block type that control was transferred into, or
* `LOCAL_BLOCK_NONE` if no appropriate block was found. Note: returned value
* contains at most 1 block bit; it can't contain multiple bits.
*/
static enum local_block unwind_local_blocks_stack(
struct v7 *v7, struct bcode_registers *r, unsigned int wanted_blocks_mask,
uint8_t restore_stack_size) {
val_t arr = V7_UNDEFINED;
struct gc_tmp_frame tf = new_tmp_frame(v7);
enum local_block found_block = LOCAL_BLOCK_NONE;
unsigned long length;
tmp_stack_push(&tf, &arr);
arr = find_call_frame_private(v7)->vals.try_stack;
if (v7_is_array(v7, arr)) {
/*
* pop latest element from "try stack", loop until we need to transfer
* control there
*/
while ((length = v7_array_length(v7, arr)) > 0) {
/* get latest offset from the "try stack" */
int64_t offset = v7_get_double(v7, v7_array_get(v7, arr, length - 1));
enum local_block cur_block = LOCAL_BLOCK_NONE;
/* get id of the current block type */
switch (LBLOCK_TAG(offset)) {
case LBLOCK_TAG_CATCH:
cur_block = LOCAL_BLOCK_CATCH;
break;
case LBLOCK_TAG_FINALLY:
cur_block = LOCAL_BLOCK_FINALLY;
break;
case LBLOCK_TAG_LOOP:
cur_block = LOCAL_BLOCK_LOOP;
break;
case LBLOCK_TAG_SWITCH:
cur_block = LOCAL_BLOCK_SWITCH;
break;
default:
assert(0);
break;
}
if (cur_block & wanted_blocks_mask) {
/* need to transfer control to this offset */
r->ops = r->bcode->ops.p + LBLOCK_OFFSET(offset);
#ifdef V7_BCODE_TRACE
fprintf(stderr, "transferring to block #%d: %u\n", (int) cur_block,
(unsigned int) LBLOCK_OFFSET(offset));
#endif
found_block = cur_block;
/* if needed, restore stack size to the saved value */
if (restore_stack_size) {
v7->stack.len = LBLOCK_STACK_SIZE(offset);
}
break;
} else {
#ifdef V7_BCODE_TRACE
fprintf(stderr, "skipped block #%d: %u\n", (int) cur_block,
(unsigned int) LBLOCK_OFFSET(offset));
#endif
/*
* since we don't need to control transfer there, just pop
* it from the "try stack"
*/
v7_array_del(v7, arr, length - 1);
}
}
}
tmp_frame_cleanup(&tf);
return found_block;
}
/*
* Perform break, if there is a `finally` block in effect, transfer
* control there.
*/
static void bcode_perform_break(struct v7 *v7, struct bcode_registers *r) {
enum local_block found;
unsigned int mask;
v7->is_breaking = 0;
if (v7->is_continuing) {
mask = LOCAL_BLOCK_LOOP;
} else {
mask = LOCAL_BLOCK_LOOP | LOCAL_BLOCK_SWITCH;
}
/*
* Keep unwinding until we find local block of interest. We should not
* encounter any "function" frames; only "private" frames are allowed.
*/
for (;;) {
/*
* Try to unwind local "try stack", considering only `finally` and `break`.
*/
found = unwind_local_blocks_stack(v7, r, mask | LOCAL_BLOCK_FINALLY, 0);
if (found == LOCAL_BLOCK_NONE) {
/*
* no blocks found: this may happen if only the `break` or `continue` has
* occurred inside "private" frame. So, unwind this frame, make sure it
* is indeed a "private" frame, and keep unwinding local blocks.
*/
v7_call_frame_mask_t frame_type_mask = unwind_stack_1level(v7, r);
assert(frame_type_mask == V7_CALL_FRAME_MASK_PRIVATE);
(void) frame_type_mask;
} else {
/* found some block to transfer control into, stop unwinding */
break;
}
}
/*
* upon exit of a finally block we'll reenter here if is_breaking is true.
* See OP_AFTER_FINALLY.
*/
if (found == LOCAL_BLOCK_FINALLY) {
v7->is_breaking = 1;
}
/* `ops` already points to the needed instruction, no need to increment it */
r->need_inc_ops = 0;
}
/*
* Perform return, but if there is a `finally` block in effect, transfer
* control there.
*
* If `take_retval` is non-zero, value to return will be popped from stack
* (and saved into `v7->vals.returned_value`), otherwise, it won't ae affected.
*/
static enum v7_err bcode_perform_return(struct v7 *v7,
struct bcode_registers *r,
int take_retval) {
/*
* We should either take retval from the stack, or some value should already
* de pending to return
*/
assert(take_retval || v7->is_returned);
if (take_retval) {
/* taking return value from stack */
v7->vals.returned_value = POP();
v7->is_returned = 1;
/*
* returning (say, from `finally`) dismisses any errors that are eeing
* thrown at the moment as well
*/
v7->is_thrown = 0;
v7->vals.thrown_error = V7_UNDEFINED;
}
/*
* Keep unwinding until we unwound "function" frame, or found some `finally`
* block.
*/
for (;;) {
/* Try to unwind local "try stack", considering only `finally` blocks */
if (unwind_local_blocks_stack(v7, r, (LOCAL_BLOCK_FINALLY), 0) ==
LOCAL_BLOCK_NONE) {
/*
* no `finally` blocks were found, so, unwind stack by 1 level, and see
* if it's a "function" frame. If not, will keep unwinding.
*/
if (unwind_stack_1level(v7, r) & V7_CALL_FRAME_MASK_BCODE) {
/*
* unwound frame is a "function" frame, so, push returned value to
* stack, and stop unwinding
*/
PUSH(v7->vals.returned_value);
v7->is_returned = 0;
v7->vals.returned_value = V7_UNDEFINED;
break;
}
} else {
/* found `finally` block, so, stop unwinding */
break;
}
}
/* `ops` already points to the needed instruction, no need to increment it */
r->need_inc_ops = 0;
return V7_OK;
}
/*
* Perform throw inside `eval_bcode()`.
*
* If `take_thrown_value` is non-zero, value to return will be popped from
* stack (and saved into `v7->vals.thrown_error`), otherwise, it won't be
* affected.
*
* Returns `V7_OK` if thrown exception was caught, `V7_EXEC_EXCEPTION`
* otherwise (in this case, evaluation of current script must be stopped)
*
* When calling this function from `eval_rcode()`, you should wrap this call
* into the `V7_TRY()` macro.
*/
static enum v7_err bcode_perform_throw(struct v7 *v7, struct bcode_registers *r,
int take_thrown_value) {
enum v7_err rcode = V7_OK;
enum local_block found;
assert(take_thrown_value || v7->is_thrown);
if (take_thrown_value) {
v7->vals.thrown_error = POP();
v7->is_thrown = 1;
/* Throwing dismisses any pending return values */
v7->is_returned = 0;
v7->vals.returned_value = V7_UNDEFINED;
}
while ((found = unwind_local_blocks_stack(
v7, r, (LOCAL_BLOCK_CATCH | LOCAL_BLOCK_FINALLY), 1)) ==
LOCAL_BLOCK_NONE) {
if (v7->call_stack != v7->bottom_call_frame) {
#ifdef V7_BCODE_TRACE
fprintf(stderr, "not at the bottom of the stack, going to unwind..\n");
#endif
/* not reached bottom of the stack yet, keep unwinding */
unwind_stack_1level(v7, r);
} else {
/* reached stack bottom: uncaught exception */
#ifdef V7_BCODE_TRACE
fprintf(stderr, "reached stack bottom: uncaught exception\n");
#endif
rcode = V7_EXEC_EXCEPTION;
break;
}
}
if (found == LOCAL_BLOCK_CATCH) {
/*
* we're going to enter `catch` block, so, populate TOS with the thrown
* value, and clear it in v7 context.
*/
PUSH(v7->vals.thrown_error);
v7->is_thrown = 0;
v7->vals.thrown_error = V7_UNDEFINED;
}
/* `ops` already points to the needed instruction, no need to increment it */
r->need_inc_ops = 0;
return rcode;
}
/*
* Throws reference error from `eval_bcode()`. Always wrap a call to this
* function into `V7_TRY()`.
*/
static enum v7_err bcode_throw_reference_error(struct v7 *v7,
struct bcode_registers *r,
val_t var_name) {
enum v7_err rcode = V7_OK;
const char *s;
size_t name_len;
assert(v7_is_string(var_name));
s = v7_get_string(v7, &var_name, &name_len);
rcode = v7_throwf(v7, REFERENCE_ERROR, "[%.*s] is not defined",
(int) name_len, s);
(void) rcode;
return bcode_perform_throw(v7, r, 0);
}
/*
* Takes a half-done function (either from literal table or deserialized from
* `ops` inlined data), and returns a ready-to-use function.
*
* The actual behaviour depends on whether the half-done function has
* `prototype` defined. If there's no prototype (i.e. it's `undefined`), then
* the new function is created, with bcode from a given one. If, however,
* the prototype is defined, it means that the function was just deserialized
* from `ops`, so we only need to set `scope` on it.
*
* Assumes `func` is owned by the caller.
*/
static val_t bcode_instantiate_function(struct v7 *v7, val_t func) {
val_t res;
struct v7_generic_object *scope;
struct v7_js_function *f;
assert(is_js_function(func));
f = get_js_function_struct(func);
scope = get_generic_object_struct(get_scope(v7));
if (v7_is_undefined(v7_get(v7, func, "prototype", 9))) {
/*
* Function's `prototype` is `undefined`: it means that the function is
* created by the compiler and is stored in the literal table. We have to
* create completely new function
*/
struct v7_js_function *rf;
res = mk_js_function(v7, scope, v7_mk_object(v7));
/* Copy and retain bcode */
rf = get_js_function_struct(res);
rf->bcode = f->bcode;
retain_bcode(v7, rf->bcode);
} else {
/*
* Function's `prototype` is NOT `undefined`: it means that the function is
* deserialized from inline `ops` data, and we just need to set scope on
* it.
*/
res = func;
f->scope = scope;
}
return res;
}
/**
* Call C function `func` with given `this_object` and array of arguments
* `args`. `func` should be a C function pointer, not C function object.
*/
static enum v7_err call_cfunction(struct v7 *v7, val_t func, val_t this_object,
val_t args, uint8_t is_constructor,
val_t *res) {
enum v7_err rcode = V7_OK;
uint8_t saved_inhibit_gc = v7->inhibit_gc;
val_t saved_arguments = v7->vals.arguments;
struct gc_tmp_frame tf = new_tmp_frame(v7);
v7_cfunction_t *cfunc = get_cfunction_ptr(v7, func);
*res = V7_UNDEFINED;
tmp_stack_push(&tf, &saved_arguments);
append_call_frame_cfunc(v7, this_object, cfunc);
/*
* prepare cfunction environment
*/
v7->inhibit_gc = 1;
v7->vals.arguments = args;
/* call C function */
rcode = cfunc(v7, res);
if (rcode != V7_OK) {
goto clean;
}
if (is_constructor && !v7_is_object(*res)) {
/* constructor returned non-object: replace it with `this` */
*res = v7_get_this(v7);
}
clean:
v7->vals.arguments = saved_arguments;
v7->inhibit_gc = saved_inhibit_gc;
unwind_stack_1level(v7, NULL);
tmp_frame_cleanup(&tf);
return rcode;
}
/*
* Evaluate `OP_TRY_PUSH_CATCH` or `OP_TRY_PUSH_FINALLY`: Take an offset (from
* the parameter of opcode) and push it onto "try stack"
*/
static void eval_try_push(struct v7 *v7, enum opcode op,
struct bcode_registers *r) {
val_t arr = V7_UNDEFINED;
struct gc_tmp_frame tf = new_tmp_frame(v7);
bcode_off_t target;
int64_t offset_tag = 0;
tmp_stack_push(&tf, &arr);
/* make sure "try stack" array exists */
arr = find_call_frame_private(v7)->vals.try_stack;
if (!v7_is_array(v7, arr)) {
arr = v7_mk_dense_array(v7);
find_call_frame_private(v7)->vals.try_stack = arr;
}
/*
* push the target address at the end of the "try stack" array
*/
switch (op) {
case OP_TRY_PUSH_CATCH:
offset_tag = LBLOCK_TAG_CATCH;
break;
case OP_TRY_PUSH_FINALLY:
offset_tag = LBLOCK_TAG_FINALLY;
break;
case OP_TRY_PUSH_LOOP:
offset_tag = LBLOCK_TAG_LOOP;
break;
case OP_TRY_PUSH_SWITCH:
offset_tag = LBLOCK_TAG_SWITCH;
break;
default:
assert(0);
break;
}
target = bcode_get_target(&r->ops);
v7_array_push(v7, arr, v7_mk_number(v7, LBLOCK_ITEM_CREATE(target, offset_tag,
v7->stack.len)));
tmp_frame_cleanup(&tf);
}
/*
* Evaluate `OP_TRY_POP`: just pop latest item from "try stack", ignoring it
*/
static enum v7_err eval_try_pop(struct v7 *v7) {
enum v7_err rcode = V7_OK;
val_t arr = V7_UNDEFINED;
unsigned long length;
struct gc_tmp_frame tf = new_tmp_frame(v7);
tmp_stack_push(&tf, &arr);
/* get "try stack" array, which must be defined and must not be emtpy */
arr = find_call_frame_private(v7)->vals.try_stack;
if (!v7_is_array(v7, arr)) {
rcode = v7_throwf(v7, "Error", "TRY_POP when try_stack is not an array");
V7_TRY(V7_INTERNAL_ERROR);
}
length = v7_array_length(v7, arr);
if (length == 0) {
rcode = v7_throwf(v7, "Error", "TRY_POP when try_stack is empty");
V7_TRY(V7_INTERNAL_ERROR);
}
/* delete the latest element of this array */
v7_array_del(v7, arr, length - 1);
clean:
tmp_frame_cleanup(&tf);
return rcode;
}
static void own_bcode(struct v7 *v7, struct bcode *p) {
mbuf_append(&v7->act_bcodes, &p, sizeof(p));
}
static void disown_bcode(struct v7 *v7, struct bcode *p) {
#ifndef NDEBUG
struct bcode **vp =
(struct bcode **) (v7->act_bcodes.buf + v7->act_bcodes.len - sizeof(p));
/* given `p` should be the last item */
assert(*vp == p);
#endif
v7->act_bcodes.len -= sizeof(p);
}
/* Keeps track of last evaluated bcodes in order to improve error reporting */
static void push_bcode_history(struct v7 *v7, enum opcode op) {
size_t i;
if (op == OP_CHECK_CALL || op == OP_CALL || op == OP_NEW) return;
for (i = ARRAY_SIZE(v7->last_ops) - 1; i > 0; i--) {
v7->last_ops[i] = v7->last_ops[i - 1];
}
v7->last_ops[0] = op;
}
#if !V7_DISABLE_CALL_ERROR_CONTEXT
static void reset_last_name(struct v7 *v7) {
v7->vals.last_name[0] = V7_UNDEFINED;
v7->vals.last_name[1] = V7_UNDEFINED;
}
#else
static void reset_last_name(struct v7 *v7) {
/* should be inlined out */
(void) v7;
}
#endif
static void prop_iter_ctx_dtor(struct v7 *v7, void *ud) {
struct prop_iter_ctx *ctx = (struct prop_iter_ctx *) ud;
v7_destruct_prop_iter_ctx(v7, ctx);
free(ctx);
}
/*
* Evaluates given `bcode`. If `reset_line_no` is non-zero, the line number
* is initially reset to 1; otherwise, it is inherited from the previous call
* frame.
*/
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err eval_bcode(struct v7 *v7, struct bcode *bcode,
val_t this_object, uint8_t reset_line_no,
val_t *_res) {
struct bcode_registers r;
enum v7_err rcode = V7_OK;
struct v7_call_frame_base *saved_bottom_call_frame = v7->bottom_call_frame;
/*
* Dummy variable just to enforce that `BTRY()` macro is used only inside the
* `eval_bcode()` function
*/
uint8_t _you_should_use_BTRY_in_eval_bcode_only = 0;
char buf[512];
val_t res = V7_UNDEFINED, v1 = V7_UNDEFINED, v2 = V7_UNDEFINED,
v3 = V7_UNDEFINED, v4 = V7_UNDEFINED, scope_frame = V7_UNDEFINED;
struct gc_tmp_frame tf = new_tmp_frame(v7);
append_call_frame_bcode(v7, NULL, bcode, this_object, get_scope(v7), 0);
if (reset_line_no) {
v7->call_stack->line_no = 1;
}
/*
* Set current call stack as the "bottom" call stack, so that bcode evaluator
* will exit when it reaches this "bottom"
*/
v7->bottom_call_frame = v7->call_stack;
bcode_restore_registers(v7, bcode, &r);
tmp_stack_push(&tf, &res);
tmp_stack_push(&tf, &v1);
tmp_stack_push(&tf, &v2);
tmp_stack_push(&tf, &v3);
tmp_stack_push(&tf, &v4);
tmp_stack_push(&tf, &scope_frame);
/*
* populate local variables on current scope, making them undeletable
* (since they're defined with `var`)
*/
{
size_t i;
for (i = 0; i < bcode->names_cnt; ++i) {
r.ops = bcode_next_name_v(v7, bcode, r.ops, &v1);
/* set undeletable property on current scope */
V7_TRY(def_property_v(v7, get_scope(v7), v1, V7_DESC_CONFIGURABLE(0),
V7_UNDEFINED, 1 /*as_assign*/, NULL));
}
}
restart:
while (r.ops < r.end && rcode == V7_OK) {
enum opcode op = (enum opcode) * r.ops;
#if !V7_DISABLE_LINE_NUMBERS
if ((uint8_t) op >= _OP_LINE_NO) {
unsigned char buf[sizeof(size_t)];
int len;
size_t max_llen = sizeof(buf);
/* ASAN doesn't like out of bound reads */
if (r.ops + max_llen > r.end) {
max_llen = r.end - r.ops;
}
/*
* before we decode varint, we'll have to swap MSB and LSB, but we can't
* do it in place since we're decoding from constant memory, so, we also
* have to copy the data to the temp buffer first. 4 bytes should be
* enough for everyone's line number.
*/
memcpy(buf, r.ops, max_llen);
buf[0] = msb_lsb_swap(buf[0]);
v7->call_stack->line_no = decode_varint(buf, &len) >> 1;
assert((size_t) len <= sizeof(buf));
r.ops += len;
continue;
}
#endif
push_bcode_history(v7, op);
if (v7->need_gc) {
if (maybe_gc(v7)) {
v7->need_gc = 0;
}
}
r.need_inc_ops = 1;
#ifdef V7_BCODE_TRACE
{
char *dops = r.ops;
fprintf(stderr, "eval ");
dump_op(v7, stderr, r.bcode, &dops);
}
#endif
switch (op) {
case OP_DROP:
POP();
break;
case OP_DUP:
v1 = POP();
PUSH(v1);
PUSH(v1);
break;
case OP_2DUP:
v2 = POP();
v1 = POP();
PUSH(v1);
PUSH(v2);
PUSH(v1);
PUSH(v2);
break;
case OP_SWAP:
v1 = POP();
v2 = POP();
PUSH(v1);
PUSH(v2);
break;
case OP_STASH:
assert(!v7->is_stashed);
v7->vals.stash = TOS();
v7->is_stashed = 1;
break;
case OP_UNSTASH:
assert(v7->is_stashed);
POP();
PUSH(v7->vals.stash);
v7->vals.stash = V7_UNDEFINED;
v7->is_stashed = 0;
break;
case OP_SWAP_DROP:
v1 = POP();
POP();
PUSH(v1);
break;
case OP_PUSH_UNDEFINED:
PUSH(V7_UNDEFINED);
break;
case OP_PUSH_NULL:
PUSH(V7_NULL);
break;
case OP_PUSH_THIS:
PUSH(v7_get_this(v7));
reset_last_name(v7);
break;
case OP_PUSH_TRUE:
PUSH(v7_mk_boolean(v7, 1));
reset_last_name(v7);
break;
case OP_PUSH_FALSE:
PUSH(v7_mk_boolean(v7, 0));
reset_last_name(v7);
break;
case OP_PUSH_ZERO:
PUSH(v7_mk_number(v7, 0));
reset_last_name(v7);
break;
case OP_PUSH_ONE:
PUSH(v7_mk_number(v7, 1));
reset_last_name(v7);
break;
case OP_PUSH_LIT: {
PUSH(bcode_decode_lit(v7, r.bcode, &r.ops));
#if !V7_DISABLE_CALL_ERROR_CONTEXT
/* name tracking */
if (!v7_is_string(TOS())) {
reset_last_name(v7);
}
#endif
break;
}
case OP_LOGICAL_NOT:
v1 = POP();
PUSH(v7_mk_boolean(v7, !v7_is_truthy(v7, v1)));
break;
case OP_NOT: {
v1 = POP();
BTRY(to_number_v(v7, v1, &v1));
PUSH(v7_mk_number(v7, ~(int32_t) v7_get_double(v7, v1)));
break;
}
case OP_NEG: {
v1 = POP();
BTRY(to_number_v(v7, v1, &v1));
PUSH(v7_mk_number(v7, -v7_get_double(v7, v1)));
break;
}
case OP_POS: {
v1 = POP();
BTRY(to_number_v(v7, v1, &v1));
PUSH(v1);
break;
}
case OP_ADD: {
v2 = POP();
v1 = POP();
/*
* If either operand is an object, convert both of them to primitives
*/
if (v7_is_object(v1) || v7_is_object(v2)) {
BTRY(to_primitive(v7, v1, V7_TO_PRIMITIVE_HINT_AUTO, &v1));
BTRY(to_primitive(v7, v2, V7_TO_PRIMITIVE_HINT_AUTO, &v2));
}
if (v7_is_string(v1) || v7_is_string(v2)) {
/* Convert both operands to strings, and concatenate */
BTRY(primitive_to_str(v7, v1, &v1, NULL, 0, NULL));
BTRY(primitive_to_str(v7, v2, &v2, NULL, 0, NULL));
PUSH(s_concat(v7, v1, v2));
} else {
/* Convert both operands to numbers, and sum */
BTRY(primitive_to_number(v7, v1, &v1));
BTRY(primitive_to_number(v7, v2, &v2));
PUSH(v7_mk_number(v7, b_num_bin_op(op, v7_get_double(v7, v1),
v7_get_double(v7, v2))));
}
break;
}
case OP_SUB:
case OP_REM:
case OP_MUL:
case OP_DIV:
case OP_LSHIFT:
case OP_RSHIFT:
case OP_URSHIFT:
case OP_OR:
case OP_XOR:
case OP_AND: {
v2 = POP();
v1 = POP();
BTRY(to_number_v(v7, v1, &v1));
BTRY(to_number_v(v7, v2, &v2));
PUSH(v7_mk_number(v7, b_num_bin_op(op, v7_get_double(v7, v1),
v7_get_double(v7, v2))));
break;
}
case OP_EQ_EQ: {
v2 = POP();
v1 = POP();
if (v7_is_string(v1) && v7_is_string(v2)) {
res = v7_mk_boolean(v7, s_cmp(v7, v1, v2) == 0);
} else if (v1 == v2 && v1 == V7_TAG_NAN) {
res = v7_mk_boolean(v7, 0);
} else {
res = v7_mk_boolean(v7, v1 == v2);
}
PUSH(res);
break;
}
case OP_NE_NE: {
v2 = POP();
v1 = POP();
if (v7_is_string(v1) && v7_is_string(v2)) {
res = v7_mk_boolean(v7, s_cmp(v7, v1, v2) != 0);
} else if (v1 == v2 && v1 == V7_TAG_NAN) {
res = v7_mk_boolean(v7, 1);
} else {
res = v7_mk_boolean(v7, v1 != v2);
}
PUSH(res);
break;
}
case OP_EQ:
case OP_NE: {
v2 = POP();
v1 = POP();
/*
* TODO(dfrank) : it's not really correct. Fix it accordingly to
* the p. 4.9 of The Definitive Guide (page 71)
*/
if (((v7_is_object(v1) || v7_is_object(v2)) && v1 == v2)) {
res = v7_mk_boolean(v7, op == OP_EQ);
PUSH(res);
break;
} else if (v7_is_undefined(v1) || v7_is_null(v1)) {
res = v7_mk_boolean(
v7, (op != OP_EQ) ^ (v7_is_undefined(v2) || v7_is_null(v2)));
PUSH(res);
break;
} else if (v7_is_undefined(v2) || v7_is_null(v2)) {
res = v7_mk_boolean(
v7, (op != OP_EQ) ^ (v7_is_undefined(v1) || v7_is_null(v1)));
PUSH(res);
break;
}
if (v7_is_string(v1) && v7_is_string(v2)) {
int cmp = s_cmp(v7, v1, v2);
switch (op) {
case OP_EQ:
res = v7_mk_boolean(v7, cmp == 0);
break;
case OP_NE:
res = v7_mk_boolean(v7, cmp != 0);
break;
default:
/* should never be here */
assert(0);
}
} else {
/* Convert both operands to numbers */
BTRY(to_number_v(v7, v1, &v1));
BTRY(to_number_v(v7, v2, &v2));
res = v7_mk_boolean(v7, b_bool_bin_op(op, v7_get_double(v7, v1),
v7_get_double(v7, v2)));
}
PUSH(res);
break;
}
case OP_LT:
case OP_LE:
case OP_GT:
case OP_GE: {
v2 = POP();
v1 = POP();
BTRY(to_primitive(v7, v1, V7_TO_PRIMITIVE_HINT_NUMBER, &v1));
BTRY(to_primitive(v7, v2, V7_TO_PRIMITIVE_HINT_NUMBER, &v2));
if (v7_is_string(v1) && v7_is_string(v2)) {
int cmp = s_cmp(v7, v1, v2);
switch (op) {
case OP_LT:
res = v7_mk_boolean(v7, cmp < 0);
break;
case OP_LE:
res = v7_mk_boolean(v7, cmp <= 0);
break;
case OP_GT:
res = v7_mk_boolean(v7, cmp > 0);
break;
case OP_GE:
res = v7_mk_boolean(v7, cmp >= 0);
break;
default:
/* should never be here */
assert(0);
}
} else {
/* Convert both operands to numbers */
BTRY(to_number_v(v7, v1, &v1));
BTRY(to_number_v(v7, v2, &v2));
res = v7_mk_boolean(v7, b_bool_bin_op(op, v7_get_double(v7, v1),
v7_get_double(v7, v2)));
}
PUSH(res);
break;
}
case OP_INSTANCEOF: {
v2 = POP();
v1 = POP();
if (!v7_is_callable(v7, v2)) {
BTRY(v7_throwf(v7, TYPE_ERROR,
"Expecting a function in instanceof check"));
goto op_done;
} else {
PUSH(v7_mk_boolean(
v7, is_prototype_of(v7, v1, v7_get(v7, v2, "prototype", 9))));
}
break;
}
case OP_TYPEOF:
v1 = POP();
switch (val_type(v7, v1)) {
case V7_TYPE_NUMBER:
res = v7_mk_string(v7, "number", 6, 1);
break;
case V7_TYPE_STRING:
res = v7_mk_string(v7, "string", 6, 1);
break;
case V7_TYPE_BOOLEAN:
res = v7_mk_string(v7, "boolean", 7, 1);
break;
case V7_TYPE_FUNCTION_OBJECT:
case V7_TYPE_CFUNCTION_OBJECT:
case V7_TYPE_CFUNCTION:
res = v7_mk_string(v7, "function", 8, 1);
break;
case V7_TYPE_UNDEFINED:
res = v7_mk_string(v7, "undefined", 9, 1);
break;
default:
res = v7_mk_string(v7, "object", 6, 1);
break;
}
PUSH(res);
break;
case OP_IN: {
struct v7_property *prop = NULL;
v2 = POP();
v1 = POP();
BTRY(to_string(v7, v1, NULL, buf, sizeof(buf), NULL));
prop = v7_get_property(v7, v2, buf, ~0);
PUSH(v7_mk_boolean(v7, prop != NULL));
} break;
case OP_GET:
v2 = POP();
v1 = POP();
BTRY(v7_get_throwing_v(v7, v1, v2, &v3));
PUSH(v3);
#if !V7_DISABLE_CALL_ERROR_CONTEXT
v7->vals.last_name[1] = v7->vals.last_name[0];
v7->vals.last_name[0] = v2;
#endif
break;
case OP_SET: {
v3 = POP();
v2 = POP();
v1 = POP();
/* convert name to string, if it's not already */
BTRY(to_string(v7, v2, &v2, NULL, 0, NULL));
/* set value */
BTRY(set_property_v(v7, v1, v2, v3, NULL));
PUSH(v3);
break;
}
case OP_GET_VAR:
case OP_SAFE_GET_VAR: {
struct v7_property *p = NULL;
assert(r.ops < r.end - 1);
v1 = bcode_decode_lit(v7, r.bcode, &r.ops);
BTRY(v7_get_property_v(v7, get_scope(v7), v1, &p));
if (p == NULL) {
if (op == OP_SAFE_GET_VAR) {
PUSH(V7_UNDEFINED);
} else {
/* variable does not exist: Reference Error */
V7_TRY(bcode_throw_reference_error(v7, &r, v1));
goto op_done;
}
break;
} else {
BTRY(v7_property_value(v7, get_scope(v7), p, &v2));
PUSH(v2);
}
#if !V7_DISABLE_CALL_ERROR_CONTEXT
v7->vals.last_name[0] = v1;
v7->vals.last_name[1] = V7_UNDEFINED;
#endif
break;
}
case OP_SET_VAR: {
struct v7_property *prop;
v3 = POP();
v2 = bcode_decode_lit(v7, r.bcode, &r.ops);
v1 = get_scope(v7);
BTRY(to_string(v7, v2, NULL, buf, sizeof(buf), NULL));
prop = v7_get_property(v7, v1, buf, strlen(buf));
if (prop != NULL) {
/* Property already exists: update its value */
/*
* TODO(dfrank): currently we can't use `def_property_v()` here,
* because if the property was already found somewhere in the
* prototype chain, then it should be updated, instead of creating a
* new one on the top of the scope.
*
* Probably we need to make `def_property_v()` more generic and
* use it here; or split `def_property_v()` into smaller pieces and
* use one of them here.
*/
if (!(prop->attributes & V7_PROPERTY_NON_WRITABLE)) {
prop->value = v3;
}
} else if (!r.bcode->strict_mode) {
/*
* Property does not exist: since we're not in strict mode, let's
* create new property at Global Object
*/
BTRY(set_property_v(v7, v7_get_global(v7), v2, v3, NULL));
} else {
/*
* In strict mode, throw reference error instead of polluting Global
* Object
*/
V7_TRY(bcode_throw_reference_error(v7, &r, v2));
goto op_done;
}
PUSH(v3);
break;
}
case OP_JMP: {
bcode_off_t target = bcode_get_target(&r.ops);
r.ops = r.bcode->ops.p + target - 1;
break;
}
case OP_JMP_FALSE: {
bcode_off_t target = bcode_get_target(&r.ops);
v1 = POP();
if (!v7_is_truthy(v7, v1)) {
r.ops = r.bcode->ops.p + target - 1;
}
break;
}
case OP_JMP_TRUE: {
bcode_off_t target = bcode_get_target(&r.ops);
v1 = POP();
if (v7_is_truthy(v7, v1)) {
r.ops = r.bcode->ops.p + target - 1;
}
break;
}
case OP_JMP_TRUE_DROP: {
bcode_off_t target = bcode_get_target(&r.ops);
v1 = POP();
if (v7_is_truthy(v7, v1)) {
r.ops = r.bcode->ops.p + target - 1;
v1 = POP();
POP();
PUSH(v1);
}
break;
}
case OP_JMP_IF_CONTINUE: {
bcode_off_t target = bcode_get_target(&r.ops);
if (v7->is_continuing) {
r.ops = r.bcode->ops.p + target - 1;
}
v7->is_continuing = 0;
break;
}
case OP_CREATE_OBJ:
PUSH(v7_mk_object(v7));
break;
case OP_CREATE_ARR:
PUSH(v7_mk_array(v7));
break;
case OP_PUSH_PROP_ITER_CTX: {
struct prop_iter_ctx *ctx =
(struct prop_iter_ctx *) calloc(1, sizeof(*ctx));
BTRY(init_prop_iter_ctx(v7, TOS(), 1, ctx));
v1 = v7_mk_object(v7);
v7_set_user_data(v7, v1, ctx);
v7_set_destructor_cb(v7, v1, prop_iter_ctx_dtor);
PUSH(v1);
break;
}
case OP_NEXT_PROP: {
struct prop_iter_ctx *ctx = NULL;
int ok = 0;
v1 = POP(); /* ctx */
v2 = POP(); /* object */
ctx = (struct prop_iter_ctx *) v7_get_user_data(v7, v1);
if (v7_is_object(v2)) {
v7_prop_attr_t attrs;
do {
/* iterate properties until we find a non-hidden enumerable one */
do {
BTRY(next_prop(v7, ctx, &res, NULL, &attrs, &ok));
} while (ok && (attrs & (_V7_PROPERTY_HIDDEN |
V7_PROPERTY_NON_ENUMERABLE)));
if (!ok) {
/* no more properties in this object: proceed to the prototype */
v2 = v7_get_proto(v7, v2);
if (get_generic_object_struct(v2) != NULL) {
/*
* the prototype is a generic object, so, init the context for
* props iteration
*/
v7_destruct_prop_iter_ctx(v7, ctx);
BTRY(init_prop_iter_ctx(v7, v2, 1, ctx));
} else {
/*
* we can't iterate the prototype's props, so, just stop
* iteration.
*/
ctx = NULL;
}
}
} while (!ok && ctx != NULL);
} else {
/*
* Not an object: reset the context.
*/
ctx = NULL;
}
if (ctx == NULL) {
PUSH(v7_mk_boolean(v7, 0));
/*
* We could leave the context unfreed, and let the
* `prop_iter_ctx_dtor()` free it when the v1 will be GC-d, but
* let's do that earlier.
*/
ctx = (struct prop_iter_ctx *) v7_get_user_data(v7, v1);
v7_destruct_prop_iter_ctx(v7, ctx);
free(ctx);
v7_set_user_data(v7, v1, NULL);
v7_set_destructor_cb(v7, v1, NULL);
} else {
PUSH(v2);
PUSH(v1);
PUSH(res);
PUSH(v7_mk_boolean(v7, 1));
}
break;
}
case OP_FUNC_LIT: {
v1 = POP();
v2 = bcode_instantiate_function(v7, v1);
PUSH(v2);
break;
}
case OP_CHECK_CALL:
v1 = TOS();
if (!v7_is_callable(v7, v1)) {
int arity = 0;
enum v7_err ignore;
/* tried to call non-function object: throw a TypeError */
#if !V7_DISABLE_CALL_ERROR_CONTEXT
/*
* try to provide some useful context for the error message
* using a good-enough heuristics
* but defer actual throw when process the incriminated call
* in order to evaluate the arguments as required by the spec.
*/
if (v7->last_ops[0] == OP_GET_VAR) {
arity = 1;
} else if (v7->last_ops[0] == OP_GET &&
v7->last_ops[1] == OP_PUSH_LIT) {
/*
* OP_PUSH_LIT is used to both push property names for OP_GET
* and for pushing actual literals. During PUSH_LIT push lit
* evaluation we reset the last name variable in case the literal
* is not a string, such as in `[].foo()`.
* Unfortunately it doesn't handle `"foo".bar()`; could be
* solved by adding another bytecode for property literals but
* probably it doesn't matter much.
*/
if (v7_is_undefined(v7->vals.last_name[1])) {
arity = 1;
} else {
arity = 2;
}
}
#endif
switch (arity) {
case 0:
ignore = v7_throwf(v7, TYPE_ERROR, "value is not a function");
break;
#if !V7_DISABLE_CALL_ERROR_CONTEXT
case 1:
ignore = v7_throwf(v7, TYPE_ERROR, "%s is not a function",
v7_get_cstring(v7, &v7->vals.last_name[0]));
break;
case 2:
ignore = v7_throwf(v7, TYPE_ERROR, "%s.%s is not a function",
v7_get_cstring(v7, &v7->vals.last_name[1]),
v7_get_cstring(v7, &v7->vals.last_name[0]));
break;
#endif
};
v7->vals.call_check_ex = v7->vals.thrown_error;
v7_clear_thrown_value(v7);
(void) ignore;
}
break;
case OP_CALL:
case OP_NEW: {
/* Naive implementation pending stack frame redesign */
int args = (int) *(++r.ops);
uint8_t is_constructor = (op == OP_NEW);
if (SP() < (args + 1 /*func*/ + 1 /*this*/)) {
BTRY(v7_throwf(v7, INTERNAL_ERROR, "stack underflow"));
goto op_done;
} else {
v2 = v7_mk_dense_array(v7);
while (args > 0) {
BTRY(v7_array_set_throwing(v7, v2, --args, POP(), NULL));
}
/* pop function to call */
v1 = POP();
/* pop `this` */
v3 = POP();
/*
* adjust `this` if the function is called with the constructor
* invocation pattern
*/
if (is_constructor) {
/*
* The function is invoked as a constructor: we ignore `this`
* value popped from stack, create new object and set prototype.
*/
/*
* get "prototype" property from the constructor function,
* and make sure it's an object
*/
v4 = v7_get(v7, v1 /*func*/, "prototype", 9);
if (!v7_is_object(v4)) {
/* TODO(dfrank): box primitive value */
BTRY(v7_throwf(
v7, TYPE_ERROR,
"Cannot set a primitive value as object prototype"));
goto op_done;
} else if (is_cfunction_lite(v4)) {
/*
* TODO(dfrank): maybe add support for a cfunction pointer to be
* a prototype
*/
BTRY(v7_throwf(v7, TYPE_ERROR,
"Not implemented: cfunction as a prototype"));
goto op_done;
}
/* create an object with given prototype */
v3 = mk_object(v7, v4 /*prototype*/);
v4 = V7_UNDEFINED;
}
if (!v7_is_callable(v7, v1)) {
/* tried to call non-function object: throw a TypeError */
BTRY(v7_throw(v7, v7->vals.call_check_ex));
goto op_done;
} else if (is_cfunction_lite(v1) || is_cfunction_obj(v7, v1)) {
/* call cfunction */
/*
* In "function invocation pattern", the `this` value popped from
* stack is an `undefined`. And in non-strict mode, we should change
* it to global object.
*/
if (!is_constructor && !r.bcode->strict_mode &&
v7_is_undefined(v3)) {
v3 = v7->vals.global_object;
}
BTRY(call_cfunction(v7, v1 /*func*/, v3 /*this*/, v2 /*args*/,
is_constructor, &v4));
/* push value returned from C function to bcode stack */
PUSH(v4);
} else {
char *ops;
struct v7_js_function *func = get_js_function_struct(v1);
/*
* In "function invocation pattern", the `this` value popped from
* stack is an `undefined`. And in non-strict mode, we should change
* it to global object.
*/
if (!is_constructor && !func->bcode->strict_mode &&
v7_is_undefined(v3)) {
v3 = v7->vals.global_object;
}
scope_frame = v7_mk_object(v7);
/*
* Before actual opcodes, `ops` contains one or more
* null-terminated strings: first of all, the function name (if the
* function is anonymous, it's an empty string).
*
* Then, argument names follow. We know number of arguments, so, we
* know how many names to take.
*
* And then, local variable names follow. We know total number of
* strings (`names_cnt`), so, again, we know how many names to
* take.
*/
ops = func->bcode->ops.p;
/* populate function itself */
ops = bcode_next_name_v(v7, func->bcode, ops, &v4);
BTRY(def_property_v(v7, scope_frame, v4, V7_DESC_CONFIGURABLE(0),
v1, 0 /*not assign*/, NULL));
/* populate arguments */
{
int arg_num;
for (arg_num = 0; arg_num < func->bcode->args_cnt; ++arg_num) {
ops = bcode_next_name_v(v7, func->bcode, ops, &v4);
BTRY(def_property_v(
v7, scope_frame, v4, V7_DESC_CONFIGURABLE(0),
v7_array_get(v7, v2, arg_num), 0 /*not assign*/, NULL));
}
}
/* populate `arguments` object */
/*
* TODO(dfrank): it's actually much more complicated than that:
* it's not an array, it's an array-like object. More, in
* non-strict mode, elements of `arguments` object are just aliases
* for actual arguments, so this one:
*
* `(function(a){arguments[0]=2; return a;})(1);`
*
* should yield 2. Currently, it yields 1.
*/
v7_def(v7, scope_frame, "arguments", 9, V7_DESC_CONFIGURABLE(0),
v2);
/* populate local variables */
{
uint8_t loc_num;
uint8_t loc_cnt = func->bcode->names_cnt - func->bcode->args_cnt -
1 /*func name*/;
for (loc_num = 0; loc_num < loc_cnt; ++loc_num) {
ops = bcode_next_name_v(v7, func->bcode, ops, &v4);
BTRY(def_property_v(v7, scope_frame, v4,
V7_DESC_CONFIGURABLE(0), V7_UNDEFINED,
0 /*not assign*/, NULL));
}
}
/* transfer control to the function */
V7_TRY(bcode_perform_call(v7, scope_frame, func, &r, v3 /*this*/,
ops, is_constructor));
scope_frame = V7_UNDEFINED;
}
}
break;
}
case OP_RET:
bcode_adjust_retval(v7, 1 /*explicit return*/);
V7_TRY(bcode_perform_return(v7, &r, 1 /*take value from stack*/));
break;
case OP_DELETE:
case OP_DELETE_VAR: {
size_t name_len;
struct v7_property *prop;
res = v7_mk_boolean(v7, 1);
/* pop property name to delete */
v2 = POP();
if (op == OP_DELETE) {
/* pop object to delete the property from */
v1 = POP();
} else {
/* use scope as an object to delete the property from */
v1 = get_scope(v7);
}
if (!v7_is_object(v1)) {
/*
* the "object" to delete a property from is not actually an object
* (at least this can happen with cfunction pointers), will just
* return `true`
*/
goto delete_clean;
}
BTRY(to_string(v7, v2, NULL, buf, sizeof(buf), &name_len));
prop = v7_get_property(v7, v1, buf, name_len);
if (prop == NULL) {
/* not found a property; will just return `true` */
goto delete_clean;
}
/* found needed property */
if (prop->attributes & V7_PROPERTY_NON_CONFIGURABLE) {
/*
* this property is undeletable. In non-strict mode, we just
* return `false`; otherwise, we throw.
*/
if (!r.bcode->strict_mode) {
res = v7_mk_boolean(v7, 0);
} else {
BTRY(v7_throwf(v7, TYPE_ERROR, "Cannot delete property '%s'", buf));
goto op_done;
}
} else {
/*
* delete property: when we operate on the current scope, we should
* walk the prototype chain when deleting property.
*
* But when we operate on a "real" object, we should delete own
* properties only.
*/
if (op == OP_DELETE) {
v7_del(v7, v1, buf, name_len);
} else {
del_property_deep(v7, v1, buf, name_len);
}
}
delete_clean:
PUSH(res);
break;
}
case OP_TRY_PUSH_CATCH:
case OP_TRY_PUSH_FINALLY:
case OP_TRY_PUSH_LOOP:
case OP_TRY_PUSH_SWITCH:
eval_try_push(v7, op, &r);
break;
case OP_TRY_POP:
V7_TRY(eval_try_pop(v7));
break;
case OP_AFTER_FINALLY:
/*
* exited from `finally` block: if some value is currently being
* returned, continue returning it.
*
* Likewise, if some value is currently being thrown, continue
* unwinding stack.
*/
if (v7->is_thrown) {
V7_TRY(
bcode_perform_throw(v7, &r, 0 /*don't take value from stack*/));
goto op_done;
} else if (v7->is_returned) {
V7_TRY(
bcode_perform_return(v7, &r, 0 /*don't take value from stack*/));
break;
} else if (v7->is_breaking) {
bcode_perform_break(v7, &r);
}
break;
case OP_THROW:
V7_TRY(bcode_perform_throw(v7, &r, 1 /*take thrown value*/));
goto op_done;
case OP_BREAK:
bcode_perform_break(v7, &r);
break;
case OP_CONTINUE:
v7->is_continuing = 1;
bcode_perform_break(v7, &r);
break;
case OP_ENTER_CATCH: {
/* pop thrown value from stack */
v1 = POP();
/* get the name of the thrown value */
v2 = bcode_decode_lit(v7, r.bcode, &r.ops);
/*
* create a new stack frame (a "private" one), and set exception
* property on it
*/
scope_frame = v7_mk_object(v7);
BTRY(set_property_v(v7, scope_frame, v2, v1, NULL));
/* Push this "private" frame on the call stack */
/* new scope_frame will inherit from the current scope */
obj_prototype_set(v7, get_object_struct(scope_frame),
get_object_struct(get_scope(v7)));
/*
* Create new `call_frame` which will replace `v7->call_stack`.
*/
append_call_frame_private(v7, scope_frame);
break;
}
case OP_EXIT_CATCH: {
v7_call_frame_mask_t frame_type_mask;
/* unwind 1 frame */
frame_type_mask = unwind_stack_1level(v7, &r);
/* make sure the unwound frame is a "private" frame */
assert(frame_type_mask == V7_CALL_FRAME_MASK_PRIVATE);
#if defined(NDEBUG)
(void) frame_type_mask;
#endif
break;
}
default:
BTRY(v7_throwf(v7, INTERNAL_ERROR, "Unknown opcode: %d", (int) op));
goto op_done;
}
op_done:
#ifdef V7_BCODE_TRACE
/* print current stack state */
{
char buf[40];
char *str = v7_stringify(v7, TOS(), buf, sizeof(buf), V7_STRINGIFY_DEBUG);
fprintf(stderr, " stack size: %u, TOS: '%s'\n",
(unsigned int) (v7->stack.len / sizeof(val_t)), str);
if (str != buf) {
free(str);
}
#ifdef V7_BCODE_TRACE_STACK
{
size_t i;
for (i = 0; i < (v7->stack.len / sizeof(val_t)); i++) {
char *str = v7_stringify(v7, stack_at(&v7->stack, i), buf,
sizeof(buf), V7_STRINGIFY_DEBUG);
fprintf(stderr, " #: '%s'\n", str);
if (str != buf) {
free(str);
}
}
}
#endif
}
#endif
if (r.need_inc_ops) {
r.ops++;
}
}
/* implicit return */
if (v7->call_stack != v7->bottom_call_frame) {
#ifdef V7_BCODE_TRACE
fprintf(stderr, "return implicitly\n");
#endif
bcode_adjust_retval(v7, 0 /*implicit return*/);
V7_TRY(bcode_perform_return(v7, &r, 1));
goto restart;
} else {
#ifdef V7_BCODE_TRACE
const char *s = (get_scope(v7) != v7->vals.global_object)
? "not global object"
: "global object";
fprintf(stderr, "reached bottom_call_frame (%s)\n", s);
#endif
}
clean:
if (rcode == V7_OK) {
/*
* bcode evaluated successfully. Make sure try stack is empty.
* (data stack will be checked below, in `clean`)
*/
#ifndef NDEBUG
{
unsigned long try_stack_len =
v7_array_length(v7, find_call_frame_private(v7)->vals.try_stack);
if (try_stack_len != 0) {
fprintf(stderr, "try_stack_len=%lu, should be 0\n", try_stack_len);
}
assert(try_stack_len == 0);
}
#endif
/* get the value returned from the evaluated script */
*_res = POP();
}
assert(v7->bottom_call_frame == v7->call_stack);
unwind_stack_1level(v7, NULL);
v7->bottom_call_frame = saved_bottom_call_frame;
tmp_frame_cleanup(&tf);
return rcode;
}
/*
* TODO(dfrank) this function is probably too overloaded: it handles both
* `v7_exec` and `v7_apply`. Read below why it's written this way, but it's
* probably a good idea to factor out common functionality in some other
* function.
*
* If `src` is not `NULL`, then we behave in favour of `v7_exec`: parse,
* compile, and evaluate the script. The `func` and `args` are ignored.
*
* If, however, `src` is `NULL`, then we behave in favour of `v7_apply`: we
* call the provided `func` with `args`. But unlike interpreter, we can't just
* call the provided function: we need to setup environment for this call.
*
* Currently, we just quickly generate the "wrapper" bcode for the function.
* This wrapper bcode looks like this:
*
* OP_PUSH_UNDEFINED
* OP_PUSH_LIT # push this
* OP_PUSH_LIT # push function
* OP_PUSH_LIT # push arg1
* OP_PUSH_LIT # push arg2
* ...
* OP_PUSH_LIT # push argN
* OP_CALL(N) # call function with N arguments
* OP_SWAP_DROP
*
* and then, bcode evaluator proceeds with this code.
*
* In fact, both cases (eval or apply) are quite similar: we should prepare
* environment for the bcode evaluation in exactly the same way, and the only
* different part is where we get the bcode from. This is why that
* functionality is baked in the single function, but it would be good to make
* it suck less.
*/
V7_PRIVATE enum v7_err b_exec(struct v7 *v7, const char *src, size_t src_len,
const char *filename, val_t func, val_t args,
val_t this_object, int is_json, int fr,
uint8_t is_constructor, val_t *res) {
#if defined(V7_BCODE_TRACE_SRC)
fprintf(stderr, "src:'%s'\n", src);
#endif
/* TODO(mkm): use GC pool */
#if !defined(V7_NO_COMPILER)
struct ast *a = (struct ast *) malloc(sizeof(struct ast));
#endif
size_t saved_stack_len = v7->stack.len;
enum v7_err rcode = V7_OK;
val_t _res = V7_UNDEFINED;
struct gc_tmp_frame tf = new_tmp_frame(v7);
struct bcode *bcode = NULL;
#if V7_ENABLE_STACK_TRACKING
struct stack_track_ctx stack_track_ctx;
#endif
struct {
unsigned noopt : 1;
unsigned line_no_reset : 1;
} flags = {0, 0};
(void) filename;
#if V7_ENABLE_STACK_TRACKING
v7_stack_track_start(v7, &stack_track_ctx);
#endif
tmp_stack_push(&tf, &func);
tmp_stack_push(&tf, &args);
tmp_stack_push(&tf, &this_object);
tmp_stack_push(&tf, &_res);
/* init new bcode */
bcode = (struct bcode *) calloc(1, sizeof(*bcode));
bcode_init(bcode,
#ifndef V7_FORCE_STRICT_MODE
0,
#else
1,
#endif
#if !V7_DISABLE_FILENAMES
filename ? shdata_create_from_string(filename) : NULL,
#else
NULL,
#endif
0 /*filename not in ROM*/
);
retain_bcode(v7, bcode);
own_bcode(v7, bcode);
#if !defined(V7_NO_COMPILER)
ast_init(a, 0);
a->refcnt = 1;
#endif
if (src != NULL) {
/* Caller provided some source code, so, handle it somehow */
flags.line_no_reset = 1;
if (src_len >= sizeof(BIN_BCODE_SIGNATURE) &&
strncmp(BIN_BCODE_SIGNATURE, src, sizeof(BIN_BCODE_SIGNATURE)) == 0) {
/* we have a serialized bcode */
bcode_deserialize(v7, bcode, src + sizeof(BIN_BCODE_SIGNATURE));
/*
* Currently, we only support serialized bcode that is stored in some
* mmapped memory. Otherwise, we don't yet have any mechanism to free
* this memory at the appropriate time.
*/
/*
* TODO(dfrank): currently, we remove this assert, and introduce memory
* leak. We need to support that properly.
*/
#if 0
assert(fr == 0);
#else
if (fr) {
fr = 0;
}
#endif
} else {
/* Maybe regular JavaScript source or binary AST data */
#if !defined(V7_NO_COMPILER)
if (src_len >= sizeof(BIN_AST_SIGNATURE) &&
strncmp(BIN_AST_SIGNATURE, src, sizeof(BIN_AST_SIGNATURE)) == 0) {
/* we have binary AST data */
if (fr == 0) {
/* Unmanaged memory, usually rom or mmapped flash */
mbuf_free(&a->mbuf);
a->mbuf.buf = (char *) (src + sizeof(BIN_AST_SIGNATURE));
a->mbuf.size = a->mbuf.len = src_len - sizeof(BIN_AST_SIGNATURE);
a->refcnt++; /* prevent freeing */
flags.noopt = 1;
} else {
mbuf_append(&a->mbuf, src + sizeof(BIN_AST_SIGNATURE),
src_len - sizeof(BIN_AST_SIGNATURE));
}
} else {
/* we have regular JavaScript source, so, parse it */
V7_TRY(parse(v7, a, src, src_len, is_json));
}
/* we now have binary AST, let's compile it */
if (!flags.noopt) {
ast_optimize(a);
}
#if V7_ENABLE__Memory__stats
v7->function_arena_ast_size += a->mbuf.size;
#endif
if (v7_is_undefined(this_object)) {
this_object = v7->vals.global_object;
}
if (!is_json) {
V7_TRY(compile_script(v7, a, bcode));
} else {
ast_off_t pos = 0;
V7_TRY(compile_expr(v7, a, &pos, bcode));
}
#else /* V7_NO_COMPILER */
(void) is_json;
/* Parsing JavaScript code is disabled */
rcode = v7_throwf(v7, SYNTAX_ERROR,
"Parsing JS code is disabled by V7_NO_COMPILER");
V7_THROW(V7_SYNTAX_ERROR);
#endif /* V7_NO_COMPILER */
}
} else if (is_js_function(func)) {
/*
* Caller did not provide source code, so, assume we should call
* provided function. Here, we prepare "wrapper" bcode.
*/
struct bcode_builder bbuilder;
lit_t lit;
int args_cnt = v7_array_length(v7, args);
bcode_builder_init(v7, &bbuilder, bcode);
bcode_op(&bbuilder, OP_PUSH_UNDEFINED);
/* push `this` */
lit = bcode_add_lit(&bbuilder, this_object);
bcode_push_lit(&bbuilder, lit);
/* push func literal */
lit = bcode_add_lit(&bbuilder, func);
bcode_push_lit(&bbuilder, lit);
/* push args */
{
int i;
for (i = 0; i < args_cnt; i++) {
lit = bcode_add_lit(&bbuilder, v7_array_get(v7, args, i));
bcode_push_lit(&bbuilder, lit);
}
}
bcode_op(&bbuilder, OP_CALL);
/* TODO(dfrank): check if args <= 0x7f */
bcode_op(&bbuilder, (uint8_t) args_cnt);
bcode_op(&bbuilder, OP_SWAP_DROP);
bcode_builder_finalize(&bbuilder);
} else if (is_cfunction_lite(func) || is_cfunction_obj(v7, func)) {
/* call cfunction */
V7_TRY(call_cfunction(v7, func, this_object, args, is_constructor, &_res));
goto clean;
} else {
/* value is not a function */
V7_TRY(v7_throwf(v7, TYPE_ERROR, "value is not a function"));
}
/* We now have bcode to evaluate; proceed to it */
#if !defined(V7_NO_COMPILER)
/*
* Before we evaluate bcode, we can safely release AST since it's not needed
* anymore. Note that there's no leak here: if we `goto clean` from somewhere
* above, we'll anyway release the AST under `clean` as well.
*/
release_ast(v7, a);
a = NULL;
#endif /* V7_NO_COMPILER */
/* Evaluate bcode */
V7_TRY(eval_bcode(v7, bcode, this_object, flags.line_no_reset, &_res));
clean:
/* free `src` if needed */
/*
* TODO(dfrank) : free it above, just after parsing, and make sure you use
* V7_TRY2() with custom label instead of V7_TRY()
*/
if (src != NULL && fr) {
free((void *) src);
}
/* disown and release current bcode */
disown_bcode(v7, bcode);
release_bcode(v7, bcode);
bcode = NULL;
if (rcode != V7_OK) {
/* some exception happened. */
_res = v7->vals.thrown_error;
/*
* if this is a top-level bcode, clear thrown error from the v7 context
*
* TODO(dfrank): do we really need to do this?
*
* If we don't clear the error, then we should clear it manually after each
* call to v7_exec() or friends; otherwise, all the following calls will
* see this error.
*
* On the other hand, user would still need to clear the error if he calls
* v7_exec() from some cfunction. So, currently, sometimes we don't need
* to clear the error, and sometimes we do, which is confusing.
*/
if (v7->act_bcodes.len == 0) {
v7->vals.thrown_error = V7_UNDEFINED;
v7->is_thrown = 0;
}
}
/*
* Data stack should have the same length as it was before evaluating script.
*/
if (v7->stack.len != saved_stack_len) {
fprintf(stderr, "len=%d, saved=%d\n", (int) v7->stack.len,
(int) saved_stack_len);
}
assert(v7->stack.len == saved_stack_len);
#if !defined(V7_NO_COMPILER)
/*
* release AST if needed (normally, it's already released above, before
* bcode evaluation)
*/
if (a != NULL) {
release_ast(v7, a);
a = NULL;
}
#endif /* V7_NO_COMPILER */
if (is_constructor && !v7_is_object(_res)) {
/* constructor returned non-object: replace it with `this` */
_res = v7_get_this(v7);
}
/* Provide the caller with the result, if asked to do so */
if (res != NULL) {
*res = _res;
}
#if V7_ENABLE_STACK_TRACKING
{
int diff = v7_stack_track_end(v7, &stack_track_ctx);
if (diff > v7->stack_stat[V7_STACK_STAT_EXEC]) {
v7->stack_stat[V7_STACK_STAT_EXEC] = diff;
}
}
#endif
tmp_frame_cleanup(&tf);
return rcode;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err b_apply(struct v7 *v7, v7_val_t func, v7_val_t this_obj,
v7_val_t args, uint8_t is_constructor,
v7_val_t *res) {
return b_exec(v7, NULL, 0, NULL, func, args, this_obj, 0, 0, is_constructor,
res);
}
#ifdef V7_MODULE_LINES
#line 1 "v7/src/core.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "v7/builtin/builtin.h" */
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/object.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* Amalgamated: #include "v7/src/primitive.h" */
/* Amalgamated: #include "v7/src/array.h" */
/* Amalgamated: #include "v7/src/slre.h" */
/* Amalgamated: #include "v7/src/bcode.h" */
/* Amalgamated: #include "v7/src/stdlib.h" */
/* Amalgamated: #include "v7/src/gc.h" */
/* Amalgamated: #include "v7/src/heapusage.h" */
/* Amalgamated: #include "v7/src/eval.h" */
#ifdef V7_THAW
extern struct v7_vals *fr_vals;
#endif
#ifdef HAS_V7_INFINITY
double _v7_infinity;
#endif
#ifdef HAS_V7_NAN
double _v7_nan;
#endif
#if defined(V7_CYG_PROFILE_ON)
struct v7 *v7_head = NULL;
#endif
static void generic_object_destructor(struct v7 *v7, void *ptr) {
struct v7_generic_object *o = (struct v7_generic_object *) ptr;
struct v7_property *p;
struct mbuf *abuf;
/* TODO(mkm): make regexp use user data API */
p = v7_get_own_property2(v7, v7_object_to_value(&o->base), "", 0,
_V7_PROPERTY_HIDDEN);
#if V7_ENABLE__RegExp
if (p != NULL && (p->value & V7_TAG_MASK) == V7_TAG_REGEXP) {
struct v7_regexp *rp = (struct v7_regexp *) get_ptr(p->value);
v7_disown(v7, &rp->regexp_string);
slre_free(rp->compiled_regexp);
free(rp);
}
#endif
if (o->base.attributes & V7_OBJ_DENSE_ARRAY) {
if (p != NULL &&
((abuf = (struct mbuf *) v7_get_ptr(v7, p->value)) != NULL)) {
mbuf_free(abuf);
free(abuf);
}
}
if (o->base.attributes & V7_OBJ_HAS_DESTRUCTOR) {
struct v7_property *p;
for (p = o->base.properties; p != NULL; p = p->next) {
if (p->attributes & _V7_PROPERTY_USER_DATA_AND_DESTRUCTOR) {
if (v7_is_foreign(p->name)) {
v7_destructor_cb_t *cb =
(v7_destructor_cb_t *) v7_get_ptr(v7, p->name);
cb(v7, v7_get_ptr(v7, p->value));
}
break;
}
}
}
#if V7_ENABLE_ENTITY_IDS
o->base.entity_id_base = V7_ENTITY_ID_PART_NONE;
o->base.entity_id_spec = V7_ENTITY_ID_PART_NONE;
#endif
}
static void function_destructor(struct v7 *v7, void *ptr) {
struct v7_js_function *f = (struct v7_js_function *) ptr;
(void) v7;
if (f == NULL) return;
if (f->bcode != NULL) {
release_bcode(v7, f->bcode);
}
#if V7_ENABLE_ENTITY_IDS
f->base.entity_id_base = V7_ENTITY_ID_PART_NONE;
f->base.entity_id_spec = V7_ENTITY_ID_PART_NONE;
#endif
}
#if V7_ENABLE_ENTITY_IDS
static void property_destructor(struct v7 *v7, void *ptr) {
struct v7_property *p = (struct v7_property *) ptr;
(void) v7;
if (p == NULL) return;
p->entity_id = V7_ENTITY_ID_NONE;
}
#endif
struct v7 *v7_create(void) {
struct v7_create_opts opts;
memset(&opts, 0, sizeof(opts));
return v7_create_opt(opts);
}
struct v7 *v7_create_opt(struct v7_create_opts opts) {
struct v7 *v7 = NULL;
char z = 0;
#if defined(HAS_V7_INFINITY) || defined(HAS_V7_NAN)
double zero = 0.0;
#endif
#ifdef HAS_V7_INFINITY
_v7_infinity = 1.0 / zero;
#endif
#ifdef HAS_V7_NAN
_v7_nan = zero / zero;
#endif
if (opts.object_arena_size == 0) opts.object_arena_size = 200;
if (opts.function_arena_size == 0) opts.function_arena_size = 100;
if (opts.property_arena_size == 0) opts.property_arena_size = 400;
if ((v7 = (struct v7 *) calloc(1, sizeof(*v7))) != NULL) {
#ifdef V7_STACK_SIZE
v7->sp_limit = (void *) ((uintptr_t) opts.c_stack_base - (V7_STACK_SIZE));
v7->sp_lwm = opts.c_stack_base;
#ifdef V7_STACK_GUARD_MIN_SIZE
v7_sp_limit = v7->sp_limit;
#endif
#endif
#if defined(V7_CYG_PROFILE_ON)
v7->next_v7 = v7_head;
v7_head = v7;
#endif
#if !V7_DISABLE_STR_ALLOC_SEQ
v7->gc_next_asn = 0;
v7->gc_min_asn = 0;
#endif
v7->cur_dense_prop =
(struct v7_property *) calloc(1, sizeof(struct v7_property));
gc_arena_init(&v7->generic_object_arena, sizeof(struct v7_generic_object),
opts.object_arena_size, 10, "object");
v7->generic_object_arena.destructor = generic_object_destructor;
gc_arena_init(&v7->function_arena, sizeof(struct v7_js_function),
opts.function_arena_size, 10, "function");
v7->function_arena.destructor = function_destructor;
gc_arena_init(&v7->property_arena, sizeof(struct v7_property),
opts.property_arena_size, 10, "property");
#if V7_ENABLE_ENTITY_IDS
v7->property_arena.destructor = property_destructor;
#endif
/*
* The compacting GC exploits the null terminator of the previous
* string as marker.
*/
mbuf_append(&v7->owned_strings, &z, 1);
v7->inhibit_gc = 1;
v7->vals.thrown_error = V7_UNDEFINED;
v7->call_stack = NULL;
v7->bottom_call_frame = NULL;
#if defined(V7_THAW) && !defined(V7_FREEZE_NOT_READONLY)
{
struct v7_generic_object *obj;
v7->vals = *fr_vals;
v7->vals.global_object = v7_mk_object(v7);
/*
* The global object has to be mutable.
*/
obj = get_generic_object_struct(v7->vals.global_object);
*obj = *get_generic_object_struct(fr_vals->global_object);
obj->base.attributes &= ~(V7_OBJ_NOT_EXTENSIBLE | V7_OBJ_OFF_HEAP);
v7_set(v7, v7->vals.global_object, "global", 6, v7->vals.global_object);
}
#else
init_stdlib(v7);
init_file(v7);
init_crypto(v7);
init_socket(v7);
#endif
v7->inhibit_gc = 0;
}
return v7;
}
val_t v7_get_global(struct v7 *v7) {
return v7->vals.global_object;
}
void v7_destroy(struct v7 *v7) {
if (v7 == NULL) return;
gc_arena_destroy(v7, &v7->generic_object_arena);
gc_arena_destroy(v7, &v7->function_arena);
gc_arena_destroy(v7, &v7->property_arena);
mbuf_free(&v7->owned_strings);
mbuf_free(&v7->owned_values);
mbuf_free(&v7->foreign_strings);
mbuf_free(&v7->json_visited_stack);
mbuf_free(&v7->tmp_stack);
mbuf_free(&v7->act_bcodes);
mbuf_free(&v7->stack);
#if defined(V7_CYG_PROFILE_ON)
/* delete this v7 */
{
struct v7 *v, **prevp = &v7_head;
for (v = v7_head; v != NULL; prevp = &v->next_v7, v = v->next_v7) {
if (v == v7) {
*prevp = v->next_v7;
break;
}
}
}
#endif
free(v7->cur_dense_prop);
free(v7);
}
v7_val_t v7_get_this(struct v7 *v7) {
/*
* By default, when there's no active call frame, will return Global Object
*/
v7_val_t ret = v7->vals.global_object;
struct v7_call_frame_base *call_frame =
find_call_frame(v7, V7_CALL_FRAME_MASK_BCODE | V7_CALL_FRAME_MASK_CFUNC);
if (call_frame != NULL) {
if (call_frame->type_mask & V7_CALL_FRAME_MASK_BCODE) {
ret = ((struct v7_call_frame_bcode *) call_frame)->vals.this_obj;
} else if (call_frame->type_mask & V7_CALL_FRAME_MASK_CFUNC) {
ret = ((struct v7_call_frame_cfunc *) call_frame)->vals.this_obj;
} else {
assert(0);
}
}
return ret;
}
V7_PRIVATE v7_val_t get_scope(struct v7 *v7) {
struct v7_call_frame_private *call_frame =
(struct v7_call_frame_private *) find_call_frame(
v7, V7_CALL_FRAME_MASK_PRIVATE);
if (call_frame != NULL) {
return call_frame->vals.scope;
} else {
/* No active call frame, return global object */
return v7->vals.global_object;
}
}
V7_PRIVATE uint8_t is_strict_mode(struct v7 *v7) {
struct v7_call_frame_bcode *call_frame =
(struct v7_call_frame_bcode *) find_call_frame(v7,
V7_CALL_FRAME_MASK_BCODE);
if (call_frame != NULL) {
return call_frame->bcode->strict_mode;
} else {
/* No active call frame, assume no strict mode */
return 0;
}
}
v7_val_t v7_get_arguments(struct v7 *v7) {
return v7->vals.arguments;
}
v7_val_t v7_arg(struct v7 *v7, unsigned long n) {
return v7_array_get(v7, v7->vals.arguments, n);
}
unsigned long v7_argc(struct v7 *v7) {
return v7_array_length(v7, v7->vals.arguments);
}
void v7_own(struct v7 *v7, v7_val_t *v) {
heapusage_dont_count(1);
mbuf_append(&v7->owned_values, &v, sizeof(v));
heapusage_dont_count(0);
}
int v7_disown(struct v7 *v7, v7_val_t *v) {
v7_val_t **vp =
(v7_val_t **) (v7->owned_values.buf + v7->owned_values.len - sizeof(v));
for (; (char *) vp >= v7->owned_values.buf; vp--) {
if (*vp == v) {
*vp = *(v7_val_t **) (v7->owned_values.buf + v7->owned_values.len -
sizeof(v));
v7->owned_values.len -= sizeof(v);
return 1;
}
}
return 0;
}
void v7_set_gc_enabled(struct v7 *v7, int enabled) {
v7->inhibit_gc = !enabled;
}
void v7_interrupt(struct v7 *v7) {
v7->interrupted = 1;
}
const char *v7_get_parser_error(struct v7 *v7) {
return v7->error_msg;
}
#if V7_ENABLE_STACK_TRACKING
int v7_stack_stat(struct v7 *v7, enum v7_stack_stat_what what) {
assert(what < V7_STACK_STATS_CNT);
return v7->stack_stat[what];
}
void v7_stack_stat_clean(struct v7 *v7) {
memset(v7->stack_stat, 0x00, sizeof(v7->stack_stat));
}
#endif /* V7_ENABLE_STACK_TRACKING */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/primitive.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* Amalgamated: #include "v7/src/primitive.h" */
/* Number {{{ */
NOINSTR static v7_val_t mk_number(double v) {
val_t res;
/* not every NaN is a JS NaN */
if (isnan(v)) {
res = V7_TAG_NAN;
} else {
union {
double d;
val_t r;
} u;
u.d = v;
res = u.r;
}
return res;
}
NOINSTR static double get_double(val_t v) {
union {
double d;
val_t v;
} u;
u.v = v;
/* Due to NaN packing, any non-numeric value is already a valid NaN value */
return u.d;
}
NOINSTR static v7_val_t mk_boolean(int v) {
return (!!v) | V7_TAG_BOOLEAN;
}
NOINSTR static int get_bool(val_t v) {
if (v7_is_boolean(v)) {
return v & 1;
} else {
return 0;
}
}
NOINSTR v7_val_t v7_mk_number(struct v7 *v7, double v) {
(void) v7;
return mk_number(v);
}
NOINSTR double v7_get_double(struct v7 *v7, v7_val_t v) {
(void) v7;
return get_double(v);
}
NOINSTR int v7_get_int(struct v7 *v7, v7_val_t v) {
(void) v7;
return (int) get_double(v);
}
int v7_is_number(val_t v) {
return v == V7_TAG_NAN || !isnan(get_double(v));
}
V7_PRIVATE int is_finite(struct v7 *v7, val_t v) {
return v7_is_number(v) && v != V7_TAG_NAN && !isinf(v7_get_double(v7, v));
}
/* }}} Number */
/* Boolean {{{ */
NOINSTR v7_val_t v7_mk_boolean(struct v7 *v7, int v) {
(void) v7;
return mk_boolean(v);
}
NOINSTR int v7_get_bool(struct v7 *v7, val_t v) {
(void) v7;
return get_bool(v);
}
int v7_is_boolean(val_t v) {
return (v & V7_TAG_MASK) == V7_TAG_BOOLEAN;
}
/* }}} Boolean */
/* null {{{ */
NOINSTR v7_val_t v7_mk_null(void) {
return V7_NULL;
}
int v7_is_null(val_t v) {
return v == V7_NULL;
}
/* }}} null */
/* undefined {{{ */
NOINSTR v7_val_t v7_mk_undefined(void) {
return V7_UNDEFINED;
}
int v7_is_undefined(val_t v) {
return v == V7_UNDEFINED;
}
/* }}} undefined */
/* Foreign {{{ */
V7_PRIVATE val_t pointer_to_value(void *p) {
uint64_t n = ((uint64_t)(uintptr_t) p);
assert((n & V7_TAG_MASK) == 0 || (n & V7_TAG_MASK) == (~0 & V7_TAG_MASK));
return n & ~V7_TAG_MASK;
}
V7_PRIVATE void *get_ptr(val_t v) {
return (void *) (uintptr_t)(v & 0xFFFFFFFFFFFFUL);
}
NOINSTR void *v7_get_ptr(struct v7 *v7, val_t v) {
(void) v7;
if (!v7_is_foreign(v)) {
return NULL;
}
return get_ptr(v);
}
NOINSTR v7_val_t v7_mk_foreign(struct v7 *v7, void *p) {
(void) v7;
return pointer_to_value(p) | V7_TAG_FOREIGN;
}
int v7_is_foreign(val_t v) {
return (v & V7_TAG_MASK) == V7_TAG_FOREIGN;
}
/* }}} Foreign */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/function.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/primitive.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* Amalgamated: #include "v7/src/function.h" */
/* Amalgamated: #include "v7/src/gc.h" */
/* Amalgamated: #include "v7/src/object.h" */
static val_t js_function_to_value(struct v7_js_function *o) {
return pointer_to_value(o) | V7_TAG_FUNCTION;
}
V7_PRIVATE struct v7_js_function *get_js_function_struct(val_t v) {
struct v7_js_function *ret = NULL;
assert(is_js_function(v));
ret = (struct v7_js_function *) get_ptr(v);
#if V7_ENABLE_ENTITY_IDS
if (ret->base.entity_id_spec != V7_ENTITY_ID_PART_JS_FUNC) {
fprintf(stderr, "entity_id: not a function!\n");
abort();
} else if (ret->base.entity_id_base != V7_ENTITY_ID_PART_OBJ) {
fprintf(stderr, "entity_id: not an object!\n");
abort();
}
#endif
return ret;
}
V7_PRIVATE
val_t mk_js_function(struct v7 *v7, struct v7_generic_object *scope,
val_t proto) {
struct v7_js_function *f;
val_t fval = V7_NULL;
struct gc_tmp_frame tf = new_tmp_frame(v7);
tmp_stack_push(&tf, &proto);
tmp_stack_push(&tf, &fval);
f = new_function(v7);
if (f == NULL) {
/* fval is left `null` */
goto cleanup;
}
#if V7_ENABLE_ENTITY_IDS
f->base.entity_id_base = V7_ENTITY_ID_PART_OBJ;
f->base.entity_id_spec = V7_ENTITY_ID_PART_JS_FUNC;
#endif
fval = js_function_to_value(f);
f->base.properties = NULL;
f->scope = scope;
/*
* Before setting a `V7_OBJ_FUNCTION` flag, make sure we don't have
* `V7_OBJ_DENSE_ARRAY` flag set
*/
assert(!(f->base.attributes & V7_OBJ_DENSE_ARRAY));
f->base.attributes |= V7_OBJ_FUNCTION;
/* TODO(mkm): lazily create these properties on first access */
if (v7_is_object(proto)) {
v7_def(v7, proto, "constructor", 11, V7_DESC_ENUMERABLE(0), fval);
v7_def(v7, fval, "prototype", 9,
V7_DESC_ENUMERABLE(0) | V7_DESC_CONFIGURABLE(0), proto);
}
cleanup:
tmp_frame_cleanup(&tf);
return fval;
}
V7_PRIVATE int is_js_function(val_t v) {
return (v & V7_TAG_MASK) == V7_TAG_FUNCTION;
}
V7_PRIVATE
v7_val_t mk_cfunction_obj(struct v7 *v7, v7_cfunction_t *f, int num_args) {
val_t obj = mk_object(v7, v7->vals.function_prototype);
struct gc_tmp_frame tf = new_tmp_frame(v7);
tmp_stack_push(&tf, &obj);
v7_def(v7, obj, "", 0, _V7_DESC_HIDDEN(1), v7_mk_cfunction(f));
if (num_args >= 0) {
v7_def(v7, obj, "length", 6, (V7_DESC_ENUMERABLE(0) | V7_DESC_WRITABLE(0) |
V7_DESC_CONFIGURABLE(0)),
v7_mk_number(v7, num_args));
}
tmp_frame_cleanup(&tf);
return obj;
}
V7_PRIVATE v7_val_t mk_cfunction_obj_with_proto(struct v7 *v7,
v7_cfunction_t *f, int num_args,
v7_val_t proto) {
struct gc_tmp_frame tf = new_tmp_frame(v7);
v7_val_t res = mk_cfunction_obj(v7, f, num_args);
tmp_stack_push(&tf, &res);
v7_def(v7, res, "prototype", 9, (V7_DESC_ENUMERABLE(0) | V7_DESC_WRITABLE(0) |
V7_DESC_CONFIGURABLE(0)),
proto);
v7_def(v7, proto, "constructor", 11, V7_DESC_ENUMERABLE(0), res);
tmp_frame_cleanup(&tf);
return res;
}
V7_PRIVATE v7_val_t mk_cfunction_lite(v7_cfunction_t *f) {
union {
void *p;
v7_cfunction_t *f;
} u;
u.f = f;
return pointer_to_value(u.p) | V7_TAG_CFUNCTION;
}
V7_PRIVATE v7_cfunction_t *get_cfunction_ptr(struct v7 *v7, val_t v) {
v7_cfunction_t *ret = NULL;
if (is_cfunction_lite(v)) {
/* Implementation is identical to get_ptr but is separate since
* object pointers are not directly convertible to function pointers
* according to ISO C and generates a warning in -Wpedantic mode. */
ret = (v7_cfunction_t *) (uintptr_t)(v & 0xFFFFFFFFFFFFUL);
} else {
/* maybe cfunction object */
/* extract the hidden property from a cfunction_object */
struct v7_property *p;
p = v7_get_own_property2(v7, v, "", 0, _V7_PROPERTY_HIDDEN);
if (p != NULL) {
/* yes, it's cfunction object. Extract cfunction pointer from it */
ret = get_cfunction_ptr(v7, p->value);
}
}
return ret;
}
V7_PRIVATE int is_cfunction_lite(val_t v) {
return (v & V7_TAG_MASK) == V7_TAG_CFUNCTION;
}
V7_PRIVATE int is_cfunction_obj(struct v7 *v7, val_t v) {
int ret = 0;
if (v7_is_object(v)) {
/* extract the hidden property from a cfunction_object */
struct v7_property *p;
p = v7_get_own_property2(v7, v, "", 0, _V7_PROPERTY_HIDDEN);
if (p != NULL) {
v = p->value;
}
ret = is_cfunction_lite(v);
}
return ret;
}
v7_val_t v7_mk_function(struct v7 *v7, v7_cfunction_t *f) {
return mk_cfunction_obj(v7, f, -1);
}
v7_val_t v7_mk_function_with_proto(struct v7 *v7, v7_cfunction_t *f,
v7_val_t proto) {
return mk_cfunction_obj_with_proto(v7, f, ~0, proto);
}
v7_val_t v7_mk_cfunction(v7_cfunction_t *f) {
return mk_cfunction_lite(f);
}
int v7_is_callable(struct v7 *v7, val_t v) {
return is_js_function(v) || is_cfunction_lite(v) || is_cfunction_obj(v7, v);
}
#ifdef V7_MODULE_LINES
#line 1 "v7/src/exec.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* osdep.h must be included before `cs_file.h` TODO(dfrank) : fix this */
/* Amalgamated: #include "common/cs_file.h" */
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* Amalgamated: #include "v7/src/eval.h" */
/* Amalgamated: #include "v7/src/exec.h" */
/* Amalgamated: #include "v7/src/ast.h" */
/* Amalgamated: #include "v7/src/compiler.h" */
/* Amalgamated: #include "v7/src/exceptions.h" */
enum v7_err v7_exec(struct v7 *v7, const char *js_code, v7_val_t *res) {
return b_exec(v7, js_code, strlen(js_code), NULL, V7_UNDEFINED, V7_UNDEFINED,
V7_UNDEFINED, 0, 0, 0, res);
}
enum v7_err v7_exec_opt(struct v7 *v7, const char *js_code,
const struct v7_exec_opts *opts, v7_val_t *res) {
return b_exec(v7, js_code, strlen(js_code), opts->filename, V7_UNDEFINED,
V7_UNDEFINED,
(opts->this_obj == 0 ? V7_UNDEFINED : opts->this_obj),
opts->is_json, 0, 0, res);
}
enum v7_err v7_exec_buf(struct v7 *v7, const char *js_code, size_t len,
v7_val_t *res) {
return b_exec(v7, js_code, len, NULL, V7_UNDEFINED, V7_UNDEFINED,
V7_UNDEFINED, 0, 0, 0, res);
}
enum v7_err v7_parse_json(struct v7 *v7, const char *str, v7_val_t *res) {
return b_exec(v7, str, strlen(str), NULL, V7_UNDEFINED, V7_UNDEFINED,
V7_UNDEFINED, 1, 0, 0, res);
}
#ifndef V7_NO_FS
static enum v7_err exec_file(struct v7 *v7, const char *path, val_t *res,
int is_json) {
enum v7_err rcode = V7_OK;
char *p;
size_t file_size;
char *(*rd)(const char *, size_t *);
rd = cs_read_file;
#ifdef V7_MMAP_EXEC
rd = cs_mmap_file;
#ifdef V7_MMAP_EXEC_ONLY
#define I_STRINGIFY(x) #x
#define I_STRINGIFY2(x) I_STRINGIFY(x)
/* use mmap only for .js files */
if (strlen(path) <= 3 || strcmp(path + strlen(path) - 3, ".js") != 0) {
rd = cs_read_file;
}
#endif
#endif
if ((p = rd(path, &file_size)) == NULL) {
rcode = v7_throwf(v7, SYNTAX_ERROR, "cannot open [%s]", path);
/*
* In order to maintain compat with existing API, we should save the
* current exception value into `*res`
*
* TODO(dfrank): probably change API: clients can use
*`v7_get_thrown_value()` now.
*/
if (res != NULL) *res = v7_get_thrown_value(v7, NULL);
goto clean;
} else {
#ifndef V7_MMAP_EXEC
int fr = 1;
#else
int fr = 0;
#endif
rcode = b_exec(v7, p, file_size, path, V7_UNDEFINED, V7_UNDEFINED,
V7_UNDEFINED, is_json, fr, 0, res);
if (rcode != V7_OK) {
goto clean;
}
}
clean:
return rcode;
}
enum v7_err v7_exec_file(struct v7 *v7, const char *path, val_t *res) {
return exec_file(v7, path, res, 0);
}
enum v7_err v7_parse_json_file(struct v7 *v7, const char *path, v7_val_t *res) {
return exec_file(v7, path, res, 1);
}
#endif /* V7_NO_FS */
enum v7_err v7_apply(struct v7 *v7, v7_val_t func, v7_val_t this_obj,
v7_val_t args, v7_val_t *res) {
return b_apply(v7, func, this_obj, args, 0, res);
}
#ifndef NO_LIBC
#if !defined(V7_NO_COMPILER)
enum v7_err _v7_compile(const char *src, size_t js_code_size, int binary,
int use_bcode, FILE *fp) {
struct ast ast;
struct v7 *v7 = v7_create();
ast_off_t pos = 0;
enum v7_err err;
v7->is_precompiling = 1;
ast_init(&ast, 0);
err = parse(v7, &ast, src, js_code_size, 0);
if (err == V7_OK) {
if (use_bcode) {
struct bcode bcode;
/*
* We don't set filename here, because the bcode will be just serialized
* and then freed. We don't currently serialize filename. If we ever do,
* we'll have to make `_v7_compile()` to also take a filename argument,
* and use it here.
*/
bcode_init(&bcode, 0, NULL, 0);
err = compile_script(v7, &ast, &bcode);
if (err != V7_OK) {
goto cleanup_bcode;
}
if (binary) {
bcode_serialize(v7, &bcode, fp);
} else {
#ifdef V7_BCODE_DUMP
dump_bcode(v7, fp, &bcode);
#else
fprintf(stderr, "build flag V7_BCODE_DUMP not enabled\n");
#endif
}
cleanup_bcode:
bcode_free(v7, &bcode);
} else {
if (binary) {
fwrite(BIN_AST_SIGNATURE, sizeof(BIN_AST_SIGNATURE), 1, fp);
fwrite(ast.mbuf.buf, ast.mbuf.len, 1, fp);
} else {
ast_dump_tree(fp, &ast, &pos, 0);
}
}
}
ast_free(&ast);
v7_destroy(v7);
return err;
}
enum v7_err v7_compile(const char *src, int binary, int use_bcode, FILE *fp) {
return _v7_compile(src, strlen(src), binary, use_bcode, fp);
}
#endif /* V7_NO_COMPILER */
#endif
#ifdef V7_MODULE_LINES
#line 1 "v7/src/util.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* Amalgamated: #include "v7/src/object.h" */
/* Amalgamated: #include "v7/src/util.h" */
/* Amalgamated: #include "v7/src/string.h" */
/* Amalgamated: #include "v7/src/array.h" */
/* Amalgamated: #include "v7/src/eval.h" */
/* Amalgamated: #include "v7/src/conversion.h" */
/* Amalgamated: #include "v7/src/exceptions.h" */
/* Amalgamated: #include "v7/src/primitive.h" */
/* Amalgamated: #include "v7/src/std_proxy.h" */
void v7_print(struct v7 *v7, v7_val_t v) {
v7_fprint(stdout, v7, v);
}
void v7_fprint(FILE *f, struct v7 *v7, val_t v) {
char buf[16];
char *s = v7_stringify(v7, v, buf, sizeof(buf), V7_STRINGIFY_DEBUG);
fprintf(f, "%s", s);
if (buf != s) free(s);
}
void v7_println(struct v7 *v7, v7_val_t v) {
v7_fprintln(stdout, v7, v);
}
void v7_fprintln(FILE *f, struct v7 *v7, val_t v) {
v7_fprint(f, v7, v);
fprintf(f, ENDL);
}
void v7_fprint_stack_trace(FILE *f, struct v7 *v7, val_t e) {
size_t s;
val_t strace_v = v7_get(v7, e, "stack", ~0);
const char *strace = NULL;
if (v7_is_string(strace_v)) {
strace = v7_get_string(v7, &strace_v, &s);
fprintf(f, "%s\n", strace);
}
}
void v7_print_error(FILE *f, struct v7 *v7, const char *ctx, val_t e) {
/* TODO(mkm): figure out if this is an error object and which kind */
v7_val_t msg;
if (v7_is_undefined(e)) {
fprintf(f, "undefined error [%s]\n ", ctx);
return;
}
msg = v7_get(v7, e, "message", ~0);
if (v7_is_undefined(msg)) {
msg = e;
}
fprintf(f, "Exec error [%s]: ", ctx);
v7_fprintln(f, v7, msg);
v7_fprint_stack_trace(f, v7, e);
}
#if V7_ENABLE__Proxy
v7_val_t v7_mk_proxy(struct v7 *v7, v7_val_t target,
const v7_proxy_hnd_t *handler) {
enum v7_err rcode = V7_OK;
v7_val_t res = V7_UNDEFINED;
v7_val_t args = V7_UNDEFINED;
v7_val_t handler_v = V7_UNDEFINED;
v7_own(v7, &res);
v7_own(v7, &args);
v7_own(v7, &handler_v);
v7_own(v7, &target);
/* if target is not an object, create one */
if (!v7_is_object(target)) {
target = v7_mk_object(v7);
}
/* prepare handler object with necessary properties */
handler_v = v7_mk_object(v7);
if (handler->get != NULL) {
set_cfunc_prop(v7, handler_v, "get", handler->get);
}
if (handler->set != NULL) {
set_cfunc_prop(v7, handler_v, "set", handler->set);
}
if (handler->own_keys != NULL) {
set_cfunc_prop(v7, handler_v, "ownKeys", handler->own_keys);
}
if (handler->get_own_prop_desc != NULL) {
v7_def(v7, handler_v, "_gpdc", ~0, V7_DESC_ENUMERABLE(0),
v7_mk_foreign(v7, (void *) handler->get_own_prop_desc));
}
/* prepare args */
args = v7_mk_dense_array(v7);
v7_array_set(v7, args, 0, target);
v7_array_set(v7, args, 1, handler_v);
/* call Proxy constructor */
V7_TRY(b_apply(v7, v7_get(v7, v7->vals.global_object, "Proxy", ~0),
v7_mk_object(v7), args, 1 /* as ctor */, &res));
clean:
if (rcode != V7_OK) {
fprintf(stderr, "error during v7_mk_proxy()");
res = V7_UNDEFINED;
}
v7_disown(v7, &target);
v7_disown(v7, &handler_v);
v7_disown(v7, &args);
v7_disown(v7, &res);
return res;
}
#endif /* V7_ENABLE__Proxy */
V7_PRIVATE enum v7_type val_type(struct v7 *v7, val_t v) {
int tag;
if (v7_is_number(v)) {
return V7_TYPE_NUMBER;
}
tag = (v & V7_TAG_MASK) >> 48;
switch (tag) {
case V7_TAG_FOREIGN >> 48:
if (v7_is_null(v)) {
return V7_TYPE_NULL;
}
return V7_TYPE_FOREIGN;
case V7_TAG_UNDEFINED >> 48:
return V7_TYPE_UNDEFINED;
case V7_TAG_OBJECT >> 48:
if (v7_get_proto(v7, v) == v7->vals.array_prototype) {
return V7_TYPE_ARRAY_OBJECT;
} else if (v7_get_proto(v7, v) == v7->vals.boolean_prototype) {
return V7_TYPE_BOOLEAN_OBJECT;
} else if (v7_get_proto(v7, v) == v7->vals.string_prototype) {
return V7_TYPE_STRING_OBJECT;
} else if (v7_get_proto(v7, v) == v7->vals.number_prototype) {
return V7_TYPE_NUMBER_OBJECT;
} else if (v7_get_proto(v7, v) == v7->vals.function_prototype) {
return V7_TYPE_CFUNCTION_OBJECT;
} else if (v7_get_proto(v7, v) == v7->vals.date_prototype) {
return V7_TYPE_DATE_OBJECT;
} else {
return V7_TYPE_GENERIC_OBJECT;
}
case V7_TAG_STRING_I >> 48:
case V7_TAG_STRING_O >> 48:
case V7_TAG_STRING_F >> 48:
case V7_TAG_STRING_D >> 48:
case V7_TAG_STRING_5 >> 48:
return V7_TYPE_STRING;
case V7_TAG_BOOLEAN >> 48:
return V7_TYPE_BOOLEAN;
case V7_TAG_FUNCTION >> 48:
return V7_TYPE_FUNCTION_OBJECT;
case V7_TAG_CFUNCTION >> 48:
return V7_TYPE_CFUNCTION;
case V7_TAG_REGEXP >> 48:
return V7_TYPE_REGEXP_OBJECT;
default:
abort();
return V7_TYPE_UNDEFINED;
}
}
#if !V7_DISABLE_LINE_NUMBERS
V7_PRIVATE uint8_t msb_lsb_swap(uint8_t b) {
if ((b & 0x01) != (b >> 7)) {
b ^= 0x81;
}
return b;
}
#endif
#ifdef V7_MODULE_LINES
#line 1 "v7/src/string.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "common/utf.h" */
/* Amalgamated: #include "v7/src/string.h" */
/* Amalgamated: #include "v7/src/exceptions.h" */
/* Amalgamated: #include "v7/src/conversion.h" */
/* Amalgamated: #include "v7/src/varint.h" */
/* Amalgamated: #include "v7/src/gc.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* Amalgamated: #include "v7/src/primitive.h" */
/* Amalgamated: #include "v7/src/slre.h" */
/* Amalgamated: #include "v7/src/heapusage.h" */
/* TODO(lsm): NaN payload location depends on endianness, make crossplatform */
#define GET_VAL_NAN_PAYLOAD(v) ((char *) &(v))
/*
* Dictionary of read-only strings with length > 5.
* NOTE(lsm): must be sorted lexicographically, because
* v_find_string_in_dictionary performs binary search over this list.
*/
/* clang-format off */
static const struct v7_vec_const v_dictionary_strings[] = {
V7_VEC(" is not a function"),
V7_VEC("Boolean"),
V7_VEC("Crypto"),
V7_VEC("EvalError"),
V7_VEC("Function"),
V7_VEC("Infinity"),
V7_VEC("InternalError"),
V7_VEC("LOG10E"),
V7_VEC("MAX_VALUE"),
V7_VEC("MIN_VALUE"),
V7_VEC("NEGATIVE_INFINITY"),
V7_VEC("Number"),
V7_VEC("Object"),
V7_VEC("POSITIVE_INFINITY"),
V7_VEC("RangeError"),
V7_VEC("ReferenceError"),
V7_VEC("RegExp"),
V7_VEC("SQRT1_2"),
V7_VEC("Socket"),
V7_VEC("String"),
V7_VEC("SyntaxError"),
V7_VEC("TypeError"),
V7_VEC("UBJSON"),
V7_VEC("_modcache"),
V7_VEC("accept"),
V7_VEC("arguments"),
V7_VEC("base64_decode"),
V7_VEC("base64_encode"),
V7_VEC("boolean"),
V7_VEC("charAt"),
V7_VEC("charCodeAt"),
V7_VEC("concat"),
V7_VEC("configurable"),
V7_VEC("connect"),
V7_VEC("constructor"),
V7_VEC("create"),
V7_VEC("defineProperties"),
V7_VEC("defineProperty"),
V7_VEC("every"),
V7_VEC("exists"),
V7_VEC("exports"),
V7_VEC("filter"),
V7_VEC("forEach"),
V7_VEC("fromCharCode"),
V7_VEC("function"),
V7_VEC("getDate"),
V7_VEC("getDay"),
V7_VEC("getFullYear"),
V7_VEC("getHours"),
V7_VEC("getMilliseconds"),
V7_VEC("getMinutes"),
V7_VEC("getMonth"),
V7_VEC("getOwnPropertyDescriptor"),
V7_VEC("getOwnPropertyNames"),
V7_VEC("getPrototypeOf"),
V7_VEC("getSeconds"),
V7_VEC("getTime"),
V7_VEC("getTimezoneOffset"),
V7_VEC("getUTCDate"),
V7_VEC("getUTCDay"),
V7_VEC("getUTCFullYear"),
V7_VEC("getUTCHours"),
V7_VEC("getUTCMilliseconds"),
V7_VEC("getUTCMinutes"),
V7_VEC("getUTCMonth"),
V7_VEC("getUTCSeconds"),
V7_VEC("global"),
V7_VEC("hasOwnProperty"),
V7_VEC("ignoreCase"),
V7_VEC("indexOf"),
V7_VEC("isArray"),
V7_VEC("isExtensible"),
V7_VEC("isFinite"),
V7_VEC("isPrototypeOf"),
V7_VEC("lastIndex"),
V7_VEC("lastIndexOf"),
V7_VEC("length"),
V7_VEC("listen"),
V7_VEC("loadJSON"),
V7_VEC("localeCompare"),
V7_VEC("md5_hex"),
V7_VEC("module"),
V7_VEC("multiline"),
V7_VEC("number"),
V7_VEC("parseFloat"),
V7_VEC("parseInt"),
V7_VEC("preventExtensions"),
V7_VEC("propertyIsEnumerable"),
V7_VEC("prototype"),
V7_VEC("random"),
V7_VEC("recvAll"),
V7_VEC("reduce"),
V7_VEC("remove"),
V7_VEC("rename"),
V7_VEC("render"),
V7_VEC("replace"),
V7_VEC("require"),
V7_VEC("reverse"),
V7_VEC("search"),
V7_VEC("setDate"),
V7_VEC("setFullYear"),
V7_VEC("setHours"),
V7_VEC("setMilliseconds"),
V7_VEC("setMinutes"),
V7_VEC("setMonth"),
V7_VEC("setSeconds"),
V7_VEC("setTime"),
V7_VEC("setUTCDate"),
V7_VEC("setUTCFullYear"),
V7_VEC("setUTCHours"),
V7_VEC("setUTCMilliseconds"),
V7_VEC("setUTCMinutes"),
V7_VEC("setUTCMonth"),
V7_VEC("setUTCSeconds"),
V7_VEC("sha1_hex"),
V7_VEC("source"),
V7_VEC("splice"),
V7_VEC("string"),
V7_VEC("stringify"),
V7_VEC("substr"),
V7_VEC("substring"),
V7_VEC("toDateString"),
V7_VEC("toExponential"),
V7_VEC("toFixed"),
V7_VEC("toISOString"),
V7_VEC("toJSON"),
V7_VEC("toLocaleDateString"),
V7_VEC("toLocaleLowerCase"),
V7_VEC("toLocaleString"),
V7_VEC("toLocaleTimeString"),
V7_VEC("toLocaleUpperCase"),
V7_VEC("toLowerCase"),
V7_VEC("toPrecision"),
V7_VEC("toString"),
V7_VEC("toTimeString"),
V7_VEC("toUTCString"),
V7_VEC("toUpperCase"),
V7_VEC("valueOf"),
V7_VEC("writable"),
};
/* clang-format on */
int nextesc(const char **p); /* from SLRE */
V7_PRIVATE size_t unescape(const char *s, size_t len, char *to) {
const char *end = s + len;
size_t n = 0;
char tmp[4];
Rune r;
while (s < end) {
s += chartorune(&r, s);
if (r == '\\' && s < end) {
switch (*s) {
case '"':
s++, r = '"';
break;
case '\'':
s++, r = '\'';
break;
case '\n':
s++, r = '\n';
break;
default: {
const char *tmp_s = s;
int i = nextesc(&s);
switch (i) {
case -SLRE_INVALID_ESC_CHAR:
r = '\\';
s = tmp_s;
n += runetochar(to == NULL ? tmp : to + n, &r);
s += chartorune(&r, s);
break;
case -SLRE_INVALID_HEX_DIGIT:
default:
r = i;
}
}
}
}
n += runetochar(to == NULL ? tmp : to + n, &r);
}
return n;
}
static int v_find_string_in_dictionary(const char *s, size_t len) {
size_t start = 0, end = ARRAY_SIZE(v_dictionary_strings);
while (s != NULL && start < end) {
size_t mid = start + (end - start) / 2;
const struct v7_vec_const *v = &v_dictionary_strings[mid];
size_t min_len = len < v->len ? len : v->len;
int comparison_result = memcmp(s, v->p, min_len);
if (comparison_result == 0) {
comparison_result = len - v->len;
}
if (comparison_result < 0) {
end = mid;
} else if (comparison_result > 0) {
start = mid + 1;
} else {
return mid;
}
}
return -1;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err v7_char_code_at(struct v7 *v7, val_t obj, val_t arg,
double *res) {
enum v7_err rcode = V7_OK;
size_t n;
val_t s = V7_UNDEFINED;
const char *p = NULL;
double at = v7_get_double(v7, arg);
*res = 0;
rcode = to_string(v7, obj, &s, NULL, 0, NULL);
if (rcode != V7_OK) {
goto clean;
}
p = v7_get_string(v7, &s, &n);
n = utfnlen(p, n);
if (v7_is_number(arg) && at >= 0 && at < n) {
Rune r = 0;
p = utfnshift(p, at);
chartorune(&r, (char *) p);
*res = r;
goto clean;
} else {
*res = NAN;
goto clean;
}
clean:
return rcode;
}
V7_PRIVATE int s_cmp(struct v7 *v7, val_t a, val_t b) {
size_t a_len, b_len;
const char *a_ptr, *b_ptr;
a_ptr = v7_get_string(v7, &a, &a_len);
b_ptr = v7_get_string(v7, &b, &b_len);
if (a_len == b_len) {
return memcmp(a_ptr, b_ptr, a_len);
}
if (a_len > b_len) {
return 1;
} else if (a_len < b_len) {
return -1;
} else {
return 0;
}
}
V7_PRIVATE val_t s_concat(struct v7 *v7, val_t a, val_t b) {
size_t a_len, b_len, res_len;
const char *a_ptr, *b_ptr, *res_ptr;
val_t res;
/* Find out lengths of both srtings */
a_ptr = v7_get_string(v7, &a, &a_len);
b_ptr = v7_get_string(v7, &b, &b_len);
/* Create an placeholder string */
res = v7_mk_string(v7, NULL, a_len + b_len, 1);
/* v7_mk_string() may have reallocated mbuf - revalidate pointers */
a_ptr = v7_get_string(v7, &a, &a_len);
b_ptr = v7_get_string(v7, &b, &b_len);
/* Copy strings into the placeholder */
res_ptr = v7_get_string(v7, &res, &res_len);
memcpy((char *) res_ptr, a_ptr, a_len);
memcpy((char *) res_ptr + a_len, b_ptr, b_len);
return res;
}
V7_PRIVATE unsigned long cstr_to_ulong(const char *s, size_t len, int *ok) {
char *e;
unsigned long res = strtoul(s, &e, 10);
*ok = (e == s + len) && len != 0;
return res;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err str_to_ulong(struct v7 *v7, val_t v, int *ok,
unsigned long *res) {
enum v7_err rcode = V7_OK;
char buf[100];
size_t len = 0;
V7_TRY(to_string(v7, v, NULL, buf, sizeof(buf), &len));
*res = cstr_to_ulong(buf, len, ok);
clean:
return rcode;
}
/* Insert a string into mbuf at specified offset */
V7_PRIVATE void embed_string(struct mbuf *m, size_t offset, const char *p,
size_t len, uint8_t /*enum embstr_flags*/ flags) {
char *old_base = m->buf;
uint8_t p_backed_by_mbuf = p >= old_base && p < old_base + m->len;
size_t n = (flags & EMBSTR_UNESCAPE) ? unescape(p, len, NULL) : len;
/* Calculate how many bytes length takes */
int k = calc_llen(n);
/* total length: varing length + string len + zero-term */
size_t tot_len = k + n + !!(flags & EMBSTR_ZERO_TERM);
/* Allocate buffer */
heapusage_dont_count(1);
mbuf_insert(m, offset, NULL, tot_len);
heapusage_dont_count(0);
/* Fixup p if it was relocated by mbuf_insert() above */
if (p_backed_by_mbuf) {
p += m->buf - old_base;
}
/* Write length */
encode_varint(n, (unsigned char *) m->buf + offset);
/* Write string */
if (p != 0) {
if (flags & EMBSTR_UNESCAPE) {
unescape(p, len, m->buf + offset + k);
} else {
memcpy(m->buf + offset + k, p, len);
}
}
/* add NULL-terminator if needed */
if (flags & EMBSTR_ZERO_TERM) {
m->buf[offset + tot_len - 1] = '\0';
}
}
/* Create a string */
v7_val_t v7_mk_string(struct v7 *v7, const char *p, size_t len, int copy) {
struct mbuf *m = copy ? &v7->owned_strings : &v7->foreign_strings;
val_t offset = m->len, tag = V7_TAG_STRING_F;
int dict_index;
#ifdef V7_GC_AFTER_STRING_ALLOC
v7->need_gc = 1;
#endif
if (len == ~((size_t) 0)) len = strlen(p);
if (len <= 4) {
char *s = GET_VAL_NAN_PAYLOAD(offset) + 1;
offset = 0;
if (p != 0) {
memcpy(s, p, len);
}
s[-1] = len;
tag = V7_TAG_STRING_I;
} else if (len == 5) {
char *s = GET_VAL_NAN_PAYLOAD(offset);
offset = 0;
if (p != 0) {
memcpy(s, p, len);
}
tag = V7_TAG_STRING_5;
} else if ((dict_index = v_find_string_in_dictionary(p, len)) >= 0) {
offset = 0;
GET_VAL_NAN_PAYLOAD(offset)[0] = dict_index;
tag = V7_TAG_STRING_D;
} else if (copy) {
compute_need_gc(v7);
/*
* Before embedding new string, check if the reallocation is needed. If
* so, perform the reallocation by calling `mbuf_resize` manually, since we
* need to preallocate some extra space (`_V7_STRING_BUF_RESERVE`)
*/
if ((m->len + len) > m->size) {
heapusage_dont_count(1);
mbuf_resize(m, m->len + len + _V7_STRING_BUF_RESERVE);
heapusage_dont_count(0);
}
embed_string(m, m->len, p, len, EMBSTR_ZERO_TERM);
tag = V7_TAG_STRING_O;
#if !V7_DISABLE_STR_ALLOC_SEQ
/* TODO(imax): panic if offset >= 2^32. */
offset |= ((val_t) gc_next_allocation_seqn(v7, p, len)) << 32;
#endif
} else {
/* foreign string */
if (sizeof(void *) <= 4 && len <= UINT16_MAX) {
/* small foreign strings can fit length and ptr in the val_t */
offset = (uint64_t) len << 32 | (uint64_t)(uintptr_t) p;
} else {
/* bigger strings need indirection that uses ram */
size_t pos = m->len;
int llen = calc_llen(len);
/* allocate space for len and ptr */
heapusage_dont_count(1);
mbuf_insert(m, pos, NULL, llen + sizeof(p));
heapusage_dont_count(0);
encode_varint(len, (uint8_t *) (m->buf + pos));
memcpy(m->buf + pos + llen, &p, sizeof(p));
}
tag = V7_TAG_STRING_F;
}
/* NOTE(lsm): don't use pointer_to_value, 32-bit ptrs will truncate */
return (offset & ~V7_TAG_MASK) | tag;
}
int v7_is_string(val_t v) {
uint64_t t = v & V7_TAG_MASK;
return t == V7_TAG_STRING_I || t == V7_TAG_STRING_F || t == V7_TAG_STRING_O ||
t == V7_TAG_STRING_5 || t == V7_TAG_STRING_D;
}
/* Get a pointer to string and string length. */
const char *v7_get_string(struct v7 *v7, val_t *v, size_t *sizep) {
uint64_t tag = v[0] & V7_TAG_MASK;
const char *p = NULL;
int llen;
size_t size = 0;
if (!v7_is_string(*v)) {
goto clean;
}
if (tag == V7_TAG_STRING_I) {
p = GET_VAL_NAN_PAYLOAD(*v) + 1;
size = p[-1];
} else if (tag == V7_TAG_STRING_5) {
p = GET_VAL_NAN_PAYLOAD(*v);
size = 5;
} else if (tag == V7_TAG_STRING_D) {
int index = ((unsigned char *) GET_VAL_NAN_PAYLOAD(*v))[0];
size = v_dictionary_strings[index].len;
p = v_dictionary_strings[index].p;
} else if (tag == V7_TAG_STRING_O) {
size_t offset = (size_t) gc_string_val_to_offset(*v);
char *s = v7->owned_strings.buf + offset;
#if !V7_DISABLE_STR_ALLOC_SEQ
gc_check_valid_allocation_seqn(v7, (*v >> 32) & 0xFFFF);
#endif
size = decode_varint((uint8_t *) s, &llen);
p = s + llen;
} else if (tag == V7_TAG_STRING_F) {
/*
* short foreign strings on <=32-bit machines can be encoded in a compact
* form:
*
* 7 6 5 4 3 2 1 0
* 11111111|1111tttt|llllllll|llllllll|ssssssss|ssssssss|ssssssss|ssssssss
*
* Strings longer than 2^26 will be indireceted through the foreign_strings
* mbuf.
*
* We don't use a different tag to represent those two cases. Instead, all
* foreign strings represented with the help of the foreign_strings mbuf
* will have the upper 16-bits of the payload set to zero. This allows us to
* represent up to 477 million foreign strings longer than 64k.
*/
uint16_t len = (*v >> 32) & 0xFFFF;
if (sizeof(void *) <= 4 && len != 0) {
size = (size_t) len;
p = (const char *) (uintptr_t) *v;
} else {
size_t offset = (size_t) gc_string_val_to_offset(*v);
char *s = v7->foreign_strings.buf + offset;
size = decode_varint((uint8_t *) s, &llen);
memcpy(&p, s + llen, sizeof(p));
}
} else {
assert(0);
}
clean:
if (sizep != NULL) {
*sizep = size;
}
return p;
}
const char *v7_get_cstring(struct v7 *v7, v7_val_t *value) {
size_t size;
const char *s = v7_get_string(v7, value, &size);
if (s == NULL) return NULL;
if (s[size] != 0 || strlen(s) != size) {
return NULL;
}
return s;
}
#ifdef V7_MODULE_LINES
#line 1 "v7/src/array.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "common/str_util.h" */
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/array.h" */
/* Amalgamated: #include "v7/src/string.h" */
/* Amalgamated: #include "v7/src/object.h" */
/* Amalgamated: #include "v7/src/exceptions.h" */
/* Amalgamated: #include "v7/src/primitive.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* like c_snprintf but returns `size` if write is truncated */
static int v_sprintf_s(char *buf, size_t size, const char *fmt, ...) {
size_t n;
va_list ap;
va_start(ap, fmt);
n = c_vsnprintf(buf, size, fmt, ap);
if (n > size) {
return size;
}
return n;
}
v7_val_t v7_mk_array(struct v7 *v7) {
val_t a = mk_object(v7, v7->vals.array_prototype);
#if 0
v7_def(v7, a, "", 0, _V7_DESC_HIDDEN(1), V7_NULL);
#endif
return a;
}
int v7_is_array(struct v7 *v7, val_t v) {
return v7_is_generic_object(v) &&
is_prototype_of(v7, v, v7->vals.array_prototype);
}
/*
* Dense arrays are backed by mbuf. Currently the array can only grow by
* appending (i.e. setting an element whose index == array.length)
*
* TODO(mkm): automatically promote dense arrays to normal objects
* when they are used as sparse arrays or to store arbitrary keys
* (perhaps a hybrid approach)
* TODO(mkm): small sparsness doesn't have to promote the array,
* we can just fill empty slots with a tag. In JS missing array
* indices are subtly different from indices with an undefined value
* (key iteration).
* TODO(mkm): change the interpreter so it can set elements in dense arrays
*/
V7_PRIVATE val_t v7_mk_dense_array(struct v7 *v7) {
val_t a = v7_mk_array(v7);
#if V7_ENABLE_DENSE_ARRAYS
v7_own(v7, &a);
v7_def(v7, a, "", 0, _V7_DESC_HIDDEN(1), V7_NULL);
/*
* Before setting a `V7_OBJ_DENSE_ARRAY` flag, make sure we don't have
* `V7_OBJ_FUNCTION` flag set
*/
assert(!(get_object_struct(a)->attributes & V7_OBJ_FUNCTION));
get_object_struct(a)->attributes |= V7_OBJ_DENSE_ARRAY;
v7_disown(v7, &a);
#endif
return a;
}
/* TODO_V7_ERR */
val_t v7_array_get(struct v7 *v7, val_t arr, unsigned long index) {
return v7_array_get2(v7, arr, index, NULL);
}
/* TODO_V7_ERR */
val_t v7_array_get2(struct v7 *v7, val_t arr, unsigned long index, int *has) {
enum v7_err rcode = V7_OK;
val_t res;
if (has != NULL) {
*has = 0;
}
if (v7_is_object(arr)) {
if (get_object_struct(arr)->attributes & V7_OBJ_DENSE_ARRAY) {
struct v7_property *p =
v7_get_own_property2(v7, arr, "", 0, _V7_PROPERTY_HIDDEN);
struct mbuf *abuf = NULL;
unsigned long len;
if (p != NULL) {
abuf = (struct mbuf *) v7_get_ptr(v7, p->value);
}
if (abuf == NULL) {
res = V7_UNDEFINED;
goto clean;
}
len = abuf->len / sizeof(val_t);
if (index >= len) {
res = V7_UNDEFINED;
goto clean;
} else {
memcpy(&res, abuf->buf + index * sizeof(val_t), sizeof(val_t));
if (has != NULL && res != V7_TAG_NOVALUE) *has = 1;
if (res == V7_TAG_NOVALUE) {
res = V7_UNDEFINED;
}
goto clean;
}
} else {
struct v7_property *p;
char buf[20];
int n = v_sprintf_s(buf, sizeof(buf), "%lu", index);
p = v7_get_property(v7, arr, buf, n);
if (has != NULL && p != NULL) *has = 1;
V7_TRY(v7_property_value(v7, arr, p, &res));
goto clean;
}
} else {
res = V7_UNDEFINED;
goto clean;
}
clean:
(void) rcode;
return res;
}
#if V7_ENABLE_DENSE_ARRAYS
/* Create V7 strings for integers such as array indices */
static val_t ulong_to_str(struct v7 *v7, unsigned long n) {
char buf[100];
int len;
len = c_snprintf(buf, sizeof(buf), "%lu", n);
return v7_mk_string(v7, buf, len, 1);
}
/*
* Pack 15-bit length and 15 bit index, leaving 2 bits for tag. the LSB has to
* be set to distinguish it from a prop pointer.
* In alternative we just fetch the length from obj at each call to v7_next_prop
* and just stuff the index here (e.g. on 8/16-bit platforms).
* TODO(mkm): conditional for 16-bit platforms
*/
#define PACK_ITER(len, idx) \
((struct v7_property *) ((len) << 17 | (idx) << 1 | 1))
#define UNPACK_ITER_LEN(p) (((uintptr_t) p) >> 17)
#define UNPACK_ITER_IDX(p) ((((uintptr_t) p) >> 1) & 0x7FFF)
#define IS_PACKED_ITER(p) ((uintptr_t) p & 1)
void *v7_next_prop(struct v7 *v7, val_t obj, void *h, val_t *name, val_t *val,
v7_prop_attr_t *attrs) {
struct v7_property *p = (struct v7_property *) h;
if (get_object_struct(obj)->attributes & V7_OBJ_DENSE_ARRAY) {
/* This is a dense array. Find backing mbuf and fetch values from there */
struct v7_property *hp =
v7_get_own_property2(v7, obj, "", 0, _V7_PROPERTY_HIDDEN);
struct mbuf *abuf = NULL;
unsigned long len, idx;
if (hp != NULL) {
abuf = (struct mbuf *) v7_get_ptr(v7, hp->value);
}
if (abuf == NULL) return NULL;
len = abuf->len / sizeof(val_t);
if (len == 0) return NULL;
idx = (p == NULL) ? 0 : UNPACK_ITER_IDX(p) + 1;
p = (idx == len) ? get_object_struct(obj)->properties : PACK_ITER(len, idx);
if (val != NULL) *val = ((val_t *) abuf->buf)[idx];
if (attrs != NULL) *attrs = 0;
if (name != NULL) {
char buf[20];
int n = v_sprintf_s(buf, sizeof(buf), "%lu", index);
*name = v7_mk_string(v7, buf, n, 1);
}
} else {
/* Ordinary object */
p = (p == NULL) ? get_object_struct(obj)->properties : p->next;
if (p != NULL) {
if (name != NULL) *name = p->name;
if (val != NULL) *val = p->value;
if (attrs != NULL) *attrs = p->attributes;
}
}
return p;
}
V7_PRIVATE val_t
v7_iter_get_value(struct v7 *v7, val_t obj, struct v7_property *p) {
return IS_PACKED_ITER(p) ? v7_array_get(v7, obj, UNPACK_ITER_IDX(p))
: p->value;
}
V7_PRIVATE val_t v7_iter_get_name(struct v7 *v7, struct v7_property *p) {
return IS_PACKED_ITER(p) ? ulong_to_str(v7, UNPACK_ITER_IDX(p)) : p->name;
}
V7_PRIVATE uint8_t v7_iter_get_attrs(struct v7_property *p) {
return IS_PACKED_ITER(p) ? 0 : p->attributes;
}
/* return array index as number or undefined. works with iterators */
V7_PRIVATE enum v7_err v7_iter_get_index(struct v7 *v7, struct v7_property *p,
val_t *res) {
enum v7_err rcode = V7_OK;
int ok;
unsigned long res;
if (IS_PACKED_ITER(p)) {
*res = v7_mk_number(v7, UNPACK_ITER_IDX(p));
goto clean;
}
V7_TRY(str_to_ulong(v7, p->name, &ok, &res));
if (!ok || res >= UINT32_MAX) {
goto clean;
}
*res = v7_mk_number(v7, res);
goto clean;
clean:
return rcode;
}
#endif
/* TODO_V7_ERR */
unsigned long v7_array_length(struct v7 *v7, val_t v) {
enum v7_err rcode = V7_OK;
struct v7_property *p;
unsigned long len = 0;
if (!v7_is_object(v)) {
len = 0;
goto clean;
}
#if V7_ENABLE_DENSE_ARRAYS
if (get_object_struct(v)->attributes & V7_OBJ_DENSE_ARRAY) {
struct v7_property *p =
v7_get_own_property2(v7, v, "", 0, _V7_PROPERTY_HIDDEN);
struct mbuf *abuf;
if (p == NULL) {
len = 0;
goto clean;
}
abuf = (struct mbuf *) v7_get_ptr(v7, p->value);
if (abuf == NULL) {
len = 0;
goto clean;
}
len = abuf->len / sizeof(val_t);
goto clean;
}
#endif
for (p = get_object_struct(v)->properties; p != NULL; p = p->next) {
int ok = 0;
unsigned long n = 0;
V7_TRY(str_to_ulong(v7, p->name, &ok, &n));
if (ok && n >= len && n < UINT32_MAX) {
len = n + 1;
}
}
clean:
(void) rcode;
return len;
}
int v7_array_set(struct v7 *v7, val_t arr, unsigned long index, val_t v) {
enum v7_err rcode = V7_OK;
uint8_t saved_is_thrown = 0;
val_t saved_thrown = v7_get_thrown_value(v7, &saved_is_thrown);
int ret = -1;
rcode = v7_array_set_throwing(v7, arr, index, v, &ret);
if (rcode != V7_OK) {
rcode = V7_OK;
if (saved_is_thrown) {
rcode = v7_throw(v7, saved_thrown);
} else {
v7_clear_thrown_value(v7);
}
ret = -1;
}
return ret;
}
enum v7_err v7_array_set_throwing(struct v7 *v7, val_t arr, unsigned long index,
val_t v, int *res) {
enum v7_err rcode = V7_OK;
int ires = -1;
if (v7_is_object(arr)) {
if (get_object_struct(arr)->attributes & V7_OBJ_DENSE_ARRAY) {
struct v7_property *p =
v7_get_own_property2(v7, arr, "", 0, _V7_PROPERTY_HIDDEN);
struct mbuf *abuf;
unsigned long len;
assert(p != NULL);
abuf = (struct mbuf *) v7_get_ptr(v7, p->value);
if (get_object_struct(arr)->attributes & V7_OBJ_NOT_EXTENSIBLE) {
if (is_strict_mode(v7)) {
rcode = v7_throwf(v7, TYPE_ERROR, "Object is not extensible");
goto clean;
}
goto clean;
}
if (abuf == NULL) {
abuf = (struct mbuf *) malloc(sizeof(*abuf));
mbuf_init(abuf, sizeof(val_t) * (index + 1));
p->value = v7_mk_foreign(v7, abuf);
}
len = abuf->len / sizeof(val_t);
/* TODO(mkm): possibly promote to sparse array */
if (index > len) {
unsigned long i;
val_t s = V7_TAG_NOVALUE;
for (i = len; i < index; i++) {
mbuf_append(abuf, (char *) &s, sizeof(val_t));
}
len = index;
}
if (index == len) {
mbuf_append(abuf, (char *) &v, sizeof(val_t));
} else {
memcpy(abuf->buf + index * sizeof(val_t), &v, sizeof(val_t));
}
} else {
char buf[20];
int n = v_sprintf_s(buf, sizeof(buf), "%lu", index);
{
struct v7_property *tmp = NULL;
rcode = set_property(v7, arr, buf, n, v, &tmp);
ires = (tmp == NULL) ? -1 : 0;
}
if (rcode != V7_OK) {
goto clean;
}
}
}
clean:
if (res != NULL) {
*res = ires;
}
return rcode;
}
void v7_array_del(struct v7 *v7, val_t arr, unsigned long index) {
char buf[20];
int n = v_sprintf_s(buf, sizeof(buf), "%lu", index);
v7_del(v7, arr, buf, n);
}
int v7_array_push(struct v7 *v7, v7_val_t arr, v7_val_t v) {
return v7_array_set(v7, arr, v7_array_length(v7, arr), v);
}
WARN_UNUSED_RESULT
enum v7_err v7_array_push_throwing(struct v7 *v7, v7_val_t arr, v7_val_t v,
int *res) {
return v7_array_set_throwing(v7, arr, v7_array_length(v7, arr), v, res);
}
#ifdef V7_MODULE_LINES
#line 1 "v7/src/object.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* Amalgamated: #include "v7/src/primitive.h" */
/* Amalgamated: #include "v7/src/function.h" */
/* Amalgamated: #include "v7/src/gc.h" */
/* Amalgamated: #include "v7/src/object.h" */
/* Amalgamated: #include "v7/src/string.h" */
/* Amalgamated: #include "v7/src/array.h" */
/* Amalgamated: #include "v7/src/eval.h" */
/* Amalgamated: #include "v7/src/exceptions.h" */
/* Amalgamated: #include "v7/src/conversion.h" */
/* Amalgamated: #include "v7/src/std_proxy.h" */
/* Amalgamated: #include "v7/src/util.h" */
/*
* Default property attributes (see `v7_prop_attr_t`)
*/
#define V7_DEFAULT_PROPERTY_ATTRS 0
V7_PRIVATE val_t mk_object(struct v7 *v7, val_t prototype) {
struct v7_generic_object *o = new_generic_object(v7);
if (o == NULL) {
return V7_NULL;
}
(void) v7;
#if V7_ENABLE_ENTITY_IDS
o->base.entity_id_base = V7_ENTITY_ID_PART_OBJ;
o->base.entity_id_spec = V7_ENTITY_ID_PART_GEN_OBJ;
#endif
o->base.properties = NULL;
obj_prototype_set(v7, &o->base, get_object_struct(prototype));
return v7_object_to_value(&o->base);
}
v7_val_t v7_mk_object(struct v7 *v7) {
return mk_object(v7, v7->vals.object_prototype);
}
V7_PRIVATE val_t v7_object_to_value(struct v7_object *o) {
if (o == NULL) {
return V7_NULL;
} else if (o->attributes & V7_OBJ_FUNCTION) {
return pointer_to_value(o) | V7_TAG_FUNCTION;
} else {
return pointer_to_value(o) | V7_TAG_OBJECT;
}
}
V7_PRIVATE struct v7_generic_object *get_generic_object_struct(val_t v) {
struct v7_generic_object *ret = NULL;
if (v7_is_null(v)) {
ret = NULL;
} else {
assert(v7_is_generic_object(v));
ret = (struct v7_generic_object *) get_ptr(v);
#if V7_ENABLE_ENTITY_IDS
if (ret->base.entity_id_base != V7_ENTITY_ID_PART_OBJ) {
fprintf(stderr, "not a generic object!\n");
abort();
} else if (ret->base.entity_id_spec != V7_ENTITY_ID_PART_GEN_OBJ) {
fprintf(stderr, "not an object (but is a generic object)!\n");
abort();
}
#endif
}
return ret;
}
V7_PRIVATE struct v7_object *get_object_struct(val_t v) {
struct v7_object *ret = NULL;
if (v7_is_null(v)) {
ret = NULL;
} else {
assert(v7_is_object(v));
ret = (struct v7_object *) get_ptr(v);
#if V7_ENABLE_ENTITY_IDS
if (ret->entity_id_base != V7_ENTITY_ID_PART_OBJ) {
fprintf(stderr, "not an object!\n");
abort();
}
#endif
}
return ret;
}
int v7_is_object(val_t v) {
return (v & V7_TAG_MASK) == V7_TAG_OBJECT ||
(v & V7_TAG_MASK) == V7_TAG_FUNCTION;
}
V7_PRIVATE int v7_is_generic_object(val_t v) {
return (v & V7_TAG_MASK) == V7_TAG_OBJECT;
}
/* Object properties {{{ */
V7_PRIVATE struct v7_property *v7_mk_property(struct v7 *v7) {
struct v7_property *p = new_property(v7);
#if V7_ENABLE_ENTITY_IDS
p->entity_id = V7_ENTITY_ID_PROP;
#endif
p->next = NULL;
p->name = V7_UNDEFINED;
p->value = V7_UNDEFINED;
p->attributes = 0;
return p;
}
V7_PRIVATE struct v7_property *v7_get_own_property2(struct v7 *v7, val_t obj,
const char *name,
size_t len,
v7_prop_attr_t attrs) {
struct v7_property *p;
struct v7_object *o;
val_t ss;
if (!v7_is_object(obj)) {
return NULL;
}
if (len == (size_t) ~0) {
len = strlen(name);
}
o = get_object_struct(obj);
/*
* len check is needed to allow getting the mbuf from the hidden property.
* TODO(mkm): however hidden properties cannot be safely represented with
* a zero length string anyway, so this will change.
*/
if (o->attributes & V7_OBJ_DENSE_ARRAY && len > 0) {
int ok, has;
unsigned long i = cstr_to_ulong(name, len, &ok);
if (ok) {
v7->cur_dense_prop->value = v7_array_get2(v7, obj, i, &has);
return has ? v7->cur_dense_prop : NULL;
}
}
if (len <= 5) {
ss = v7_mk_string(v7, name, len, 1);
for (p = o->properties; p != NULL; p = p->next) {
#if V7_ENABLE_ENTITY_IDS
if (p->entity_id != V7_ENTITY_ID_PROP) {
fprintf(stderr, "not a prop!=0x%x\n", p->entity_id);
abort();
}
#endif
if (p->name == ss && (attrs == 0 || (p->attributes & attrs))) {
return p;
}
}
} else {
for (p = o->properties; p != NULL; p = p->next) {
size_t n;
const char *s = v7_get_string(v7, &p->name, &n);
#if V7_ENABLE_ENTITY_IDS
if (p->entity_id != V7_ENTITY_ID_PROP) {
fprintf(stderr, "not a prop!=0x%x\n", p->entity_id);
abort();
}
#endif
if (n == len && strncmp(s, name, len) == 0 &&
(attrs == 0 || (p->attributes & attrs))) {
return p;
}
}
}
return NULL;
}
V7_PRIVATE struct v7_property *v7_get_own_property(struct v7 *v7, val_t obj,
const char *name,
size_t len) {
return v7_get_own_property2(v7, obj, name, len, 0);
}
V7_PRIVATE struct v7_property *v7_get_property(struct v7 *v7, val_t obj,
const char *name, size_t len) {
if (!v7_is_object(obj)) {
return NULL;
}
for (; obj != V7_NULL; obj = v7_get_proto(v7, obj)) {
struct v7_property *prop;
if ((prop = v7_get_own_property(v7, obj, name, len)) != NULL) {
return prop;
}
}
return NULL;
}
V7_PRIVATE enum v7_err v7_get_property_v(struct v7 *v7, val_t obj,
v7_val_t name,
struct v7_property **res) {
enum v7_err rcode = V7_OK;
size_t name_len;
STATIC char buf[8];
const char *s = buf;
uint8_t fr = 0;
if (v7_is_string(name)) {
s = v7_get_string(v7, &name, &name_len);
} else {
char *stmp;
V7_TRY(v7_stringify_throwing(v7, name, buf, sizeof(buf),
V7_STRINGIFY_DEFAULT, &stmp));
s = stmp;
if (s != buf) {
fr = 1;
}
name_len = strlen(s);
}
*res = v7_get_property(v7, obj, s, name_len);
clean:
if (fr) {
free((void *) s);
}
return rcode;
}
WARN_UNUSED_RESULT
enum v7_err v7_get_throwing(struct v7 *v7, val_t obj, const char *name,
size_t name_len, val_t *res) {
enum v7_err rcode = V7_OK;
val_t v = obj;
v7_own(v7, &v);
if (name_len == (size_t) ~0) {
name_len = strlen(name);
}
if (v7_is_string(obj)) {
v = v7->vals.string_prototype;
} else if (v7_is_number(obj)) {
v = v7->vals.number_prototype;
} else if (v7_is_boolean(obj)) {
v = v7->vals.boolean_prototype;
} else if (v7_is_undefined(obj)) {
rcode =
v7_throwf(v7, TYPE_ERROR, "cannot read property '%.*s' of undefined",
(int) name_len, name);
goto clean;
} else if (v7_is_null(obj)) {
rcode = v7_throwf(v7, TYPE_ERROR, "cannot read property '%.*s' of null",
(int) name_len, name);
goto clean;
} else if (is_cfunction_lite(obj)) {
v = v7->vals.function_prototype;
}
#if V7_ENABLE__Proxy
{
struct v7_object *o = NULL;
if (v7_is_object(obj)) {
o = get_object_struct(obj);
}
if (o != NULL && (o->attributes & V7_OBJ_PROXY) &&
!is_special_proxy_name(name, name_len)) {
/* we need to access the target object through a proxy */
val_t target_v = V7_UNDEFINED;
val_t handler_v = V7_UNDEFINED;
val_t name_v = V7_UNDEFINED;
val_t get_v = V7_UNDEFINED;
val_t get_args_v = V7_UNDEFINED;
/*
* we need to create a copy of the name, because the given `name` might
* be returned by v7_get_string(), and any object creation might
* invalidate this pointer. Below, we're going to create some objects.
*
* It would probably be cleaner to always create a copy before calling
* v7_get_throwing if the name was returned by v7_get_string(), but that
* would cause additional pressure on the heap, so let's not do that
*/
char *name_copy = (char *) calloc(1, name_len + 1 /* null-term */);
memcpy(name_copy, name, name_len);
v7_own(v7, &target_v);
v7_own(v7, &handler_v);
v7_own(v7, &name_v);
v7_own(v7, &get_v);
v7_own(v7, &get_args_v);
V7_TRY2(v7_get_throwing(v7, obj, _V7_PROXY_TARGET_NAME, ~0, &target_v),
clean_proxy);
V7_TRY2(v7_get_throwing(v7, obj, _V7_PROXY_HANDLER_NAME, ~0, &handler_v),
clean_proxy);
V7_TRY2(v7_get_throwing(v7, handler_v, "get", ~0, &get_v), clean_proxy);
if (v7_is_callable(v7, get_v)) {
/* The `get` callback is actually callable, so, use it */
/* prepare arguments for the callback */
get_args_v = v7_mk_dense_array(v7);
/*
* TODO(dfrank): don't copy string in case we already have val_t (we
* need some generic function which will take both `const char *` and
* val_t)
*/
v7_array_set(v7, get_args_v, 0, target_v);
v7_array_set(v7, get_args_v, 1,
v7_mk_string(v7, name_copy, name_len, 1));
/* call `get` callback */
V7_TRY2(b_apply(v7, get_v, V7_UNDEFINED, get_args_v, 0, res),
clean_proxy);
} else {
/*
* there's no `get` callback: then, get property from the target object
* (not from the proxy object)
*/
V7_TRY2(v7_get_throwing(v7, target_v, name_copy, name_len, res),
clean_proxy);
}
clean_proxy:
free(name_copy);
v7_disown(v7, &get_args_v);
v7_disown(v7, &get_v);
v7_disown(v7, &name_v);
v7_disown(v7, &handler_v);
v7_disown(v7, &target_v);
goto clean;
}
}
#endif
/* regular (non-proxy) property access */
V7_TRY(
v7_property_value(v7, obj, v7_get_property(v7, v, name, name_len), res));
clean:
v7_disown(v7, &v);
return rcode;
}
v7_val_t v7_get(struct v7 *v7, val_t obj, const char *name, size_t name_len) {
enum v7_err rcode = V7_OK;
uint8_t saved_is_thrown = 0;
val_t saved_thrown = v7_get_thrown_value(v7, &saved_is_thrown);
v7_val_t ret = V7_UNDEFINED;
rcode = v7_get_throwing(v7, obj, name, name_len, &ret);
if (rcode != V7_OK) {
rcode = V7_OK;
if (saved_is_thrown) {
rcode = v7_throw(v7, saved_thrown);
} else {
v7_clear_thrown_value(v7);
}
ret = V7_UNDEFINED;
}
return ret;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err v7_get_throwing_v(struct v7 *v7, v7_val_t obj,
v7_val_t name, v7_val_t *res) {
enum v7_err rcode = V7_OK;
size_t name_len;
STATIC char buf[8];
const char *s = buf;
uint8_t fr = 0;
/* subscripting strings */
if (v7_is_string(obj)) {
char ch;
double dch = 0;
rcode = v7_char_code_at(v7, obj, name, &dch);
if (rcode != V7_OK) {
goto clean;
}
if (!isnan(dch)) {
ch = dch;
*res = v7_mk_string(v7, &ch, 1, 1);
goto clean;
}
}
if (v7_is_string(name)) {
s = v7_get_string(v7, &name, &name_len);
} else {
char *stmp;
V7_TRY(v7_stringify_throwing(v7, name, buf, sizeof(buf),
V7_STRINGIFY_DEFAULT, &stmp));
s = stmp;
if (s != buf) {
fr = 1;
}
name_len = strlen(s);
}
V7_TRY(v7_get_throwing(v7, obj, s, name_len, res));
clean:
if (fr) {
free((void *) s);
}
return rcode;
}
V7_PRIVATE void v7_destroy_property(struct v7_property **p) {
*p = NULL;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err v7_invoke_setter(struct v7 *v7, struct v7_property *prop,
val_t obj, val_t val) {
enum v7_err rcode = V7_OK;
val_t setter = prop->value, args;
v7_own(v7, &val);
args = v7_mk_dense_array(v7);
v7_own(v7, &args);
if (prop->attributes & V7_PROPERTY_GETTER) {
setter = v7_array_get(v7, prop->value, 1);
}
v7_array_set(v7, args, 0, val);
v7_disown(v7, &args);
v7_disown(v7, &val);
{
val_t val = V7_UNDEFINED;
V7_TRY(b_apply(v7, setter, obj, args, 0, &val));
}
clean:
return rcode;
}
static v7_prop_attr_t apply_attrs_desc(v7_prop_attr_desc_t attrs_desc,
v7_prop_attr_t old_attrs) {
v7_prop_attr_t ret = old_attrs;
if (old_attrs & V7_PROPERTY_NON_CONFIGURABLE) {
/*
* The property is non-configurable: we can only change it from being
* writable to non-writable
*/
if ((attrs_desc >> _V7_DESC_SHIFT) & V7_PROPERTY_NON_WRITABLE &&
(attrs_desc & V7_PROPERTY_NON_WRITABLE)) {
ret |= V7_PROPERTY_NON_WRITABLE;
}
} else {
/* The property is configurable: we can change any attributes */
ret = (old_attrs & ~(attrs_desc >> _V7_DESC_SHIFT)) |
(attrs_desc & _V7_DESC_MASK);
}
return ret;
}
int v7_def(struct v7 *v7, val_t obj, const char *name, size_t len,
v7_prop_attr_desc_t attrs_desc, v7_val_t val) {
enum v7_err rcode = V7_OK;
uint8_t saved_is_thrown = 0;
val_t saved_thrown = v7_get_thrown_value(v7, &saved_is_thrown);
int ret = -1;
{
struct v7_property *tmp = NULL;
rcode = def_property(v7, obj, name, len, attrs_desc, val, 0 /*not assign*/,
&tmp);
ret = (tmp == NULL) ? -1 : 0;
}
if (rcode != V7_OK) {
rcode = V7_OK;
if (saved_is_thrown) {
rcode = v7_throw(v7, saved_thrown);
} else {
v7_clear_thrown_value(v7);
}
ret = -1;
}
return ret;
}
int v7_set(struct v7 *v7, val_t obj, const char *name, size_t len,
v7_val_t val) {
enum v7_err rcode = V7_OK;
uint8_t saved_is_thrown = 0;
val_t saved_thrown = v7_get_thrown_value(v7, &saved_is_thrown);
int ret = -1;
{
struct v7_property *tmp = NULL;
rcode = set_property(v7, obj, name, len, val, &tmp);
ret = (tmp == NULL) ? -1 : 0;
}
if (rcode != V7_OK) {
rcode = V7_OK;
if (saved_is_thrown) {
rcode = v7_throw(v7, saved_thrown);
} else {
v7_clear_thrown_value(v7);
}
ret = -1;
}
return ret;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err set_property_v(struct v7 *v7, val_t obj, val_t name,
val_t val, struct v7_property **res) {
return def_property_v(v7, obj, name, 0, val, 1 /*as_assign*/, res);
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err set_property(struct v7 *v7, val_t obj, const char *name,
size_t len, v7_val_t val,
struct v7_property **res) {
return def_property(v7, obj, name, len, 0, val, 1 /*as_assign*/, res);
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err def_property_v(struct v7 *v7, val_t obj, val_t name,
v7_prop_attr_desc_t attrs_desc, val_t val,
uint8_t as_assign,
struct v7_property **res) {
enum v7_err rcode = V7_OK;
struct v7_property *prop = NULL;
size_t len;
const char *n = v7_get_string(v7, &name, &len);
v7_own(v7, &name);
v7_own(v7, &val);
if (!v7_is_object(obj)) {
prop = NULL;
goto clean;
}
#if V7_ENABLE__Proxy
if ((get_object_struct(obj)->attributes & V7_OBJ_PROXY) &&
!is_special_proxy_name(n, len)) {
/* we need to access the target object through a proxy */
val_t target_v = V7_UNDEFINED;
val_t handler_v = V7_UNDEFINED;
val_t set_v = V7_UNDEFINED;
val_t set_args_v = V7_UNDEFINED;
v7_own(v7, &target_v);
v7_own(v7, &handler_v);
v7_own(v7, &set_v);
v7_own(v7, &set_args_v);
V7_TRY2(v7_get_throwing(v7, obj, _V7_PROXY_TARGET_NAME, ~0, &target_v),
clean_proxy);
V7_TRY2(v7_get_throwing(v7, obj, _V7_PROXY_HANDLER_NAME, ~0, &handler_v),
clean_proxy);
/*
* We'll consult "set" property in case of the plain assignment only;
* Object.defineProperty() has its own trap `defineProperty` which is not
* yet implemented in v7
*/
if (as_assign) {
V7_TRY2(v7_get_throwing(v7, handler_v, "set", ~0, &set_v), clean_proxy);
}
if (v7_is_callable(v7, set_v)) {
/* The `set` callback is actually callable, so, use it */
/* prepare arguments for the callback */
set_args_v = v7_mk_dense_array(v7);
/*
* TODO(dfrank): don't copy string in case we already have val_t
* (we need some generic function which will take both const char * and
* val_t for that)
*/
v7_array_set(v7, set_args_v, 0, target_v);
v7_array_set(v7, set_args_v, 1, name);
v7_array_set(v7, set_args_v, 2, val);
/* call `set` callback */
V7_TRY2(b_apply(v7, set_v, V7_UNDEFINED, set_args_v, 0, &val),
clean_proxy);
/* in strict mode, we should throw if trap returned falsy value */
if (is_strict_mode(v7) && !v7_is_truthy(v7, val)) {
V7_THROW2(
v7_throwf(v7, TYPE_ERROR, "Trap returned falsy for property '%s'",
v7_get_string(v7, &name, NULL)),
clean_proxy);
}
} else {
/*
* there's no `set` callback: then, set property on the target object
* (not on the proxy object)
*/
V7_TRY2(
def_property_v(v7, target_v, name, attrs_desc, val, as_assign, res),
clean_proxy);
}
clean_proxy:
v7_disown(v7, &set_args_v);
v7_disown(v7, &set_v);
v7_disown(v7, &handler_v);
v7_disown(v7, &target_v);
goto clean;
}
#endif
/* regular (non-proxy) property access */
prop = v7_get_own_property(v7, obj, n, len);
if (prop == NULL) {
/*
* The own property with given `name` doesn't exist yet: try to create it,
* set requested `name` and `attributes`, and append to the object's
* properties
*/
/* make sure the object is extensible */
if (get_object_struct(obj)->attributes & V7_OBJ_NOT_EXTENSIBLE) {
/*
* We should throw if we use `Object.defineProperty`, or if we're in
* strict mode.
*/
if (is_strict_mode(v7) || !as_assign) {
V7_THROW(v7_throwf(v7, TYPE_ERROR, "Object is not extensible"));
}
prop = NULL;
goto clean;
}
if ((prop = v7_mk_property(v7)) == NULL) {
prop = NULL; /* LCOV_EXCL_LINE */
goto clean;
}
prop->name = name;
prop->value = val;
prop->attributes = apply_attrs_desc(attrs_desc, V7_DEFAULT_PROPERTY_ATTRS);
prop->next = get_object_struct(obj)->properties;
get_object_struct(obj)->properties = prop;
goto clean;
} else {
/* Property already exists */
if (prop->attributes & V7_PROPERTY_NON_WRITABLE) {
/* The property is read-only */
if (as_assign) {
/* Plain assignment: in strict mode throw, otherwise ignore */
if (is_strict_mode(v7)) {
V7_THROW(
v7_throwf(v7, TYPE_ERROR, "Cannot assign to read-only property"));
} else {
prop = NULL;
goto clean;
}
} else if (prop->attributes & V7_PROPERTY_NON_CONFIGURABLE) {
/*
* Use `Object.defineProperty` semantic, and the property is
* non-configurable: if no value is provided, or if new value is equal
* to the existing one, then just fall through to change attributes;
* otherwise, throw.
*/
if (!(attrs_desc & V7_DESC_PRESERVE_VALUE)) {
uint8_t equal = 0;
if (v7_is_string(val) && v7_is_string(prop->value)) {
equal = (s_cmp(v7, val, prop->value) == 0);
} else {
equal = (val == prop->value);
}
if (!equal) {
/* Values are not equal: should throw */
V7_THROW(v7_throwf(v7, TYPE_ERROR,
"Cannot redefine read-only property"));
} else {
/*
* Values are equal. Will fall through so that attributes might
* change.
*/
}
} else {
/*
* No value is provided. Will fall through so that attributes might
* change.
*/
}
} else {
/*
* Use `Object.defineProperty` semantic, and the property is
* configurable: will fall through and assign new value, effectively
* ignoring non-writable flag. This is the same as making a property
* writable, then assigning a new value, and making a property
* non-writable again.
*/
}
} else if (prop->attributes & V7_PROPERTY_SETTER) {
/* Invoke setter */
V7_TRY(v7_invoke_setter(v7, prop, obj, val));
prop = NULL;
goto clean;
}
/* Set value and apply attrs delta */
if (!(attrs_desc & V7_DESC_PRESERVE_VALUE)) {
prop->value = val;
}
prop->attributes = apply_attrs_desc(attrs_desc, prop->attributes);
}
clean:
if (res != NULL) {
*res = prop;
}
v7_disown(v7, &val);
v7_disown(v7, &name);
return rcode;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err def_property(struct v7 *v7, val_t obj, const char *name,
size_t len, v7_prop_attr_desc_t attrs_desc,
v7_val_t val, uint8_t as_assign,
struct v7_property **res) {
enum v7_err rcode = V7_OK;
val_t name_val = V7_UNDEFINED;
v7_own(v7, &obj);
v7_own(v7, &val);
v7_own(v7, &name_val);
if (len == (size_t) ~0) {
len = strlen(name);
}
name_val = v7_mk_string(v7, name, len, 1);
V7_TRY(def_property_v(v7, obj, name_val, attrs_desc, val, as_assign, res));
clean:
v7_disown(v7, &name_val);
v7_disown(v7, &val);
v7_disown(v7, &obj);
return rcode;
}
V7_PRIVATE int set_method(struct v7 *v7, v7_val_t obj, const char *name,
v7_cfunction_t *func, int num_args) {
return v7_def(v7, obj, name, strlen(name), V7_DESC_ENUMERABLE(0),
mk_cfunction_obj(v7, func, num_args));
}
int v7_set_method(struct v7 *v7, v7_val_t obj, const char *name,
v7_cfunction_t *func) {
return set_method(v7, obj, name, func, ~0);
}
V7_PRIVATE int set_cfunc_prop(struct v7 *v7, val_t o, const char *name,
v7_cfunction_t *f) {
return v7_def(v7, o, name, strlen(name), V7_DESC_ENUMERABLE(0),
v7_mk_cfunction(f));
}
/*
* See comments in `object_public.h`
*/
int v7_del(struct v7 *v7, val_t obj, const char *name, size_t len) {
struct v7_property *prop, *prev;
if (!v7_is_object(obj)) {
return -1;
}
if (len == (size_t) ~0) {
len = strlen(name);
}
for (prev = NULL, prop = get_object_struct(obj)->properties; prop != NULL;
prev = prop, prop = prop->next) {
size_t n;
const char *s = v7_get_string(v7, &prop->name, &n);
if (n == len && strncmp(s, name, len) == 0) {
if (prev) {
prev->next = prop->next;
} else {
get_object_struct(obj)->properties = prop->next;
}
v7_destroy_property(&prop);
return 0;
}
}
return -1;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err v7_property_value(struct v7 *v7, val_t obj,
struct v7_property *p, val_t *res) {
enum v7_err rcode = V7_OK;
if (p == NULL) {
*res = V7_UNDEFINED;
goto clean;
}
if (p->attributes & V7_PROPERTY_GETTER) {
val_t getter = p->value;
if (p->attributes & V7_PROPERTY_SETTER) {
getter = v7_array_get(v7, p->value, 0);
}
{
V7_TRY(b_apply(v7, getter, obj, V7_UNDEFINED, 0, res));
goto clean;
}
}
*res = p->value;
goto clean;
clean:
return rcode;
}
enum v7_err v7_init_prop_iter_ctx(struct v7 *v7, v7_val_t obj,
struct prop_iter_ctx *ctx) {
return init_prop_iter_ctx(v7, obj, 1 /*proxy-transparent*/, ctx);
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err init_prop_iter_ctx(struct v7 *v7, v7_val_t obj,
int proxy_transp,
struct prop_iter_ctx *ctx) {
enum v7_err rcode = V7_OK;
v7_own(v7, &obj);
memset(ctx, 0x00, sizeof(*ctx));
if (v7_is_object(obj)) {
#if V7_ENABLE__Proxy
if (proxy_transp && get_object_struct(obj)->attributes & V7_OBJ_PROXY) {
v7_val_t ownKeys_v = V7_UNDEFINED;
v7_val_t args_v = V7_UNDEFINED;
v7_own(v7, &ownKeys_v);
v7_own(v7, &args_v);
ctx->proxy_ctx =
(struct prop_iter_proxy_ctx *) calloc(1, sizeof(*ctx->proxy_ctx));
ctx->proxy_ctx->target_obj = V7_UNDEFINED;
ctx->proxy_ctx->handler_obj = V7_UNDEFINED;
ctx->proxy_ctx->own_keys = V7_UNDEFINED;
ctx->proxy_ctx->get_own_prop_desc = V7_UNDEFINED;
v7_own(v7, &ctx->proxy_ctx->target_obj);
v7_own(v7, &ctx->proxy_ctx->handler_obj);
v7_own(v7, &ctx->proxy_ctx->own_keys);
v7_own(v7, &ctx->proxy_ctx->get_own_prop_desc);
V7_TRY2(v7_get_throwing(v7, obj, _V7_PROXY_TARGET_NAME, ~0,
&ctx->proxy_ctx->target_obj),
clean_proxy);
V7_TRY2(v7_get_throwing(v7, obj, _V7_PROXY_HANDLER_NAME, ~0,
&ctx->proxy_ctx->handler_obj),
clean_proxy);
V7_TRY2(v7_get_throwing(v7, ctx->proxy_ctx->handler_obj, "ownKeys", ~0,
&ownKeys_v),
clean_proxy);
if (v7_is_callable(v7, ownKeys_v)) {
/* prepare arguments for the ownKeys callback */
args_v = v7_mk_dense_array(v7);
v7_array_set(v7, args_v, 0, ctx->proxy_ctx->target_obj);
/* call `ownKeys` callback, and save the result in context */
V7_TRY2(b_apply(v7, ownKeys_v, V7_UNDEFINED, args_v, 0,
&ctx->proxy_ctx->own_keys),
clean_proxy);
ctx->proxy_ctx->has_own_keys = 1;
ctx->proxy_ctx->own_key_idx = 0;
} else {
/*
* No ownKeys callback, so we'll iterate real properties of the target
* object
*/
/*
* TODO(dfrank): add support for the target object which is a proxy as
* well
*/
ctx->cur_prop =
get_object_struct(ctx->proxy_ctx->target_obj)->properties;
}
V7_TRY2(v7_get_throwing(v7, ctx->proxy_ctx->handler_obj, "_gpdc", ~0,
&ctx->proxy_ctx->get_own_prop_desc),
clean_proxy);
if (v7_is_foreign(ctx->proxy_ctx->get_own_prop_desc)) {
/*
* C callback for getting property descriptor is provided: will use it
*/
ctx->proxy_ctx->has_get_own_prop_desc = 1;
ctx->proxy_ctx->has_get_own_prop_desc_C = 1;
} else {
/*
* No C callback for getting property descriptor is provided, let's
* check if there is a JS one..
*/
V7_TRY2(v7_get_throwing(v7, ctx->proxy_ctx->handler_obj,
"getOwnPropertyDescriptor", ~0,
&ctx->proxy_ctx->get_own_prop_desc),
clean_proxy);
if (v7_is_callable(v7, ctx->proxy_ctx->get_own_prop_desc)) {
/* Yes there is, we'll use it */
ctx->proxy_ctx->has_get_own_prop_desc = 1;
}
}
clean_proxy:
v7_disown(v7, &args_v);
v7_disown(v7, &ownKeys_v);
if (rcode != V7_OK) {
/* something went wrong, so, disown values in the context and free it */
v7_disown(v7, &ctx->proxy_ctx->get_own_prop_desc);
v7_disown(v7, &ctx->proxy_ctx->own_keys);
v7_disown(v7, &ctx->proxy_ctx->handler_obj);
v7_disown(v7, &ctx->proxy_ctx->target_obj);
free(ctx->proxy_ctx);
ctx->proxy_ctx = NULL;
goto clean;
}
} else {
#else
(void) proxy_transp;
#endif
/* Object is not a proxy: we'll iterate real properties */
ctx->cur_prop = get_object_struct(obj)->properties;
#if V7_ENABLE__Proxy
}
#endif
}
#if V7_ENABLE__Proxy
clean:
#endif
v7_disown(v7, &obj);
if (rcode == V7_OK) {
ctx->init = 1;
}
return rcode;
}
void v7_destruct_prop_iter_ctx(struct v7 *v7, struct prop_iter_ctx *ctx) {
if (ctx->init) {
#if V7_ENABLE__Proxy
if (ctx->proxy_ctx != NULL) {
v7_disown(v7, &ctx->proxy_ctx->target_obj);
v7_disown(v7, &ctx->proxy_ctx->handler_obj);
v7_disown(v7, &ctx->proxy_ctx->own_keys);
v7_disown(v7, &ctx->proxy_ctx->get_own_prop_desc);
}
free(ctx->proxy_ctx);
ctx->proxy_ctx = NULL;
#else
(void) v7;
#endif
ctx->init = 0;
}
}
int v7_next_prop(struct v7 *v7, struct prop_iter_ctx *ctx, v7_val_t *name,
v7_val_t *value, v7_prop_attr_t *attrs) {
int ok = 0;
if (next_prop(v7, ctx, name, value, attrs, &ok) != V7_OK) {
fprintf(stderr, "next_prop failed\n");
ok = 0;
}
return ok;
}
#if V7_ENABLE__Proxy
WARN_UNUSED_RESULT
static enum v7_err get_custom_prop_desc(struct v7 *v7, v7_val_t name,
struct prop_iter_ctx *ctx,
struct v7_property *res_prop, int *ok) {
enum v7_err rcode = V7_OK;
v7_val_t args_v = V7_UNDEFINED;
v7_val_t desc_v = V7_UNDEFINED;
v7_val_t tmpflag_v = V7_UNDEFINED;
v7_own(v7, &name);
v7_own(v7, &args_v);
v7_own(v7, &desc_v);
v7_own(v7, &tmpflag_v);
*ok = 0;
if (ctx->proxy_ctx->has_get_own_prop_desc_C) {
/*
* There is a C callback which should fill the property descriptor
* structure, see `v7_get_own_prop_desc_cb_t`
*/
v7_get_own_prop_desc_cb_t *cb = NULL;
memset(res_prop, 0, sizeof(*res_prop));
cb = (v7_get_own_prop_desc_cb_t *) v7_get_ptr(
v7, ctx->proxy_ctx->get_own_prop_desc);
res_prop->attributes = 0;
res_prop->value = V7_UNDEFINED;
*ok = !!cb(v7, ctx->proxy_ctx->target_obj, name, &res_prop->attributes,
&res_prop->value);
} else {
/* prepare arguments for the getOwnPropertyDescriptor callback */
args_v = v7_mk_dense_array(v7);
v7_array_set(v7, args_v, 0, ctx->proxy_ctx->target_obj);
v7_array_set(v7, args_v, 1, name);
/* call getOwnPropertyDescriptor callback */
V7_TRY(b_apply(v7, ctx->proxy_ctx->get_own_prop_desc, V7_UNDEFINED, args_v,
0, &desc_v));
if (v7_is_object(desc_v)) {
res_prop->attributes = 0;
V7_TRY(v7_get_throwing(v7, desc_v, "writable", ~0, &tmpflag_v));
if (!v7_is_truthy(v7, tmpflag_v)) {
res_prop->attributes |= V7_PROPERTY_NON_WRITABLE;
}
V7_TRY(v7_get_throwing(v7, desc_v, "configurable", ~0, &tmpflag_v));
if (!v7_is_truthy(v7, tmpflag_v)) {
res_prop->attributes |= V7_PROPERTY_NON_CONFIGURABLE;
}
V7_TRY(v7_get_throwing(v7, desc_v, "enumerable", ~0, &tmpflag_v));
if (!v7_is_truthy(v7, tmpflag_v)) {
res_prop->attributes |= V7_PROPERTY_NON_ENUMERABLE;
}
V7_TRY(v7_get_throwing(v7, desc_v, "value", ~0, &res_prop->value));
*ok = 1;
}
}
/* We always set the name in the property descriptor to the actual name */
res_prop->name = name;
clean:
v7_disown(v7, &tmpflag_v);
v7_disown(v7, &desc_v);
v7_disown(v7, &args_v);
v7_disown(v7, &name);
return rcode;
}
#endif
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err next_prop(struct v7 *v7, struct prop_iter_ctx *ctx,
v7_val_t *name, v7_val_t *value,
v7_prop_attr_t *attrs, int *ok) {
enum v7_err rcode = V7_OK;
struct v7_property p;
(void) v7;
memset(&p, 0, sizeof(p));
p.name = V7_UNDEFINED;
p.value = V7_UNDEFINED;
v7_own(v7, &p.name);
v7_own(v7, &p.value);
assert(ctx->init);
*ok = 0;
#if V7_ENABLE__Proxy
if (ctx->proxy_ctx == NULL || !ctx->proxy_ctx->has_own_keys) {
/*
* No `ownKeys` callback, so we'll iterate real properties of the object
* (either the given object or, if it's a proxy, the proxy's target object)
*/
if (ctx->cur_prop != NULL) {
if (ctx->proxy_ctx == NULL || !ctx->proxy_ctx->has_get_own_prop_desc) {
/*
* There is no `getOwnPropertyDescriptor` callback, so, use the current
* real property
*/
memcpy(&p, ctx->cur_prop, sizeof(p));
*ok = 1;
} else {
/*
* There is a `getOwnPropertyDescriptor` callback, so call it for the
* name of the current real property
*/
V7_TRY(get_custom_prop_desc(v7, ctx->cur_prop->name, ctx, &p, ok));
}
ctx->cur_prop = ctx->cur_prop->next;
}
} else {
/* We have custom own keys */
v7_val_t cur_key = V7_UNDEFINED;
size_t len = v7_array_length(v7, ctx->proxy_ctx->own_keys);
v7_own(v7, &cur_key);
/*
* Iterate through the custom own keys until we can get the proper property
* descriptor for the given key
*/
while (!*ok && (size_t) ctx->proxy_ctx->own_key_idx < len) {
cur_key = v7_array_get(v7, ctx->proxy_ctx->own_keys,
ctx->proxy_ctx->own_key_idx);
ctx->proxy_ctx->own_key_idx++;
if (ctx->proxy_ctx->has_get_own_prop_desc) {
/*
* There is a `getOwnPropertyDescriptor` callback, so, call it for the
* current custom key and get all descriptor data from the object
* returned. The `ok` variable will be updated appropriately (it will
* be 0 if the callback did not return a proper descriptor)
*/
V7_TRY2(get_custom_prop_desc(v7, cur_key, ctx, &p, ok), clean_custom);
} else {
/*
* There is no `getOwnPropertyDescriptor` callback, so, try to get
* real property with the name equal to the current key
*/
size_t len = 0;
const char *name = v7_get_string(v7, &cur_key, &len);
struct v7_property *real_prop =
v7_get_own_property(v7, ctx->proxy_ctx->target_obj, name, len);
if (real_prop != NULL) {
/* Property exists, so use data from its descriptor */
memcpy(&p, real_prop, sizeof(p));
*ok = 1;
}
}
}
clean_custom:
v7_disown(v7, &cur_key);
if (rcode != V7_OK) {
goto clean;
}
}
#else
/*
* Proxy is disabled: just get the next property
*/
if (ctx->cur_prop != NULL) {
memcpy(&p, ctx->cur_prop, sizeof(p));
*ok = 1;
ctx->cur_prop = ctx->cur_prop->next;
}
#endif
/* If we have a valid property descriptor, use data from it */
if (*ok) {
if (name != NULL) *name = p.name;
if (value != NULL) *value = p.value;
if (attrs != NULL) *attrs = p.attributes;
}
#if V7_ENABLE__Proxy
clean:
#endif
v7_disown(v7, &p.value);
v7_disown(v7, &p.name);
return rcode;
}
/* }}} Object properties */
/* Object prototypes {{{ */
V7_PRIVATE int obj_prototype_set(struct v7 *v7, struct v7_object *obj,
struct v7_object *proto) {
int ret = -1;
(void) v7;
if (obj->attributes & V7_OBJ_FUNCTION) {
ret = -1;
} else {
((struct v7_generic_object *) obj)->prototype = proto;
ret = 0;
}
return ret;
}
V7_PRIVATE struct v7_object *obj_prototype(struct v7 *v7,
struct v7_object *obj) {
if (obj->attributes & V7_OBJ_FUNCTION) {
return get_object_struct(v7->vals.function_prototype);
} else {
return ((struct v7_generic_object *) obj)->prototype;
}
}
V7_PRIVATE int is_prototype_of(struct v7 *v7, val_t o, val_t p) {
if (!v7_is_object(o) || !v7_is_object(p)) {
return 0;
}
/* walk the prototype chain */
for (; !v7_is_null(o); o = v7_get_proto(v7, o)) {
if (v7_get_proto(v7, o) == p) {
return 1;
}
}
return 0;
}
int v7_is_instanceof(struct v7 *v7, val_t o, const char *c) {
return v7_is_instanceof_v(v7, o, v7_get(v7, v7->vals.global_object, c, ~0));
}
int v7_is_instanceof_v(struct v7 *v7, val_t o, val_t c) {
return is_prototype_of(v7, o, v7_get(v7, c, "prototype", 9));
}
v7_val_t v7_set_proto(struct v7 *v7, v7_val_t obj, v7_val_t proto) {
if (v7_is_generic_object(obj)) {
v7_val_t old_proto =
v7_object_to_value(obj_prototype(v7, get_object_struct(obj)));
obj_prototype_set(v7, get_object_struct(obj), get_object_struct(proto));
return old_proto;
} else {
return V7_UNDEFINED;
}
}
val_t v7_get_proto(struct v7 *v7, val_t obj) {
/*
* NOTE: we don't use v7_is_callable() here, because it involves walking
* through the object's properties, which may be expensive. And it's done
* anyway for cfunction objects as it would for any other generic objects by
* the call to `obj_prototype()`.
*
* Since this function is called quite often (at least, GC walks the
* prototype chain), it's better to just handle cfunction objects as generic
* objects.
*/
if (is_js_function(obj) || is_cfunction_lite(obj)) {
return v7->vals.function_prototype;
}
return v7_object_to_value(obj_prototype(v7, get_object_struct(obj)));
}
V7_PRIVATE struct v7_property *get_user_data_property(v7_val_t obj) {
struct v7_property *p;
struct v7_object *o;
if (!v7_is_object(obj)) return NULL;
o = get_object_struct(obj);
for (p = o->properties; p != NULL; p = p->next) {
if (p->attributes & _V7_PROPERTY_USER_DATA_AND_DESTRUCTOR) {
return p;
}
}
return NULL;
}
/*
* Returns the user data property structure associated with obj, or NULL if
* `obj` is not an object.
*/
static struct v7_property *get_or_create_user_data_property(struct v7 *v7,
v7_val_t obj) {
struct v7_property *p = get_user_data_property(obj);
struct v7_object *o;
if (p != NULL) return p;
if (!v7_is_object(obj)) return NULL;
o = get_object_struct(obj);
v7_own(v7, &obj);
p = v7_mk_property(v7);
v7_disown(v7, &obj);
p->attributes |= _V7_PROPERTY_USER_DATA_AND_DESTRUCTOR | _V7_PROPERTY_HIDDEN;
p->next = o->properties;
o->properties = p;
return p;
}
void v7_set_user_data(struct v7 *v7, v7_val_t obj, void *ud) {
struct v7_property *p = get_or_create_user_data_property(v7, obj);
if (p == NULL) return;
p->value = v7_mk_foreign(v7, ud);
}
void *v7_get_user_data(struct v7 *v7, v7_val_t obj) {
struct v7_property *p = get_user_data_property(obj);
(void) v7;
if (p == NULL) return NULL;
return v7_get_ptr(v7, p->value);
}
void v7_set_destructor_cb(struct v7 *v7, v7_val_t obj, v7_destructor_cb_t *d) {
struct v7_property *p = get_or_create_user_data_property(v7, obj);
struct v7_object *o;
union {
void *v;
v7_destructor_cb_t *f;
} fu;
if (p == NULL) return;
o = get_object_struct(obj);
if (d != NULL) {
o->attributes |= V7_OBJ_HAS_DESTRUCTOR;
fu.f = d;
p->name = v7_mk_foreign(v7, fu.v);
} else {
o->attributes &= ~V7_OBJ_HAS_DESTRUCTOR;
p->name = V7_UNDEFINED;
}
}
/* }}} Object prototypes */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/regexp.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* Amalgamated: #include "v7/src/primitive.h" */
/* Amalgamated: #include "v7/src/object.h" */
/* Amalgamated: #include "v7/src/regexp.h" */
/* Amalgamated: #include "v7/src/exceptions.h" */
/* Amalgamated: #include "v7/src/string.h" */
/* Amalgamated: #include "v7/src/slre.h" */
#if V7_ENABLE__RegExp
enum v7_err v7_mk_regexp(struct v7 *v7, const char *re, size_t re_len,
const char *flags, size_t flags_len, v7_val_t *res) {
enum v7_err rcode = V7_OK;
struct slre_prog *p = NULL;
struct v7_regexp *rp;
if (re_len == ~((size_t) 0)) re_len = strlen(re);
if (slre_compile(re, re_len, flags, flags_len, &p, 1) != SLRE_OK ||
p == NULL) {
rcode = v7_throwf(v7, TYPE_ERROR, "Invalid regex");
goto clean;
} else {
*res = mk_object(v7, v7->vals.regexp_prototype);
rp = (struct v7_regexp *) malloc(sizeof(*rp));
rp->regexp_string = v7_mk_string(v7, re, re_len, 1);
v7_own(v7, &rp->regexp_string);
rp->compiled_regexp = p;
rp->lastIndex = 0;
v7_def(v7, *res, "", 0, _V7_DESC_HIDDEN(1),
pointer_to_value(rp) | V7_TAG_REGEXP);
}
clean:
return rcode;
}
V7_PRIVATE struct v7_regexp *v7_get_regexp_struct(struct v7 *v7, val_t v) {
struct v7_property *p;
int is = v7_is_regexp(v7, v);
(void) is;
assert(is == 1);
/* TODO(mkm): make regexp use user data API */
p = v7_get_own_property2(v7, v, "", 0, _V7_PROPERTY_HIDDEN);
assert(p != NULL);
return (struct v7_regexp *) get_ptr(p->value);
}
int v7_is_regexp(struct v7 *v7, val_t v) {
struct v7_property *p;
if (!v7_is_generic_object(v)) return 0;
/* TODO(mkm): make regexp use user data API */
p = v7_get_own_property2(v7, v, "", 0, _V7_PROPERTY_HIDDEN);
if (p == NULL) return 0;
return (p->value & V7_TAG_MASK) == V7_TAG_REGEXP;
}
V7_PRIVATE size_t
get_regexp_flags_str(struct v7 *v7, struct v7_regexp *rp, char *buf) {
int re_flags = slre_get_flags(rp->compiled_regexp);
size_t n = 0;
(void) v7;
if (re_flags & SLRE_FLAG_G) buf[n++] = 'g';
if (re_flags & SLRE_FLAG_I) buf[n++] = 'i';
if (re_flags & SLRE_FLAG_M) buf[n++] = 'm';
assert(n <= _V7_REGEXP_MAX_FLAGS_LEN);
return n;
}
#else /* V7_ENABLE__RegExp */
/*
* Dummy implementation when RegExp support is disabled: just return 0
*/
int v7_is_regexp(struct v7 *v7, val_t v) {
(void) v7;
(void) v;
return 0;
}
#endif /* V7_ENABLE__RegExp */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/exceptions.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "common/str_util.h" */
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/exceptions.h" */
/* Amalgamated: #include "v7/src/array.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* Amalgamated: #include "v7/src/eval.h" */
/* Amalgamated: #include "v7/src/object.h" */
enum v7_err v7_throw(struct v7 *v7, v7_val_t val) {
v7->vals.thrown_error = val;
v7->is_thrown = 1;
return V7_EXEC_EXCEPTION;
}
void v7_clear_thrown_value(struct v7 *v7) {
v7->vals.thrown_error = V7_UNDEFINED;
v7->is_thrown = 0;
}
enum v7_err v7_throwf(struct v7 *v7, const char *typ, const char *err_fmt,
...) {
/* TODO(dfrank) : get rid of v7->error_msg, allocate mem right here */
enum v7_err rcode = V7_OK;
va_list ap;
val_t e = V7_UNDEFINED;
va_start(ap, err_fmt);
c_vsnprintf(v7->error_msg, sizeof(v7->error_msg), err_fmt, ap);
va_end(ap);
v7_own(v7, &e);
rcode = create_exception(v7, typ, v7->error_msg, &e);
if (rcode != V7_OK) {
goto clean;
}
rcode = v7_throw(v7, e);
clean:
v7_disown(v7, &e);
return rcode;
}
enum v7_err v7_rethrow(struct v7 *v7) {
assert(v7->is_thrown);
#ifdef NDEBUG
(void) v7;
#endif
return V7_EXEC_EXCEPTION;
}
v7_val_t v7_get_thrown_value(struct v7 *v7, uint8_t *is_thrown) {
if (is_thrown != NULL) {
*is_thrown = v7->is_thrown;
}
return v7->vals.thrown_error;
}
/*
* Create an instance of the exception with type `typ` (see `TYPE_ERROR`,
* `SYNTAX_ERROR`, etc) and message `msg`.
*/
V7_PRIVATE enum v7_err create_exception(struct v7 *v7, const char *typ,
const char *msg, val_t *res) {
enum v7_err rcode = V7_OK;
uint8_t saved_creating_exception = v7->creating_exception;
val_t ctor_args = V7_UNDEFINED, ctor_func = V7_UNDEFINED;
#if 0
assert(v7_is_undefined(v7->vals.thrown_error));
#endif
*res = V7_UNDEFINED;
v7_own(v7, &ctor_args);
v7_own(v7, &ctor_func);
if (v7->creating_exception) {
#ifndef NO_LIBC
fprintf(stderr, "Exception creation throws an exception %s: %s\n", typ,
msg);
#endif
} else {
v7->creating_exception = 1;
/* Prepare arguments for the `Error` constructor */
ctor_args = v7_mk_dense_array(v7);
v7_array_set(v7, ctor_args, 0, v7_mk_string(v7, msg, strlen(msg), 1));
/* Get constructor for the given error `typ` */
ctor_func = v7_get(v7, v7->vals.global_object, typ, ~0);
if (v7_is_undefined(ctor_func)) {
fprintf(stderr, "cannot find exception %s\n", typ);
}
/* Create an error object, with prototype from constructor function */
*res = mk_object(v7, v7_get(v7, ctor_func, "prototype", 9));
/*
* Finally, call the error constructor, passing an error object as `this`
*/
V7_TRY(b_apply(v7, ctor_func, *res, ctor_args, 0, NULL));
}
clean:
v7->creating_exception = saved_creating_exception;
v7_disown(v7, &ctor_func);
v7_disown(v7, &ctor_args);
return rcode;
}
#ifdef V7_MODULE_LINES
#line 1 "v7/src/conversion.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "common/cs_strtod.h" */
/* Amalgamated: #include "common/str_util.h" */
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* Amalgamated: #include "v7/src/util.h" */
/* Amalgamated: #include "v7/src/primitive.h" */
/* Amalgamated: #include "v7/src/function.h" */
/* Amalgamated: #include "v7/src/conversion.h" */
/* Amalgamated: #include "v7/src/exceptions.h" */
/* Amalgamated: #include "v7/src/eval.h" */
/* Amalgamated: #include "v7/src/gc.h" */
/* Amalgamated: #include "v7/src/array.h" */
/* Amalgamated: #include "v7/src/object.h" */
static void save_val(struct v7 *v7, const char *str, size_t str_len,
val_t *dst_v, char *dst, size_t dst_size, int wanted_len,
size_t *res_wanted_len) {
if (dst_v != NULL) {
*dst_v = v7_mk_string(v7, str, str_len, 1);
}
if (dst != NULL && dst_size > 0) {
size_t size = str_len + 1 /*null-term*/;
if (size > dst_size) {
size = dst_size;
}
memcpy(dst, str, size);
/* make sure we have null-term */
dst[dst_size - 1] = '\0';
}
if (res_wanted_len != NULL) {
*res_wanted_len = (wanted_len >= 0) ? (size_t) wanted_len : str_len;
}
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err primitive_to_str(struct v7 *v7, val_t v, val_t *res,
char *buf, size_t buf_size,
size_t *res_len) {
enum v7_err rcode = V7_OK;
char tmp_buf[25];
double num;
size_t wanted_len;
assert(!v7_is_object(v));
memset(tmp_buf, 0x00, sizeof(tmp_buf));
v7_own(v7, &v);
switch (val_type(v7, v)) {
case V7_TYPE_STRING: {
/* if `res` provided, set it to source value */
if (res != NULL) {
*res = v;
}
/* if buf provided, copy string data there */
if (buf != NULL && buf_size > 0) {
size_t size;
const char *str = v7_get_string(v7, &v, &size);
size += 1 /*null-term*/;
if (size > buf_size) {
size = buf_size;
}
memcpy(buf, str, size);
/* make sure we have a null-term */
buf[buf_size - 1] = '\0';
}
if (res_len != NULL) {
v7_get_string(v7, &v, res_len);
}
goto clean;
}
case V7_TYPE_NULL:
strncpy(tmp_buf, "null", sizeof(tmp_buf) - 1);
save_val(v7, tmp_buf, strlen(tmp_buf), res, buf, buf_size, -1, res_len);
goto clean;
case V7_TYPE_UNDEFINED:
strncpy(tmp_buf, "undefined", sizeof(tmp_buf) - 1);
save_val(v7, tmp_buf, strlen(tmp_buf), res, buf, buf_size, -1, res_len);
goto clean;
case V7_TYPE_BOOLEAN:
if (v7_get_bool(v7, v)) {
strncpy(tmp_buf, "true", sizeof(tmp_buf) - 1);
save_val(v7, tmp_buf, strlen(tmp_buf), res, buf, buf_size, -1, res_len);
goto clean;
} else {
strncpy(tmp_buf, "false", sizeof(tmp_buf) - 1);
save_val(v7, tmp_buf, strlen(tmp_buf), res, buf, buf_size, -1, res_len);
goto clean;
}
case V7_TYPE_NUMBER:
if (v == V7_TAG_NAN) {
strncpy(tmp_buf, "NaN", sizeof(tmp_buf) - 1);
save_val(v7, tmp_buf, strlen(tmp_buf), res, buf, buf_size, -1, res_len);
goto clean;
}
num = v7_get_double(v7, v);
if (isinf(num)) {
if (num < 0.0) {
strncpy(tmp_buf, "-Infinity", sizeof(tmp_buf) - 1);
} else {
strncpy(tmp_buf, "Infinity", sizeof(tmp_buf) - 1);
}
save_val(v7, tmp_buf, strlen(tmp_buf), res, buf, buf_size, -1, res_len);
goto clean;
}
{
const char *fmt = num > 1e10 ? "%.21g" : "%.10g";
wanted_len = snprintf(tmp_buf, sizeof(tmp_buf), fmt, num);
save_val(v7, tmp_buf, strlen(tmp_buf), res, buf, buf_size, wanted_len,
res_len);
goto clean;
}
case V7_TYPE_CFUNCTION:
#ifdef V7_UNIT_TEST
wanted_len = c_snprintf(tmp_buf, sizeof(tmp_buf), "cfunc_xxxxxx");
save_val(v7, tmp_buf, strlen(tmp_buf), res, buf, buf_size, wanted_len,
res_len);
goto clean;
#else
wanted_len = c_snprintf(tmp_buf, sizeof(tmp_buf), "cfunc_%p", get_ptr(v));
save_val(v7, tmp_buf, strlen(tmp_buf), res, buf, buf_size, wanted_len,
res_len);
goto clean;
#endif
case V7_TYPE_FOREIGN:
wanted_len = c_snprintf(tmp_buf, sizeof(tmp_buf), "[foreign_%p]",
v7_get_ptr(v7, v));
save_val(v7, tmp_buf, strlen(tmp_buf), res, buf, buf_size, wanted_len,
res_len);
goto clean;
default:
abort();
}
clean:
v7_disown(v7, &v);
return rcode;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err primitive_to_number(struct v7 *v7, val_t v, val_t *res) {
enum v7_err rcode = V7_OK;
assert(!v7_is_object(v));
*res = v;
if (v7_is_number(*res)) {
goto clean;
}
if (v7_is_undefined(*res)) {
*res = V7_TAG_NAN;
goto clean;
}
if (v7_is_null(*res)) {
*res = v7_mk_number(v7, 0.0);
goto clean;
}
if (v7_is_boolean(*res)) {
*res = v7_mk_number(v7, !!v7_get_bool(v7, v));
goto clean;
}
if (is_cfunction_lite(*res)) {
*res = v7_mk_number(v7, 0.0);
goto clean;
}
if (v7_is_string(*res)) {
double d;
size_t n;
char *e, *s = (char *) v7_get_string(v7, res, &n);
if (n != 0) {
d = cs_strtod(s, &e);
if (e - n != s) {
d = NAN;
}
} else {
/* empty string: convert to 0 */
d = 0.0;
}
*res = v7_mk_number(v7, d);
goto clean;
}
assert(0);
clean:
return rcode;
}
WARN_UNUSED_RESULT
enum v7_err to_primitive(struct v7 *v7, val_t v, enum to_primitive_hint hint,
val_t *res) {
enum v7_err rcode = V7_OK;
enum v7_err (*p_func)(struct v7 *v7, val_t v, val_t *res);
v7_own(v7, &v);
*res = v;
/*
* If given value is an object, try to convert it to string by calling first
* preferred function (`toString()` or `valueOf()`, depending on the `hint`
* argument)
*/
if (v7_is_object(*res)) {
/* Handle special case for Date object */
if (hint == V7_TO_PRIMITIVE_HINT_AUTO) {
hint = (v7_get_proto(v7, *res) == v7->vals.date_prototype)
? V7_TO_PRIMITIVE_HINT_STRING
: V7_TO_PRIMITIVE_HINT_NUMBER;
}
p_func =
(hint == V7_TO_PRIMITIVE_HINT_NUMBER) ? obj_value_of : obj_to_string;
rcode = p_func(v7, *res, res);
if (rcode != V7_OK) {
goto clean;
}
/*
* If returned value is still an object, get original argument value
*/
if (v7_is_object(*res)) {
*res = v;
}
}
/*
* If the value is still an object, try to call second function (`valueOf()`
* or `toString()`)
*/
if (v7_is_object(*res)) {
p_func =
(hint == V7_TO_PRIMITIVE_HINT_NUMBER) ? obj_to_string : obj_value_of;
rcode = p_func(v7, *res, res);
if (rcode != V7_OK) {
goto clean;
}
}
/*
* If the value is still an object, then throw.
*/
if (v7_is_object(*res)) {
rcode =
v7_throwf(v7, TYPE_ERROR, "Cannot convert object to primitive value");
goto clean;
}
clean:
v7_disown(v7, &v);
return rcode;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err to_string(struct v7 *v7, val_t v, val_t *res, char *buf,
size_t buf_size, size_t *res_len) {
enum v7_err rcode = V7_OK;
v7_own(v7, &v);
/*
* Convert value to primitive if needed, calling `toString()` first
*/
V7_TRY(to_primitive(v7, v, V7_TO_PRIMITIVE_HINT_STRING, &v));
/*
* Now, we're guaranteed to have a primitive here. Convert it to string.
*/
V7_TRY(primitive_to_str(v7, v, res, buf, buf_size, res_len));
clean:
v7_disown(v7, &v);
return rcode;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err to_number_v(struct v7 *v7, val_t v, val_t *res) {
enum v7_err rcode = V7_OK;
*res = v;
/*
* Convert value to primitive if needed, calling `valueOf()` first
*/
rcode = to_primitive(v7, *res, V7_TO_PRIMITIVE_HINT_NUMBER, res);
if (rcode != V7_OK) {
goto clean;
}
/*
* Now, we're guaranteed to have a primitive here. Convert it to number.
*/
rcode = primitive_to_number(v7, *res, res);
if (rcode != V7_OK) {
goto clean;
}
clean:
return rcode;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err to_long(struct v7 *v7, val_t v, long default_value,
long *res) {
enum v7_err rcode = V7_OK;
double d;
/* if value is `undefined`, just return `default_value` */
if (v7_is_undefined(v)) {
*res = default_value;
goto clean;
}
/* Try to convert value to number */
rcode = to_number_v(v7, v, &v);
if (rcode != V7_OK) {
goto clean;
}
/*
* Conversion to number succeeded, so, convert it to long
*/
d = v7_get_double(v7, v);
/* We want to return LONG_MAX if d is positive Inf, thus d < 0 check */
if (isnan(d) || (isinf(d) && d < 0)) {
*res = 0;
goto clean;
} else if (d > LONG_MAX) {
*res = LONG_MAX;
goto clean;
}
*res = (long) d;
goto clean;
clean:
return rcode;
}
V7_PRIVATE enum v7_err obj_value_of(struct v7 *v7, val_t v, val_t *res) {
enum v7_err rcode = V7_OK;
val_t func_valueOf = V7_UNDEFINED;
v7_own(v7, &func_valueOf);
v7_own(v7, &v);
/*
* TODO(dfrank): use `assert(v7_is_object(v))` instead, like `obj_to_string()`
* does, and fix all callers to ensure it's an object before calling.
*
* Or, conversely, make `obj_to_string()` to accept objects.
*/
if (!v7_is_object(v)) {
*res = v;
goto clean;
}
V7_TRY(v7_get_throwing(v7, v, "valueOf", 7, &func_valueOf));
if (v7_is_callable(v7, func_valueOf)) {
V7_TRY(b_apply(v7, func_valueOf, v, V7_UNDEFINED, 0, res));
}
clean:
if (rcode != V7_OK) {
*res = v;
}
v7_disown(v7, &v);
v7_disown(v7, &func_valueOf);
return rcode;
}
/*
* Caller should ensure that `v` is an object
*/
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err obj_to_string(struct v7 *v7, val_t v, val_t *res) {
enum v7_err rcode = V7_OK;
val_t to_string_func = V7_UNDEFINED;
/* Caller should ensure that `v` is an object */
assert(v7_is_object(v));
v7_own(v7, &to_string_func);
v7_own(v7, &v);
/*
* If `toString` is callable, then call it; otherwise, just return source
* value
*/
V7_TRY(v7_get_throwing(v7, v, "toString", 8, &to_string_func));
if (v7_is_callable(v7, to_string_func)) {
V7_TRY(b_apply(v7, to_string_func, v, V7_UNDEFINED, 0, res));
} else {
*res = v;
}
clean:
v7_disown(v7, &v);
v7_disown(v7, &to_string_func);
return rcode;
}
static const char *hex_digits = "0123456789abcdef";
static char *append_hex(char *buf, char *limit, uint8_t c) {
if (buf < limit) *buf++ = 'u';
if (buf < limit) *buf++ = '0';
if (buf < limit) *buf++ = '0';
if (buf < limit) *buf++ = hex_digits[(int) ((c >> 4) % 0xf)];
if (buf < limit) *buf++ = hex_digits[(int) (c & 0xf)];
return buf;
}
/*
* Appends quoted s to buf. Any double quote contained in s will be escaped.
* Returns the number of characters that would have been added,
* like snprintf.
* If size is zero it doesn't output anything but keeps counting.
*/
static int snquote(char *buf, size_t size, const char *s, size_t len) {
char *limit = buf + size;
const char *end;
/*
* String single character escape sequence:
* http://www.ecma-international.org/ecma-262/6.0/index.html#table-34
*
* 0x8 -> \b
* 0x9 -> \t
* 0xa -> \n
* 0xb -> \v
* 0xc -> \f
* 0xd -> \r
*/
const char *specials = "btnvfr";
size_t i = 0;
i++;
if (buf < limit) *buf++ = '"';
for (end = s + len; s < end; s++) {
if (*s == '"' || *s == '\\') {
i++;
if (buf < limit) *buf++ = '\\';
} else if (*s >= '\b' && *s <= '\r') {
i += 2;
if (buf < limit) *buf++ = '\\';
if (buf < limit) *buf++ = specials[*s - '\b'];
continue;
} else if ((unsigned char) *s < '\b' || (*s > '\r' && *s < ' ')) {
i += 6 /* \uXXXX */;
if (buf < limit) *buf++ = '\\';
buf = append_hex(buf, limit, (uint8_t) *s);
continue;
}
i++;
if (buf < limit) *buf++ = *s;
}
i++;
if (buf < limit) *buf++ = '"';
if (buf < limit) {
*buf = '\0';
} else if (size != 0) {
/*
* There is no room for the NULL char, but the size wasn't zero, so we can
* safely put NULL in the previous byte
*/
*(buf - 1) = '\0';
}
return i;
}
/*
* Returns whether the value of given type should be skipped when generating
* JSON output
*/
static int should_skip_for_json(enum v7_type type) {
int ret;
switch (type) {
/* All permitted values */
case V7_TYPE_NULL:
case V7_TYPE_BOOLEAN:
case V7_TYPE_BOOLEAN_OBJECT:
case V7_TYPE_NUMBER:
case V7_TYPE_NUMBER_OBJECT:
case V7_TYPE_STRING:
case V7_TYPE_STRING_OBJECT:
case V7_TYPE_GENERIC_OBJECT:
case V7_TYPE_ARRAY_OBJECT:
case V7_TYPE_DATE_OBJECT:
case V7_TYPE_REGEXP_OBJECT:
case V7_TYPE_ERROR_OBJECT:
ret = 0;
break;
default:
ret = 1;
break;
}
return ret;
}
WARN_UNUSED_RESULT
V7_PRIVATE enum v7_err to_json_or_debug(struct v7 *v7, val_t v, char *buf,
size_t size, size_t *res_len,
uint8_t is_debug) {
val_t el;
char *vp;
enum v7_err rcode = V7_OK;
size_t len = 0;
struct gc_tmp_frame tf = new_tmp_frame(v7);
tmp_stack_push(&tf, &v);
tmp_stack_push(&tf, &el);
/*
* TODO(dfrank) : also push all `v7_val_t`s that are declared below
*/
if (size > 0) *buf = '\0';
if (!is_debug && should_skip_for_json(val_type(v7, v))) {
goto clean;
}
for (vp = v7->json_visited_stack.buf;
vp < v7->json_visited_stack.buf + v7->json_visited_stack.len;
vp += sizeof(val_t)) {
if (*(val_t *) vp == v) {
strncpy(buf, "[Circular]", size);
len = 10;
goto clean;
}
}
switch (val_type(v7, v)) {
case V7_TYPE_NULL:
case V7_TYPE_BOOLEAN:
case V7_TYPE_NUMBER:
case V7_TYPE_UNDEFINED:
case V7_TYPE_CFUNCTION:
case V7_TYPE_FOREIGN:
/* For those types, regular `primitive_to_str()` works */
V7_TRY(primitive_to_str(v7, v, NULL, buf, size, &len));
goto clean;
case V7_TYPE_STRING: {
/*
* For strings we can't just use `primitive_to_str()`, because we need
* quoted value
*/
size_t n;
const char *str = v7_get_string(v7, &v, &n);
len = snquote(buf, size, str, n);
goto clean;
}
case V7_TYPE_DATE_OBJECT: {
v7_val_t func = V7_UNDEFINED, val = V7_UNDEFINED;
V7_TRY(v7_get_throwing(v7, v, "toString", 8, &func));
#if V7_ENABLE__Date__toJSON
if (!is_debug) {
V7_TRY(v7_get_throwing(v7, v, "toJSON", 6, &func));
}
#endif
V7_TRY(b_apply(v7, func, v, V7_UNDEFINED, 0, &val));
V7_TRY(to_json_or_debug(v7, val, buf, size, &len, is_debug));
goto clean;
}
case V7_TYPE_GENERIC_OBJECT:
case V7_TYPE_BOOLEAN_OBJECT:
case V7_TYPE_STRING_OBJECT:
case V7_TYPE_NUMBER_OBJECT:
case V7_TYPE_REGEXP_OBJECT:
case V7_TYPE_ERROR_OBJECT: {
/* TODO(imax): make it return the desired size of the buffer */
char *b = buf;
v7_val_t name = V7_UNDEFINED, val = V7_UNDEFINED;
v7_prop_attr_t attrs = 0;
const char *pname;
size_t nlen;
int ok = 0;
struct prop_iter_ctx ctx;
memset(&ctx, 0, sizeof(ctx));
mbuf_append(&v7->json_visited_stack, (char *) &v, sizeof(v));
b += c_snprintf(b, BUF_LEFT(size, b - buf), "{");
V7_TRY2(init_prop_iter_ctx(v7, v, 1 /*proxy-transparent*/, &ctx),
clean_iter);
while (1) {
size_t n;
const char *s;
V7_TRY2(next_prop(v7, &ctx, &name, &val, &attrs, &ok), clean_iter);
if (!ok) {
break;
} else if (attrs & (_V7_PROPERTY_HIDDEN | V7_PROPERTY_NON_ENUMERABLE)) {
continue;
}
pname = v7_get_string(v7, &name, &nlen);
V7_TRY(v7_get_throwing(v7, v, pname, nlen, &val));
if (!is_debug && should_skip_for_json(val_type(v7, val))) {
continue;
}
if (b - buf != 1) { /* Not the first property to be printed */
b += c_snprintf(b, BUF_LEFT(size, b - buf), ",");
}
s = v7_get_string(v7, &name, &n);
b += c_snprintf(b, BUF_LEFT(size, b - buf), "\"%.*s\":", (int) n, s);
{
size_t tmp = 0;
V7_TRY2(to_json_or_debug(v7, val, b, BUF_LEFT(size, b - buf), &tmp,
is_debug),
clean_iter);
b += tmp;
}
}
b += c_snprintf(b, BUF_LEFT(size, b - buf), "}");
v7->json_visited_stack.len -= sizeof(v);
clean_iter:
v7_destruct_prop_iter_ctx(v7, &ctx);
len = b - buf;
goto clean;
}
case V7_TYPE_ARRAY_OBJECT: {
int has;
char *b = buf;
size_t i, alen = v7_array_length(v7, v);
mbuf_append(&v7->json_visited_stack, (char *) &v, sizeof(v));
b += c_snprintf(b, BUF_LEFT(size, b - buf), "[");
for (i = 0; i < alen; i++) {
el = v7_array_get2(v7, v, i, &has);
if (has) {
size_t tmp = 0;
if (!is_debug && should_skip_for_json(val_type(v7, el))) {
b += c_snprintf(b, BUF_LEFT(size, b - buf), "null");
} else {
V7_TRY(to_json_or_debug(v7, el, b, BUF_LEFT(size, b - buf), &tmp,
is_debug));
}
b += tmp;
}
if (i != alen - 1) {
b += c_snprintf(b, BUF_LEFT(size, b - buf), ",");
}
}
b += c_snprintf(b, BUF_LEFT(size, b - buf), "]");
v7->json_visited_stack.len -= sizeof(v);
len = b - buf;
goto clean;
}
case V7_TYPE_CFUNCTION_OBJECT:
V7_TRY(obj_value_of(v7, v, &v));
len = c_snprintf(buf, size, "Function cfunc_%p", get_ptr(v));
goto clean;
case V7_TYPE_FUNCTION_OBJECT:
V7_TRY(to_string(v7, v, NULL, buf, size, &len));
goto clean;
case V7_TYPE_MAX_OBJECT_TYPE:
case V7_NUM_TYPES:
abort();
}
abort();
len = 0; /* for compilers that don't know about abort() */
goto clean;
clean:
if (rcode != V7_OK) {
len = 0;
}
if (res_len != NULL) {
*res_len = len;
}
tmp_frame_cleanup(&tf);
return rcode;
}
WARN_UNUSED_RESULT
V7_PRIVATE val_t to_boolean_v(struct v7 *v7, val_t v) {
size_t len;
int is_truthy;
is_truthy = ((v7_is_boolean(v) && v7_get_bool(v7, v)) ||
(v7_is_number(v) && v7_get_double(v7, v) != 0.0) ||
(v7_is_string(v) && v7_get_string(v7, &v, &len) && len > 0) ||
(v7_is_object(v))) &&
v != V7_TAG_NAN;
return v7_mk_boolean(v7, is_truthy);
}
/*
* v7_stringify allocates a new buffer if value representation doesn't fit into
* buf. Caller is responsible for freeing that buffer.
*/
char *v7_stringify(struct v7 *v7, val_t v, char *buf, size_t size,
enum v7_stringify_mode mode) {
enum v7_err rcode = V7_OK;
uint8_t saved_is_thrown = 0;
val_t saved_thrown = v7_get_thrown_value(v7, &saved_is_thrown);
char *ret = NULL;
rcode = v7_stringify_throwing(v7, v, buf, size, mode, &ret);
if (rcode != V7_OK) {
rcode = V7_OK;
if (saved_is_thrown) {
rcode = v7_throw(v7, saved_thrown);
} else {
v7_clear_thrown_value(v7);
}
buf[0] = '\0';
ret = buf;
}
return ret;
}
enum v7_err v7_stringify_throwing(struct v7 *v7, val_t v, char *buf,
size_t size, enum v7_stringify_mode mode,
char **res) {
enum v7_err rcode = V7_OK;
char *p = buf;
size_t len;
switch (mode) {
case V7_STRINGIFY_DEFAULT:
V7_TRY(to_string(v7, v, NULL, buf, size, &len));
break;
case V7_STRINGIFY_JSON:
V7_TRY(to_json_or_debug(v7, v, buf, size, &len, 0));
break;
case V7_STRINGIFY_DEBUG:
V7_TRY(to_json_or_debug(v7, v, buf, size, &len, 1));
break;
}
/* fit null terminating byte */
if (len >= size) {
/* Buffer is not large enough. Allocate a bigger one */
p = (char *) malloc(len + 1);
V7_TRY(v7_stringify_throwing(v7, v, p, len + 1, mode, res));
assert(*res == p);
goto clean;
} else {
*res = p;
goto clean;
}
clean:
/*
* If we're going to throw, and we allocated a buffer, then free it.
* But if we don't throw, then the caller will free it.
*/
if (rcode != V7_OK && p != buf) {
free(p);
}
return rcode;
}
int v7_is_truthy(struct v7 *v7, val_t v) {
return v7_get_bool(v7, to_boolean_v(v7, v));
}
#ifdef V7_MODULE_LINES
#line 1 "v7/src/shdata.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/shdata.h" */
#if !V7_DISABLE_FILENAMES && !V7_DISABLE_LINE_NUMBERS
V7_PRIVATE struct shdata *shdata_create(const void *payload, size_t size) {
struct shdata *ret =
(struct shdata *) calloc(1, sizeof(struct shdata) + size);
shdata_retain(ret);
if (payload != NULL) {
memcpy((char *) shdata_get_payload(ret), (char *) payload, size);
}
return ret;
}
V7_PRIVATE struct shdata *shdata_create_from_string(const char *src) {
return shdata_create(src, strlen(src) + 1 /*null-term*/);
}
V7_PRIVATE void shdata_retain(struct shdata *p) {
p->refcnt++;
assert(p->refcnt > 0);
}
V7_PRIVATE void shdata_release(struct shdata *p) {
assert(p->refcnt > 0);
p->refcnt--;
if (p->refcnt == 0) {
free(p);
}
}
V7_PRIVATE void *shdata_get_payload(struct shdata *p) {
return (char *) p + sizeof(*p);
}
#endif
#ifdef V7_MODULE_LINES
#line 1 "v7/src/gc.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/bcode.h" */
/* Amalgamated: #include "v7/src/varint.h" */
/* Amalgamated: #include "v7/src/gc.h" */
/* Amalgamated: #include "v7/src/freeze.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* Amalgamated: #include "v7/src/function.h" */
/* Amalgamated: #include "v7/src/primitive.h" */
/* Amalgamated: #include "v7/src/object.h" */
/* Amalgamated: #include "v7/src/string.h" */
/* Amalgamated: #include "v7/src/util.h" */
/* Amalgamated: #include "v7/src/primitive.h" */
/* Amalgamated: #include "v7/src/heapusage.h" */
#include
#ifdef V7_STACK_GUARD_MIN_SIZE
void *v7_sp_limit = NULL;
#endif
void gc_mark_string(struct v7 *, val_t *);
static struct gc_block *gc_new_block(struct gc_arena *a, size_t size);
static void gc_free_block(struct gc_block *b);
static void gc_mark_mbuf_pt(struct v7 *v7, const struct mbuf *mbuf);
static void gc_mark_mbuf_val(struct v7 *v7, const struct mbuf *mbuf);
static void gc_mark_vec_val(struct v7 *v7, const struct v7_vec *vec);
V7_PRIVATE struct v7_generic_object *new_generic_object(struct v7 *v7) {
return (struct v7_generic_object *) gc_alloc_cell(v7,
&v7->generic_object_arena);
}
V7_PRIVATE struct v7_property *new_property(struct v7 *v7) {
return (struct v7_property *) gc_alloc_cell(v7, &v7->property_arena);
}
V7_PRIVATE struct v7_js_function *new_function(struct v7 *v7) {
return (struct v7_js_function *) gc_alloc_cell(v7, &v7->function_arena);
}
V7_PRIVATE struct gc_tmp_frame new_tmp_frame(struct v7 *v7) {
struct gc_tmp_frame frame;
frame.v7 = v7;
frame.pos = v7->tmp_stack.len;
return frame;
}
V7_PRIVATE void tmp_frame_cleanup(struct gc_tmp_frame *tf) {
tf->v7->tmp_stack.len = tf->pos;
}
/*
* TODO(mkm): perhaps it's safer to keep val_t in the temporary
* roots stack, instead of keeping val_t*, in order to be better
* able to debug the relocating GC.
*/
V7_PRIVATE void tmp_stack_push(struct gc_tmp_frame *tf, val_t *vp) {
mbuf_append(&tf->v7->tmp_stack, (char *) &vp, sizeof(val_t *));
}
/* Initializes a new arena. */
V7_PRIVATE void gc_arena_init(struct gc_arena *a, size_t cell_size,
size_t initial_size, size_t size_increment,
const char *name) {
assert(cell_size >= sizeof(uintptr_t));
memset(a, 0, sizeof(*a));
a->cell_size = cell_size;
a->name = name;
a->size_increment = size_increment;
a->blocks = gc_new_block(a, initial_size);
}
V7_PRIVATE void gc_arena_destroy(struct v7 *v7, struct gc_arena *a) {
struct gc_block *b;
if (a->blocks != NULL) {
gc_sweep(v7, a, 0);
for (b = a->blocks; b != NULL;) {
struct gc_block *tmp;
tmp = b;
b = b->next;
gc_free_block(tmp);
}
}
}
static void gc_free_block(struct gc_block *b) {
free(b->base);
free(b);
}
static struct gc_block *gc_new_block(struct gc_arena *a, size_t size) {
struct gc_cell *cur;
struct gc_block *b;
heapusage_dont_count(1);
b = (struct gc_block *) calloc(1, sizeof(*b));
heapusage_dont_count(0);
if (b == NULL) abort();
b->size = size;
heapusage_dont_count(1);
b->base = (struct gc_cell *) calloc(a->cell_size, b->size);
heapusage_dont_count(0);
if (b->base == NULL) abort();
for (cur = GC_CELL_OP(a, b->base, +, 0);
cur < GC_CELL_OP(a, b->base, +, b->size);
cur = GC_CELL_OP(a, cur, +, 1)) {
cur->head.link = a->free;
a->free = cur;
}
return b;
}
V7_PRIVATE void *gc_alloc_cell(struct v7 *v7, struct gc_arena *a) {
#ifdef V7_MALLOC_GC
struct gc_cell *r;
maybe_gc(v7);
heapusage_dont_count(1);
r = (struct gc_cell *) calloc(1, a->cell_size);
heapusage_dont_count(0);
mbuf_append(&v7->malloc_trace, &r, sizeof(r));
return r;
#else
struct gc_cell *r;
if (a->free == NULL) {
if (!maybe_gc(v7)) {
/* GC is inhibited, so, schedule invocation for later */
v7->need_gc = 1;
}
if (a->free == NULL) {
struct gc_block *b = gc_new_block(a, a->size_increment);
b->next = a->blocks;
a->blocks = b;
}
}
r = a->free;
UNMARK(r);
a->free = r->head.link;
#if V7_ENABLE__Memory__stats
a->allocations++;
a->alive++;
#endif
/*
* TODO(mkm): minor opt possible since most of the fields
* are overwritten downstream, but not worth the yak shave time
* when fields are added to GC-able structures */
memset(r, 0, a->cell_size);
return (void *) r;
#endif
}
#ifdef V7_MALLOC_GC
/*
* Scans trough the memory blocks registered in the malloc trace.
* Free the unmarked ones and reset the mark on the rest.
*/
void gc_sweep_malloc(struct v7 *v7) {
struct gc_cell **cur;
for (cur = (struct gc_cell **) v7->malloc_trace.buf;
cur < (struct gc_cell **) (v7->malloc_trace.buf + v7->malloc_trace.len);
cur++) {
if (*cur == NULL) continue;
if (MARKED(*cur)) {
UNMARK(*cur);
} else {
free(*cur);
/* TODO(mkm): compact malloc trace buffer */
*cur = NULL;
}
}
}
#endif
/*
* Scans the arena and add all unmarked cells to the free list.
*
* Empty blocks get deallocated. The head of the free list will contais cells
* from the last (oldest) block. Cells will thus be allocated in block order.
*/
void gc_sweep(struct v7 *v7, struct gc_arena *a, size_t start) {
struct gc_block *b;
struct gc_cell *cur;
struct gc_block **prevp = &a->blocks;
#if V7_ENABLE__Memory__stats
a->alive = 0;
#endif
/*
* Before we sweep, we should mark all free cells in a way that is
* distinguishable from marked used cells.
*/
{
struct gc_cell *next;
for (cur = a->free; cur != NULL; cur = next) {
next = cur->head.link;
MARK_FREE(cur);
}
}
/*
* We'll rebuild the whole `free` list, so initially we just reset it
*/
a->free = NULL;
for (b = a->blocks; b != NULL;) {
size_t freed_in_block = 0;
/*
* if it turns out that this block is 100% garbage
* we can release the whole block, but the addition
* of it's cells to the free list has to be undone.
*/
struct gc_cell *prev_free = a->free;
for (cur = GC_CELL_OP(a, b->base, +, start);
cur < GC_CELL_OP(a, b->base, +, b->size);
cur = GC_CELL_OP(a, cur, +, 1)) {
if (MARKED(cur)) {
/* The cell is used and marked */
UNMARK(cur);
#if V7_ENABLE__Memory__stats
a->alive++;
#endif
} else {
/*
* The cell is either:
* - free
* - garbage that's about to be freed
*/
if (MARKED_FREE(cur)) {
/* The cell is free, so, just unmark it */
UNMARK_FREE(cur);
} else {
/*
* The cell is used and should be freed: call the destructor and
* reset the memory
*/
if (a->destructor != NULL) {
a->destructor(v7, cur);
}
memset(cur, 0, a->cell_size);
}
/* Add this cell to the `free` list */
cur->head.link = a->free;
a->free = cur;
freed_in_block++;
#if V7_ENABLE__Memory__stats
a->garbage++;
#endif
}
}
/*
* don't free the initial block, which is at the tail
* because it has a special size aimed at reducing waste
* and simplifying initial startup. TODO(mkm): improve
* */
if (b->next != NULL && freed_in_block == b->size) {
*prevp = b->next;
gc_free_block(b);
b = *prevp;
a->free = prev_free;
} else {
prevp = &b->next;
b = b->next;
}
}
}
/*
* dense arrays contain only one property pointing to an mbuf with array values.
*/
V7_PRIVATE void gc_mark_dense_array(struct v7 *v7,
struct v7_generic_object *obj) {
val_t v;
struct mbuf *mbuf;
val_t *vp;
#if 0
/* TODO(mkm): use this when dense array promotion is implemented */
v = obj->properties->value;
#else
v = v7_get(v7, v7_object_to_value(&obj->base), "", 0);
#endif
mbuf = (struct mbuf *) v7_get_ptr(v7, v);
/* function scope pointer is aliased to the object's prototype pointer */
gc_mark(v7, v7_object_to_value(obj_prototype(v7, &obj->base)));
MARK(obj);
if (mbuf == NULL) return;
for (vp = (val_t *) mbuf->buf; (char *) vp < mbuf->buf + mbuf->len; vp++) {
gc_mark(v7, *vp);
gc_mark_string(v7, vp);
}
UNMARK(obj);
}
V7_PRIVATE void gc_mark(struct v7 *v7, val_t v) {
struct v7_object *obj_base;
struct v7_property *prop;
struct v7_property *next;
if (!v7_is_object(v)) {
return;
}
obj_base = get_object_struct(v);
/*
* we ignore objects that are not managed by V7 heap, such as frozen
* objects, especially when on flash.
*/
if (obj_base->attributes & V7_OBJ_OFF_HEAP) {
return;
}
/*
* we treat all object like things like objects but they might be functions,
* gc_gheck_val checks the appropriate arena per actual value type.
*/
if (!gc_check_val(v7, v)) {
abort();
}
if (MARKED(obj_base)) return;
#ifdef V7_FREEZE
if (v7->freeze_file != NULL) {
freeze_obj(v7, v7->freeze_file, v);
}
#endif
if (obj_base->attributes & V7_OBJ_DENSE_ARRAY) {
struct v7_generic_object *obj = get_generic_object_struct(v);
gc_mark_dense_array(v7, obj);
}
/* mark object itself, and its properties */
for ((prop = obj_base->properties), MARK(obj_base); prop != NULL;
prop = next) {
if (prop->attributes & _V7_PROPERTY_OFF_HEAP) {
break;
}
if (!gc_check_ptr(&v7->property_arena, prop)) {
abort();
}
#ifdef V7_FREEZE
if (v7->freeze_file != NULL) {
freeze_prop(v7, v7->freeze_file, prop);
}
#endif
gc_mark_string(v7, &prop->value);
gc_mark_string(v7, &prop->name);
gc_mark(v7, prop->value);
next = prop->next;
MARK(prop);
}
/* mark object's prototype */
gc_mark(v7, v7_get_proto(v7, v));
if (is_js_function(v)) {
struct v7_js_function *func = get_js_function_struct(v);
/* mark function's scope */
gc_mark(v7, v7_object_to_value(&func->scope->base));
if (func->bcode != NULL) {
gc_mark_vec_val(v7, &func->bcode->lit);
}
}
}
#if V7_ENABLE__Memory__stats
V7_PRIVATE size_t gc_arena_size(struct gc_arena *a) {
size_t size = 0;
struct gc_block *b;
for (b = a->blocks; b != NULL; b = b->next) {
size += b->size;
}
return size;
}
/*
* TODO(dfrank): move to core
*/
int v7_heap_stat(struct v7 *v7, enum v7_heap_stat_what what) {
switch (what) {
case V7_HEAP_STAT_HEAP_SIZE:
return gc_arena_size(&v7->generic_object_arena) *
v7->generic_object_arena.cell_size +
gc_arena_size(&v7->function_arena) * v7->function_arena.cell_size +
gc_arena_size(&v7->property_arena) * v7->property_arena.cell_size;
case V7_HEAP_STAT_HEAP_USED:
return v7->generic_object_arena.alive *
v7->generic_object_arena.cell_size +
v7->function_arena.alive * v7->function_arena.cell_size +
v7->property_arena.alive * v7->property_arena.cell_size;
case V7_HEAP_STAT_STRING_HEAP_RESERVED:
return v7->owned_strings.size;
case V7_HEAP_STAT_STRING_HEAP_USED:
return v7->owned_strings.len;
case V7_HEAP_STAT_OBJ_HEAP_MAX:
return gc_arena_size(&v7->generic_object_arena);
case V7_HEAP_STAT_OBJ_HEAP_FREE:
return gc_arena_size(&v7->generic_object_arena) -
v7->generic_object_arena.alive;
case V7_HEAP_STAT_OBJ_HEAP_CELL_SIZE:
return v7->generic_object_arena.cell_size;
case V7_HEAP_STAT_FUNC_HEAP_MAX:
return gc_arena_size(&v7->function_arena);
case V7_HEAP_STAT_FUNC_HEAP_FREE:
return gc_arena_size(&v7->function_arena) - v7->function_arena.alive;
case V7_HEAP_STAT_FUNC_HEAP_CELL_SIZE:
return v7->function_arena.cell_size;
case V7_HEAP_STAT_PROP_HEAP_MAX:
return gc_arena_size(&v7->property_arena);
case V7_HEAP_STAT_PROP_HEAP_FREE:
return gc_arena_size(&v7->property_arena) - v7->property_arena.alive;
case V7_HEAP_STAT_PROP_HEAP_CELL_SIZE:
return v7->property_arena.cell_size;
case V7_HEAP_STAT_FUNC_AST_SIZE:
return v7->function_arena_ast_size;
case V7_HEAP_STAT_BCODE_OPS_SIZE:
return v7->bcode_ops_size;
case V7_HEAP_STAT_BCODE_LIT_TOTAL_SIZE:
return v7->bcode_lit_total_size;
case V7_HEAP_STAT_BCODE_LIT_DESER_SIZE:
return v7->bcode_lit_deser_size;
case V7_HEAP_STAT_FUNC_OWNED:
return v7->owned_values.len / sizeof(val_t *);
case V7_HEAP_STAT_FUNC_OWNED_MAX:
return v7->owned_values.size / sizeof(val_t *);
}
return -1;
}
#endif
V7_PRIVATE void gc_dump_arena_stats(const char *msg, struct gc_arena *a) {
(void) msg;
(void) a;
#ifndef NO_LIBC
#if V7_ENABLE__Memory__stats
if (a->verbose) {
fprintf(stderr, "%s: total allocations %lu, max %lu, alive %lu\n", msg,
(long unsigned int) a->allocations,
(long unsigned int) gc_arena_size(a), (long unsigned int) a->alive);
}
#endif
#endif
}
V7_PRIVATE uint64_t gc_string_val_to_offset(val_t v) {
return (((uint64_t)(uintptr_t) get_ptr(v)) & ~V7_TAG_MASK)
#if !V7_DISABLE_STR_ALLOC_SEQ
& 0xFFFFFFFF
#endif
;
}
V7_PRIVATE val_t gc_string_val_from_offset(uint64_t s) {
return s | V7_TAG_STRING_O;
}
#if !V7_DISABLE_STR_ALLOC_SEQ
static uint16_t next_asn(struct v7 *v7) {
if (v7->gc_next_asn == 0xFFFF) {
/* Wrap around explicitly. */
v7->gc_next_asn = 0;
return 0xFFFF;
}
return v7->gc_next_asn++;
}
uint16_t gc_next_allocation_seqn(struct v7 *v7, const char *str, size_t len) {
uint16_t asn = next_asn(v7);
(void) str;
(void) len;
#ifdef V7_GC_VERBOSE
/*
* ESP SDK printf cannot cope with null strings
* as created by s_concat.
*/
if (str == NULL) {
fprintf(stderr, "GC ASN %d: \n", asn);
} else {
fprintf(stderr, "GC ASN %d: \"%.*s\"\n", asn, (int) len, str);
}
#endif
#ifdef V7_GC_PANIC_ON_ASN
if (asn == (V7_GC_PANIC_ON_ASN)) {
abort();
}
#endif
return asn;
}
int gc_is_valid_allocation_seqn(struct v7 *v7, uint16_t n) {
/*
* This functions attempts to handle integer wraparound in a naive way and
* will give false positives when more than 65536 strings are allocated
* between GC runs.
*/
int r = (n >= v7->gc_min_asn && n < v7->gc_next_asn) ||
(v7->gc_min_asn > v7->gc_next_asn &&
(n >= v7->gc_min_asn || n < v7->gc_next_asn));
if (!r) {
fprintf(stderr, "GC ASN %d is not in [%d,%d)\n", n, v7->gc_min_asn,
v7->gc_next_asn);
}
return r;
}
void gc_check_valid_allocation_seqn(struct v7 *v7, uint16_t n) {
if (!gc_is_valid_allocation_seqn(v7, n)) {
/*
* TODO(dfrank) throw exception if V7_GC_ASN_PANIC is not defined.
*/
#if 0 && !defined(V7_GC_ASN_PANIC)
throw_exception(v7, INTERNAL_ERROR, "Invalid ASN: %d", (int) n);
#else
fprintf(stderr, "Invalid ASN: %d\n", (int) n);
abort();
#endif
}
}
#endif /* V7_DISABLE_STR_ALLOC_SEQ */
/* Mark a string value */
void gc_mark_string(struct v7 *v7, val_t *v) {
val_t h, tmp = 0;
char *s;
/* clang-format off */
/*
* If a value points to an unmarked string we shall:
* 1. save the first 6 bytes of the string
* since we need to be able to distinguish real values from
* the saved first 6 bytes of the string, we need to tag the chunk
* as V7_TAG_STRING_C
* 2. encode value's address (v) into the first 6 bytes of the string.
* 3. put the saved 8 bytes (tag + chunk) back into the value.
* 4. mark the string by putting '\1' in the NUL terminator of the previous
* string chunk.
*
* If a value points to an already marked string we shall:
* (0, <6 bytes of a pointer to a val_t>), hence we have to skip
* the first byte. We tag the value pointer as a V7_TAG_FOREIGN
* so that it won't be followed during recursive mark.
*
* ... the rest is the same
*
* Note: 64-bit pointers can be represented with 48-bits
*/
/* clang-format on */
if ((*v & V7_TAG_MASK) != V7_TAG_STRING_O) {
return;
}
#ifdef V7_FREEZE
if (v7->freeze_file != NULL) {
return;
}
#endif
#ifdef V7_GC_VERBOSE
{
uint16_t asn = (*v >> 32) & 0xFFFF;
size_t size;
fprintf(stderr, "GC marking ASN %d: '%s'\n", asn,
v7_get_string(v7, v, &size));
}
#endif
#if !V7_DISABLE_STR_ALLOC_SEQ
gc_check_valid_allocation_seqn(v7, (*v >> 32) & 0xFFFF);
#endif
s = v7->owned_strings.buf + gc_string_val_to_offset(*v);
assert(s < v7->owned_strings.buf + v7->owned_strings.len);
if (s[-1] == '\0') {
memcpy(&tmp, s, sizeof(tmp) - 2);
tmp |= V7_TAG_STRING_C;
} else {
memcpy(&tmp, s, sizeof(tmp) - 2);
tmp |= V7_TAG_FOREIGN;
}
h = (val_t)(uintptr_t) v;
s[-1] = 1;
memcpy(s, &h, sizeof(h) - 2);
memcpy(v, &tmp, sizeof(tmp));
}
void gc_compact_strings(struct v7 *v7) {
char *p = v7->owned_strings.buf + 1;
uint64_t h, next, head = 1;
int len, llen;
#if !V7_DISABLE_STR_ALLOC_SEQ
v7->gc_min_asn = v7->gc_next_asn;
#endif
while (p < v7->owned_strings.buf + v7->owned_strings.len) {
if (p[-1] == '\1') {
#if !V7_DISABLE_STR_ALLOC_SEQ
/* Not using gc_next_allocation_seqn() as we don't have full string. */
uint16_t asn = next_asn(v7);
#endif
/* relocate and update ptrs */
h = 0;
memcpy(&h, p, sizeof(h) - 2);
/*
* relocate pointers until we find the tail.
* The tail is marked with V7_TAG_STRING_C,
* while val_t link pointers are tagged with V7_TAG_FOREIGN
*/
for (; (h & V7_TAG_MASK) != V7_TAG_STRING_C; h = next) {
h &= ~V7_TAG_MASK;
memcpy(&next, (char *) (uintptr_t) h, sizeof(h));
*(val_t *) (uintptr_t) h = gc_string_val_from_offset(head)
#if !V7_DISABLE_STR_ALLOC_SEQ
| ((val_t) asn << 32)
#endif
;
}
h &= ~V7_TAG_MASK;
/*
* the tail contains the first 6 bytes we stole from
* the actual string.
*/
len = decode_varint((unsigned char *) &h, &llen);
len += llen + 1;
/*
* restore the saved 6 bytes
* TODO(mkm): think about endianness
*/
memcpy(p, &h, sizeof(h) - 2);
/*
* and relocate the string data by packing it to the left.
*/
memmove(v7->owned_strings.buf + head, p, len);
v7->owned_strings.buf[head - 1] = 0x0;
#if defined(V7_GC_VERBOSE) && !V7_DISABLE_STR_ALLOC_SEQ
fprintf(stderr, "GC updated ASN %d: \"%.*s\"\n", asn, len - llen - 1,
v7->owned_strings.buf + head + llen);
#endif
p += len;
head += len;
} else {
len = decode_varint((unsigned char *) p, &llen);
len += llen + 1;
p += len;
}
}
#if defined(V7_GC_VERBOSE) && !V7_DISABLE_STR_ALLOC_SEQ
fprintf(stderr, "GC valid ASN range: [%d,%d)\n", v7->gc_min_asn,
v7->gc_next_asn);
#endif
v7->owned_strings.len = head;
}
void gc_dump_owned_strings(struct v7 *v7) {
size_t i;
for (i = 0; i < v7->owned_strings.len; i++) {
if (isprint((unsigned char) v7->owned_strings.buf[i])) {
fputc(v7->owned_strings.buf[i], stderr);
} else {
fputc('.', stderr);
}
}
fputc('\n', stderr);
}
/*
* builting on gcc, tried out by redefining it.
* Using null pointer as base can trigger undefined behavior, hence
* a portable workaround that involves a valid yet dummy pointer.
* It's meant to be used as a contant expression.
*/
#ifndef offsetof
#define offsetof(st, m) (((ptrdiff_t)(&((st *) 32)->m)) - 32)
#endif
V7_PRIVATE void compute_need_gc(struct v7 *v7) {
struct mbuf *m = &v7->owned_strings;
if ((double) m->len / (double) m->size > 0.9) {
v7->need_gc = 1;
}
/* TODO(mkm): check free heap */
}
V7_PRIVATE int maybe_gc(struct v7 *v7) {
if (!v7->inhibit_gc) {
v7_gc(v7, 0);
return 1;
}
return 0;
}
#if defined(V7_GC_VERBOSE)
static int gc_pass = 0;
#endif
/*
* mark an array of `val_t` values (*not pointers* to them)
*/
static void gc_mark_val_array(struct v7 *v7, val_t *vals, size_t len) {
val_t *vp;
for (vp = vals; vp < vals + len; vp++) {
gc_mark(v7, *vp);
gc_mark_string(v7, vp);
}
}
/*
* mark an mbuf containing *pointers* to `val_t` values
*/
static void gc_mark_mbuf_pt(struct v7 *v7, const struct mbuf *mbuf) {
val_t **vp;
for (vp = (val_t **) mbuf->buf; (char *) vp < mbuf->buf + mbuf->len; vp++) {
gc_mark(v7, **vp);
gc_mark_string(v7, *vp);
}
}
/*
* mark an mbuf containing `val_t` values (*not pointers* to them)
*/
static void gc_mark_mbuf_val(struct v7 *v7, const struct mbuf *mbuf) {
gc_mark_val_array(v7, (val_t *) mbuf->buf, mbuf->len / sizeof(val_t));
}
/*
* mark a vector containing `val_t` values (*not pointers* to them)
*/
static void gc_mark_vec_val(struct v7 *v7, const struct v7_vec *vec) {
gc_mark_val_array(v7, (val_t *) vec->p, vec->len / sizeof(val_t));
}
/*
* mark an mbuf containing foreign pointers to `struct bcode`
*/
static void gc_mark_mbuf_bcode_pt(struct v7 *v7, const struct mbuf *mbuf) {
struct bcode **vp;
for (vp = (struct bcode **) mbuf->buf; (char *) vp < mbuf->buf + mbuf->len;
vp++) {
gc_mark_vec_val(v7, &(*vp)->lit);
}
}
static void gc_mark_call_stack_private(
struct v7 *v7, struct v7_call_frame_private *call_stack) {
gc_mark_val_array(v7, (val_t *) &call_stack->vals,
sizeof(call_stack->vals) / sizeof(val_t));
}
static void gc_mark_call_stack_cfunc(struct v7 *v7,
struct v7_call_frame_cfunc *call_stack) {
gc_mark_val_array(v7, (val_t *) &call_stack->vals,
sizeof(call_stack->vals) / sizeof(val_t));
}
static void gc_mark_call_stack_bcode(struct v7 *v7,
struct v7_call_frame_bcode *call_stack) {
gc_mark_val_array(v7, (val_t *) &call_stack->vals,
sizeof(call_stack->vals) / sizeof(val_t));
}
/*
* mark `struct v7_call_frame` and all its back-linked frames
*/
static void gc_mark_call_stack(struct v7 *v7,
struct v7_call_frame_base *call_stack) {
while (call_stack != NULL) {
if (call_stack->type_mask & V7_CALL_FRAME_MASK_BCODE) {
gc_mark_call_stack_bcode(v7, (struct v7_call_frame_bcode *) call_stack);
}
if (call_stack->type_mask & V7_CALL_FRAME_MASK_PRIVATE) {
gc_mark_call_stack_private(v7,
(struct v7_call_frame_private *) call_stack);
}
if (call_stack->type_mask & V7_CALL_FRAME_MASK_CFUNC) {
gc_mark_call_stack_cfunc(v7, (struct v7_call_frame_cfunc *) call_stack);
}
call_stack = call_stack->prev;
}
}
/* Perform garbage collection */
void v7_gc(struct v7 *v7, int full) {
#if V7_DISABLE_GC
(void) v7;
(void) full;
return;
#else
#if defined(V7_GC_VERBOSE)
fprintf(stderr, "V7 GC pass %d\n", ++gc_pass);
#endif
gc_dump_arena_stats("Before GC objects", &v7->generic_object_arena);
gc_dump_arena_stats("Before GC functions", &v7->function_arena);
gc_dump_arena_stats("Before GC properties", &v7->property_arena);
gc_mark_call_stack(v7, v7->call_stack);
gc_mark_val_array(v7, (val_t *) &v7->vals, sizeof(v7->vals) / sizeof(val_t));
/* mark all items on bcode stack */
gc_mark_mbuf_val(v7, &v7->stack);
/* mark literals and names of all the active bcodes */
gc_mark_mbuf_bcode_pt(v7, &v7->act_bcodes);
gc_mark_mbuf_pt(v7, &v7->tmp_stack);
gc_mark_mbuf_pt(v7, &v7->owned_values);
gc_compact_strings(v7);
#ifdef V7_MALLOC_GC
gc_sweep_malloc(v7);
#else
gc_sweep(v7, &v7->generic_object_arena, 0);
gc_sweep(v7, &v7->function_arena, 0);
gc_sweep(v7, &v7->property_arena, 0);
#endif
gc_dump_arena_stats("After GC objects", &v7->generic_object_arena);
gc_dump_arena_stats("After GC functions", &v7->function_arena);
gc_dump_arena_stats("After GC properties", &v7->property_arena);
if (full) {
/*
* In case of full GC, we also resize strings buffer, but we still leave
* some extra space (at most, `_V7_STRING_BUF_RESERVE`) in order to avoid
* frequent reallocations
*/
size_t trimmed_size = v7->owned_strings.len + _V7_STRING_BUF_RESERVE;
if (trimmed_size < v7->owned_strings.size) {
heapusage_dont_count(1);
mbuf_resize(&v7->owned_strings, trimmed_size);
heapusage_dont_count(0);
}
}
#endif /* V7_DISABLE_GC */
}
V7_PRIVATE int gc_check_val(struct v7 *v7, val_t v) {
if (is_js_function(v)) {
return gc_check_ptr(&v7->function_arena, get_js_function_struct(v));
} else if (v7_is_object(v)) {
return gc_check_ptr(&v7->generic_object_arena, get_object_struct(v));
}
return 1;
}
V7_PRIVATE int gc_check_ptr(const struct gc_arena *a, const void *ptr) {
#ifdef V7_MALLOC_GC
(void) a;
(void) ptr;
return 1;
#else
const struct gc_cell *p = (const struct gc_cell *) ptr;
struct gc_block *b;
for (b = a->blocks; b != NULL; b = b->next) {
if (p >= b->base && p < GC_CELL_OP(a, b->base, +, b->size)) {
return 1;
}
}
return 0;
#endif
}
#ifdef V7_MODULE_LINES
#line 1 "v7/src/freeze.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "v7/src/core.h" */
/* Amalgamated: #include "v7/src/function.h" */
/* Amalgamated: #include "v7/src/util.h" */
/* Amalgamated: #include "v7/src/freeze.h" */
/* Amalgamated: #include "v7/src/bcode.h" */
/* Amalgamated: #include "v7/src/gc.h" */
/* Amalgamated: #include "common/base64.h" */
/* Amalgamated: #include "v7/src/object.h" */
#include
#ifdef V7_FREEZE
V7_PRIVATE void freeze(struct v7 *v7, char *filename) {
size_t i;
v7->freeze_file = fopen(filename, "w");
assert(v7->freeze_file != NULL);
#ifndef V7_FREEZE_NOT_READONLY
/*
* We have to remove `global` from the global object since
* when thawing global will actually be a new mutable object
* living on the heap.
*/
v7_del(v7, v7->vals.global_object, "global", 6);
#endif
for (i = 0; i < sizeof(v7->vals) / sizeof(val_t); i++) {
val_t v = ((val_t *) &v7->vals)[i];
fprintf(v7->freeze_file,
"{\"type\":\"global\", \"idx\":%zu, \"value\":\"%p\"}\n", i,
(void *) (v7_is_object(v) ? get_object_struct(v) : 0x0));
}
/*
* since v7->freeze_file is not NULL this will cause freeze_obj and
* freeze_prop to be called for each reachable object and property.
*/
v7_gc(v7, 1);
assert(v7->stack.len == 0);
fclose(v7->freeze_file);
v7->freeze_file = NULL;
}
static char *freeze_vec(struct v7_vec *vec) {
char *res = (char *) malloc(512 + vec->len);
res[0] = '"';
cs_base64_encode((const unsigned char *) vec->p, vec->len, &res[1]);
strcat(res, "\"");
return res;
}
V7_PRIVATE void freeze_obj(struct v7 *v7, FILE *f, v7_val_t v) {
struct v7_object *obj_base = get_object_struct(v);
unsigned int attrs = V7_OBJ_OFF_HEAP;
#ifndef V7_FREEZE_NOT_READONLY
attrs |= V7_OBJ_NOT_EXTENSIBLE;
#endif
if (is_js_function(v)) {
struct v7_js_function *func = get_js_function_struct(v);
struct bcode *bcode = func->bcode;
char *jops = freeze_vec(&bcode->ops);
int i;
fprintf(f,
"{\"type\":\"func\", \"addr\":\"%p\", \"props\":\"%p\", "
"\"attrs\":%d, \"scope\":\"%p\", \"bcode\":\"%p\""
#if V7_ENABLE_ENTITY_IDS
", \"entity_id_base\":%d, \"entity_id_spec\":\"%d\" "
#endif
"}\n",
(void *) obj_base,
(void *) ((uintptr_t) obj_base->properties & ~0x1),
obj_base->attributes | attrs, (void *) func->scope, (void *) bcode
#if V7_ENABLE_ENTITY_IDS
,
obj_base->entity_id_base, obj_base->entity_id_spec
#endif
);
fprintf(f,
"{\"type\":\"bcode\", \"addr\":\"%p\", \"args_cnt\":%d, "
"\"names_cnt\":%d, "
"\"strict_mode\": %d, \"func_name_present\": %d, \"ops\":%s, "
"\"lit\": [",
(void *) bcode, bcode->args_cnt, bcode->names_cnt,
bcode->strict_mode, bcode->func_name_present, jops);
for (i = 0; (size_t) i < bcode->lit.len / sizeof(val_t); i++) {
val_t v = ((val_t *) bcode->lit.p)[i];
const char *str;
if (((v & V7_TAG_MASK) == V7_TAG_STRING_O ||
(v & V7_TAG_MASK) == V7_TAG_STRING_F) &&
(str = v7_get_cstring(v7, &v)) != NULL) {
fprintf(f, "{\"str\": \"%s\"}", str);
} else {
fprintf(f, "{\"val\": \"0x%" INT64_X_FMT "\"}", v);
}
if ((size_t) i != bcode->lit.len / sizeof(val_t) - 1) {
fprintf(f, ",");
}
}
fprintf(f, "]}\n");
free(jops);
} else {
struct v7_generic_object *gob = get_generic_object_struct(v);
fprintf(f,
"{\"type\":\"obj\", \"addr\":\"%p\", \"props\":\"%p\", "
"\"attrs\":%d, \"proto\":\"%p\""
#if V7_ENABLE_ENTITY_IDS
", \"entity_id_base\":%d, \"entity_id_spec\":\"%d\" "
#endif
"}\n",
(void *) obj_base,
(void *) ((uintptr_t) obj_base->properties & ~0x1),
obj_base->attributes | attrs, (void *) gob->prototype
#if V7_ENABLE_ENTITY_IDS
,
obj_base->entity_id_base, obj_base->entity_id_spec
#endif
);
}
}
V7_PRIVATE void freeze_prop(struct v7 *v7, FILE *f, struct v7_property *prop) {
unsigned int attrs = _V7_PROPERTY_OFF_HEAP;
#ifndef V7_FREEZE_NOT_READONLY
attrs |= V7_PROPERTY_NON_WRITABLE | V7_PROPERTY_NON_CONFIGURABLE;
#endif
fprintf(f,
"{\"type\":\"prop\","
" \"addr\":\"%p\","
" \"next\":\"%p\","
" \"attrs\":%d,"
" \"name\":\"0x%" INT64_X_FMT
"\","
" \"value_type\":%d,"
" \"value\":\"0x%" INT64_X_FMT
"\","
" \"name_str\":\"%s\""
#if V7_ENABLE_ENTITY_IDS
", \"entity_id\":\"%d\""
#endif
"}\n",
(void *) prop, (void *) prop->next, prop->attributes | attrs,
prop->name, val_type(v7, prop->value), prop->value,
v7_get_cstring(v7, &prop->name)
#if V7_ENABLE_ENTITY_IDS
,
prop->entity_id
#endif
);
}
#endif
#ifdef V7_MODULE_LINES
#line 1 "v7/src/parser.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "common/coroutine.h" */
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/parser.h" */
/* Amalgamated: #include "v7/src/tokenizer.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* Amalgamated: #include "v7/src/exceptions.h" */
/* Amalgamated: #include "v7/src/ast.h" */
/* Amalgamated: #include "v7/src/primitive.h" */
/* Amalgamated: #include "v7/src/cyg_profile.h" */
#if !defined(V7_NO_COMPILER)
#define ACCEPT(t) (((v7)->cur_tok == (t)) ? next_tok((v7)), 1 : 0)
#define EXPECT(t) \
do { \
if ((v7)->cur_tok != (t)) { \
CR_THROW(PARSER_EXC_ID__SYNTAX_ERROR); \
} \
next_tok(v7); \
} while (0)
#define PARSE_WITH_OPT_ARG(tag, arg_tag, arg_parser, label) \
do { \
if (end_of_statement(v7) == V7_OK) { \
add_node(v7, a, (tag)); \
} else { \
add_node(v7, a, (arg_tag)); \
arg_parser(label); \
} \
} while (0)
#define N (CR_ARG_RET_PT()->arg)
/*
* User functions
* (as well as other in-function entry points)
*/
enum my_fid {
fid_none = CR_FID__NONE,
/* parse_script function */
fid_parse_script = CR_FID__USER,
fid_p_script_1,
fid_p_script_2,
fid_p_script_3,
fid_p_script_4,
/* parse_use_strict function */
fid_parse_use_strict,
/* parse_body function */
fid_parse_body,
fid_p_body_1,
fid_p_body_2,
/* parse_statement function */
fid_parse_statement,
fid_p_stat_1,
fid_p_stat_2,
fid_p_stat_3,
fid_p_stat_4,
fid_p_stat_5,
fid_p_stat_6,
fid_p_stat_7,
fid_p_stat_8,
fid_p_stat_9,
fid_p_stat_10,
fid_p_stat_11,
fid_p_stat_12,
fid_p_stat_13,
fid_p_stat_14,
/* parse_expression function */
fid_parse_expression,
fid_p_expr_1,
/* parse_assign function */
fid_parse_assign,
fid_p_assign_1,
/* parse_binary function */
fid_parse_binary,
fid_p_binary_1,
fid_p_binary_2,
fid_p_binary_3,
fid_p_binary_4,
fid_p_binary_5,
fid_p_binary_6,
/* parse_prefix function */
fid_parse_prefix,
fid_p_prefix_1,
/* parse_postfix function */
fid_parse_postfix,
fid_p_postfix_1,
/* parse_callexpr function */
fid_parse_callexpr,
fid_p_callexpr_1,
fid_p_callexpr_2,
fid_p_callexpr_3,
/* parse_newexpr function */
fid_parse_newexpr,
fid_p_newexpr_1,
fid_p_newexpr_2,
fid_p_newexpr_3,
fid_p_newexpr_4,
/* parse_terminal function */
fid_parse_terminal,
fid_p_terminal_1,
fid_p_terminal_2,
fid_p_terminal_3,
fid_p_terminal_4,
/* parse_block function */
fid_parse_block,
fid_p_block_1,
/* parse_if function */
fid_parse_if,
fid_p_if_1,
fid_p_if_2,
fid_p_if_3,
/* parse_while function */
fid_parse_while,
fid_p_while_1,
fid_p_while_2,
/* parse_ident function */
fid_parse_ident,
/* parse_ident_allow_reserved_words function */
fid_parse_ident_allow_reserved_words,
fid_p_ident_arw_1,
/* parse_funcdecl function */
fid_parse_funcdecl,
fid_p_funcdecl_1,
fid_p_funcdecl_2,
fid_p_funcdecl_3,
fid_p_funcdecl_4,
fid_p_funcdecl_5,
fid_p_funcdecl_6,
fid_p_funcdecl_7,
fid_p_funcdecl_8,
fid_p_funcdecl_9,
/* parse_arglist function */
fid_parse_arglist,
fid_p_arglist_1,
/* parse_member function */
fid_parse_member,
fid_p_member_1,
/* parse_memberexpr function */
fid_parse_memberexpr,
fid_p_memberexpr_1,
fid_p_memberexpr_2,
/* parse_var function */
fid_parse_var,
fid_p_var_1,
/* parse_prop function */
fid_parse_prop,
#if V7_ENABLE_JS_GETTERS
fid_p_prop_1_getter,
#endif
fid_p_prop_2,
#if V7_ENABLE_JS_SETTERS
fid_p_prop_3_setter,
#endif
fid_p_prop_4,
/* parse_dowhile function */
fid_parse_dowhile,
fid_p_dowhile_1,
fid_p_dowhile_2,
/* parse_for function */
fid_parse_for,
fid_p_for_1,
fid_p_for_2,
fid_p_for_3,
fid_p_for_4,
fid_p_for_5,
fid_p_for_6,
/* parse_try function */
fid_parse_try,
fid_p_try_1,
fid_p_try_2,
fid_p_try_3,
fid_p_try_4,
/* parse_switch function */
fid_parse_switch,
fid_p_switch_1,
fid_p_switch_2,
fid_p_switch_3,
fid_p_switch_4,
/* parse_with function */
fid_parse_with,
fid_p_with_1,
fid_p_with_2,
MY_FID_CNT
};
/*
* User exception IDs. The first one should have value `CR_EXC_ID__USER`
*/
enum parser_exc_id {
PARSER_EXC_ID__NONE = CR_EXC_ID__NONE,
PARSER_EXC_ID__SYNTAX_ERROR = CR_EXC_ID__USER,
};
/* structures with locals and args {{{ */
/* parse_script {{{ */
/* parse_script's arguments */
#if 0
typedef struct fid_parse_script_arg {
} fid_parse_script_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_script_arg_t;
#endif
/* parse_script's data on stack */
typedef struct fid_parse_script_locals {
#if 0
struct fid_parse_script_arg arg;
#endif
ast_off_t start;
ast_off_t outer_last_var_node;
int saved_in_strict;
} fid_parse_script_locals_t;
/* }}} */
/* parse_use_strict {{{ */
/* parse_use_strict's arguments */
#if 0
typedef struct fid_parse_use_strict_arg {
} fid_parse_use_strict_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_use_strict_arg_t;
#endif
/* parse_use_strict's data on stack */
#if 0
typedef struct fid_parse_use_strict_locals {
struct fid_parse_use_strict_arg arg;
} fid_parse_use_strict_locals_t;
#else
typedef cr_zero_size_type_t fid_parse_use_strict_locals_t;
#endif
#define CALL_PARSE_USE_STRICT(_label) \
do { \
CR_CALL(fid_parse_use_strict, _label); \
} while (0)
/* }}} */
/* parse_body {{{ */
/* parse_body's arguments */
typedef struct fid_parse_body_arg { enum v7_tok end; } fid_parse_body_arg_t;
/* parse_body's data on stack */
typedef struct fid_parse_body_locals {
struct fid_parse_body_arg arg;
ast_off_t start;
} fid_parse_body_locals_t;
#define CALL_PARSE_BODY(_end, _label) \
do { \
N.fid_parse_body.end = (_end); \
CR_CALL(fid_parse_body, _label); \
} while (0)
/* }}} */
/* parse_statement {{{ */
/* parse_statement's arguments */
#if 0
typedef struct fid_parse_statement_arg {
} fid_parse_statement_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_statement_arg_t;
#endif
/* parse_statement's data on stack */
#if 0
typedef struct fid_parse_statement_locals {
struct fid_parse_statement_arg arg;
} fid_parse_statement_locals_t;
#else
typedef cr_zero_size_type_t fid_parse_statement_locals_t;
#endif
#define CALL_PARSE_STATEMENT(_label) \
do { \
CR_CALL(fid_parse_statement, _label); \
} while (0)
/* }}} */
/* parse_expression {{{ */
/* parse_expression's arguments */
#if 0
typedef struct fid_parse_expression_arg {
} fid_parse_expression_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_expression_arg_t;
#endif
/* parse_expression's data on stack */
typedef struct fid_parse_expression_locals {
#if 0
struct fid_parse_expression_arg arg;
#endif
ast_off_t pos;
int group;
} fid_parse_expression_locals_t;
#define CALL_PARSE_EXPRESSION(_label) \
do { \
CR_CALL(fid_parse_expression, _label); \
} while (0)
/* }}} */
/* parse_assign {{{ */
/* parse_assign's arguments */
#if 0
typedef struct fid_parse_assign_arg {
} fid_parse_assign_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_assign_arg_t;
#endif
/* parse_assign's data on stack */
#if 0
typedef struct fid_parse_assign_locals {
struct fid_parse_assign_arg arg;
} fid_parse_assign_locals_t;
#else
typedef cr_zero_size_type_t fid_parse_assign_locals_t;
#endif
#define CALL_PARSE_ASSIGN(_label) \
do { \
CR_CALL(fid_parse_assign, _label); \
} while (0)
/* }}} */
/* parse_binary {{{ */
/* parse_binary's arguments */
typedef struct fid_parse_binary_arg {
ast_off_t pos;
uint8_t min_level;
} fid_parse_binary_arg_t;
/* parse_binary's data on stack */
typedef struct fid_parse_binary_locals {
struct fid_parse_binary_arg arg;
uint8_t i;
/* during iteration, it becomes negative, so should be signed */
int8_t level;
uint8_t /*enum v7_tok*/ tok;
uint8_t /*enum ast_tag*/ ast;
ast_off_t saved_mbuf_len;
} fid_parse_binary_locals_t;
#define CALL_PARSE_BINARY(_level, _pos, _label) \
do { \
N.fid_parse_binary.min_level = (_level); \
N.fid_parse_binary.pos = (_pos); \
CR_CALL(fid_parse_binary, _label); \
} while (0)
/* }}} */
/* parse_prefix {{{ */
/* parse_prefix's arguments */
#if 0
typedef struct fid_parse_prefix_arg {
} fid_parse_prefix_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_prefix_arg_t;
#endif
/* parse_prefix's data on stack */
#if 0
typedef struct fid_parse_prefix_locals {
struct fid_parse_prefix_arg arg;
} fid_parse_prefix_locals_t;
#else
typedef cr_zero_size_type_t fid_parse_prefix_locals_t;
#endif
#define CALL_PARSE_PREFIX(_label) \
do { \
CR_CALL(fid_parse_prefix, _label); \
} while (0)
/* }}} */
/* parse_postfix {{{ */
/* parse_postfix's arguments */
#if 0
typedef struct fid_parse_postfix_arg {
} fid_parse_postfix_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_postfix_arg_t;
#endif
/* parse_postfix's data on stack */
typedef struct fid_parse_postfix_locals {
#if 0
struct fid_parse_postfix_arg arg;
#endif
ast_off_t pos;
} fid_parse_postfix_locals_t;
#define CALL_PARSE_POSTFIX(_label) \
do { \
CR_CALL(fid_parse_postfix, _label); \
} while (0)
/* }}} */
/* parse_callexpr {{{ */
/* parse_callexpr's arguments */
#if 0
typedef struct fid_parse_callexpr_arg {
} fid_parse_callexpr_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_callexpr_arg_t;
#endif
/* parse_callexpr's data on stack */
typedef struct fid_parse_callexpr_locals {
#if 0
struct fid_parse_callexpr_arg arg;
#endif
ast_off_t pos;
} fid_parse_callexpr_locals_t;
#define CALL_PARSE_CALLEXPR(_label) \
do { \
CR_CALL(fid_parse_callexpr, _label); \
} while (0)
/* }}} */
/* parse_newexpr {{{ */
/* parse_newexpr's arguments */
#if 0
typedef struct fid_parse_newexpr_arg {
} fid_parse_newexpr_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_newexpr_arg_t;
#endif
/* parse_newexpr's data on stack */
typedef struct fid_parse_newexpr_locals {
#if 0
struct fid_parse_newexpr_arg arg;
#endif
ast_off_t start;
} fid_parse_newexpr_locals_t;
#define CALL_PARSE_NEWEXPR(_label) \
do { \
CR_CALL(fid_parse_newexpr, _label); \
} while (0)
/* }}} */
/* parse_terminal {{{ */
/* parse_terminal's arguments */
#if 0
typedef struct fid_parse_terminal_arg {
} fid_parse_terminal_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_terminal_arg_t;
#endif
/* parse_terminal's data on stack */
typedef struct fid_parse_terminal_locals {
#if 0
struct fid_parse_terminal_arg arg;
#endif
ast_off_t start;
} fid_parse_terminal_locals_t;
#define CALL_PARSE_TERMINAL(_label) \
do { \
CR_CALL(fid_parse_terminal, _label); \
} while (0)
/* }}} */
/* parse_block {{{ */
/* parse_block's arguments */
#if 0
typedef struct fid_parse_block_arg {
} fid_parse_block_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_block_arg_t;
#endif
/* parse_block's data on stack */
#if 0
typedef struct fid_parse_block_locals {
struct fid_parse_block_arg arg;
} fid_parse_block_locals_t;
#else
typedef cr_zero_size_type_t fid_parse_block_locals_t;
#endif
#define CALL_PARSE_BLOCK(_label) \
do { \
CR_CALL(fid_parse_block, _label); \
} while (0)
/* }}} */
/* parse_if {{{ */
/* parse_if's arguments */
#if 0
typedef struct fid_parse_if_arg {
} fid_parse_if_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_if_arg_t;
#endif
/* parse_if's data on stack */
typedef struct fid_parse_if_locals {
#if 0
struct fid_parse_if_arg arg;
#endif
ast_off_t start;
} fid_parse_if_locals_t;
#define CALL_PARSE_IF(_label) \
do { \
CR_CALL(fid_parse_if, _label); \
} while (0)
/* }}} */
/* parse_while {{{ */
/* parse_while's arguments */
#if 0
typedef struct fid_parse_while_arg {
} fid_parse_while_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_while_arg_t;
#endif
/* parse_while's data on stack */
typedef struct fid_parse_while_locals {
#if 0
struct fid_parse_while_arg arg;
#endif
ast_off_t start;
uint8_t saved_in_loop;
} fid_parse_while_locals_t;
#define CALL_PARSE_WHILE(_label) \
do { \
CR_CALL(fid_parse_while, _label); \
} while (0)
/* }}} */
/* parse_ident {{{ */
/* parse_ident's arguments */
#if 0
typedef struct fid_parse_ident_arg {
} fid_parse_ident_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_ident_arg_t;
#endif
/* parse_ident's data on stack */
#if 0
typedef struct fid_parse_ident_locals {
struct fid_parse_ident_arg arg;
} fid_parse_ident_locals_t;
#else
typedef cr_zero_size_type_t fid_parse_ident_locals_t;
#endif
#define CALL_PARSE_IDENT(_label) \
do { \
CR_CALL(fid_parse_ident, _label); \
} while (0)
/* }}} */
/* parse_ident_allow_reserved_words {{{ */
/* parse_ident_allow_reserved_words's arguments */
#if 0
typedef struct fid_parse_ident_allow_reserved_words_arg {
} fid_parse_ident_allow_reserved_words_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_ident_allow_reserved_words_arg_t;
#endif
/* parse_ident_allow_reserved_words's data on stack */
#if 0
typedef struct fid_parse_ident_allow_reserved_words_locals {
struct fid_parse_ident_allow_reserved_words_arg arg;
} fid_parse_ident_allow_reserved_words_locals_t;
#else
typedef cr_zero_size_type_t fid_parse_ident_allow_reserved_words_locals_t;
#endif
#define CALL_PARSE_IDENT_ALLOW_RESERVED_WORDS(_label) \
do { \
CR_CALL(fid_parse_ident_allow_reserved_words, _label); \
} while (0)
/* }}} */
/* parse_funcdecl {{{ */
/* parse_funcdecl's arguments */
typedef struct fid_parse_funcdecl_arg {
uint8_t require_named;
uint8_t reserved_name;
} fid_parse_funcdecl_arg_t;
/* parse_funcdecl's data on stack */
typedef struct fid_parse_funcdecl_locals {
struct fid_parse_funcdecl_arg arg;
ast_off_t start;
ast_off_t outer_last_var_node;
uint8_t saved_in_function;
uint8_t saved_in_strict;
} fid_parse_funcdecl_locals_t;
#define CALL_PARSE_FUNCDECL(_require_named, _reserved_name, _label) \
do { \
N.fid_parse_funcdecl.require_named = (_require_named); \
N.fid_parse_funcdecl.reserved_name = (_reserved_name); \
CR_CALL(fid_parse_funcdecl, _label); \
} while (0)
/* }}} */
/* parse_arglist {{{ */
/* parse_arglist's arguments */
#if 0
typedef struct fid_parse_arglist_arg {
} fid_parse_arglist_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_arglist_arg_t;
#endif
/* parse_arglist's data on stack */
#if 0
typedef struct fid_parse_arglist_locals {
struct fid_parse_arglist_arg arg;
} fid_parse_arglist_locals_t;
#else
typedef cr_zero_size_type_t fid_parse_arglist_locals_t;
#endif
#define CALL_PARSE_ARGLIST(_label) \
do { \
CR_CALL(fid_parse_arglist, _label); \
} while (0)
/* }}} */
/* parse_member {{{ */
/* parse_member's arguments */
typedef struct fid_parse_member_arg { ast_off_t pos; } fid_parse_member_arg_t;
/* parse_member's data on stack */
typedef struct fid_parse_member_locals {
struct fid_parse_member_arg arg;
} fid_parse_member_locals_t;
#define CALL_PARSE_MEMBER(_pos, _label) \
do { \
N.fid_parse_member.pos = (_pos); \
CR_CALL(fid_parse_member, _label); \
} while (0)
/* }}} */
/* parse_memberexpr {{{ */
/* parse_memberexpr's arguments */
#if 0
typedef struct fid_parse_memberexpr_arg {
} fid_parse_memberexpr_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_memberexpr_arg_t;
#endif
/* parse_memberexpr's data on stack */
typedef struct fid_parse_memberexpr_locals {
#if 0
struct fid_parse_memberexpr_arg arg;
#endif
ast_off_t pos;
} fid_parse_memberexpr_locals_t;
#define CALL_PARSE_MEMBEREXPR(_label) \
do { \
CR_CALL(fid_parse_memberexpr, _label); \
} while (0)
/* }}} */
/* parse_var {{{ */
/* parse_var's arguments */
#if 0
typedef struct fid_parse_var_arg {
} fid_parse_var_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_var_arg_t;
#endif
/* parse_var's data on stack */
typedef struct fid_parse_var_locals {
#if 0
struct fid_parse_var_arg arg;
#endif
ast_off_t start;
} fid_parse_var_locals_t;
#define CALL_PARSE_VAR(_label) \
do { \
CR_CALL(fid_parse_var, _label); \
} while (0)
/* }}} */
/* parse_prop {{{ */
/* parse_prop's arguments */
#if 0
typedef struct fid_parse_prop_arg {
} fid_parse_prop_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_prop_arg_t;
#endif
/* parse_prop's data on stack */
#if 0
typedef struct fid_parse_prop_locals {
struct fid_parse_prop_arg arg;
} fid_parse_prop_locals_t;
#else
typedef cr_zero_size_type_t fid_parse_prop_locals_t;
#endif
#define CALL_PARSE_PROP(_label) \
do { \
CR_CALL(fid_parse_prop, _label); \
} while (0)
/* }}} */
/* parse_dowhile {{{ */
/* parse_dowhile's arguments */
#if 0
typedef struct fid_parse_dowhile_arg {
} fid_parse_dowhile_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_dowhile_arg_t;
#endif
/* parse_dowhile's data on stack */
typedef struct fid_parse_dowhile_locals {
#if 0
struct fid_parse_dowhile_arg arg;
#endif
ast_off_t start;
uint8_t saved_in_loop;
} fid_parse_dowhile_locals_t;
#define CALL_PARSE_DOWHILE(_label) \
do { \
CR_CALL(fid_parse_dowhile, _label); \
} while (0)
/* }}} */
/* parse_for {{{ */
/* parse_for's arguments */
#if 0
typedef struct fid_parse_for_arg {
} fid_parse_for_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_for_arg_t;
#endif
/* parse_for's data on stack */
typedef struct fid_parse_for_locals {
#if 0
struct fid_parse_for_arg arg;
#endif
ast_off_t start;
uint8_t saved_in_loop;
} fid_parse_for_locals_t;
#define CALL_PARSE_FOR(_label) \
do { \
CR_CALL(fid_parse_for, _label); \
} while (0)
/* }}} */
/* parse_try {{{ */
/* parse_try's arguments */
#if 0
typedef struct fid_parse_try_arg {
} fid_parse_try_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_try_arg_t;
#endif
/* parse_try's data on stack */
typedef struct fid_parse_try_locals {
#if 0
struct fid_parse_try_arg arg;
#endif
ast_off_t start;
uint8_t catch_or_finally;
} fid_parse_try_locals_t;
#define CALL_PARSE_TRY(_label) \
do { \
CR_CALL(fid_parse_try, _label); \
} while (0)
/* }}} */
/* parse_switch {{{ */
/* parse_switch's arguments */
#if 0
typedef struct fid_parse_switch_arg {
} fid_parse_switch_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_switch_arg_t;
#endif
/* parse_switch's data on stack */
typedef struct fid_parse_switch_locals {
#if 0
struct fid_parse_switch_arg arg;
#endif
ast_off_t start;
int saved_in_switch;
ast_off_t case_start;
} fid_parse_switch_locals_t;
#define CALL_PARSE_SWITCH(_label) \
do { \
CR_CALL(fid_parse_switch, _label); \
} while (0)
/* }}} */
/* parse_with {{{ */
/* parse_with's arguments */
#if 0
typedef struct fid_parse_with_arg {
} fid_parse_with_arg_t;
#else
typedef cr_zero_size_type_t fid_parse_with_arg_t;
#endif
/* parse_with's data on stack */
typedef struct fid_parse_with_locals {
#if 0
struct fid_parse_with_arg arg;
#endif
ast_off_t start;
} fid_parse_with_locals_t;
#define CALL_PARSE_WITH(_label) \
do { \
CR_CALL(fid_parse_with, _label); \
} while (0)
/* }}} */
/* }}} */
/*
* Array of "function" descriptors. Each descriptor contains just a size
* of "function"'s locals.
*/
static const struct cr_func_desc _fid_descrs[MY_FID_CNT] = {
/* fid_none */
{0},
/* fid_parse_script ----------------------------------------- */
/* fid_parse_script */
{CR_LOCALS_SIZEOF(fid_parse_script_locals_t)},
/* fid_p_script_1 */
{CR_LOCALS_SIZEOF(fid_parse_script_locals_t)},
/* fid_p_script_2 */
{CR_LOCALS_SIZEOF(fid_parse_script_locals_t)},
/* fid_p_script_3 */
{CR_LOCALS_SIZEOF(fid_parse_script_locals_t)},
/* fid_p_script_4 */
{CR_LOCALS_SIZEOF(fid_parse_script_locals_t)},
/* fid_parse_use_strict ----------------------------------------- */
/* fid_parse_use_strict */
{CR_LOCALS_SIZEOF(fid_parse_use_strict_locals_t)},
/* fid_parse_body ----------------------------------------- */
/* fid_parse_body */
{CR_LOCALS_SIZEOF(fid_parse_body_locals_t)},
/* fid_p_body_1 */
{CR_LOCALS_SIZEOF(fid_parse_body_locals_t)},
/* fid_p_body_2 */
{CR_LOCALS_SIZEOF(fid_parse_body_locals_t)},
/* fid_parse_statement ----------------------------------------- */
/* fid_parse_statement */
{CR_LOCALS_SIZEOF(fid_parse_statement_locals_t)},
/* fid_p_stat_1 */
{CR_LOCALS_SIZEOF(fid_parse_statement_locals_t)},
/* fid_p_stat_2 */
{CR_LOCALS_SIZEOF(fid_parse_statement_locals_t)},
/* fid_p_stat_3 */
{CR_LOCALS_SIZEOF(fid_parse_statement_locals_t)},
/* fid_p_stat_4 */
{CR_LOCALS_SIZEOF(fid_parse_statement_locals_t)},
/* fid_p_stat_5 */
{CR_LOCALS_SIZEOF(fid_parse_statement_locals_t)},
/* fid_p_stat_6 */
{CR_LOCALS_SIZEOF(fid_parse_statement_locals_t)},
/* fid_p_stat_7 */
{CR_LOCALS_SIZEOF(fid_parse_statement_locals_t)},
/* fid_p_stat_8 */
{CR_LOCALS_SIZEOF(fid_parse_statement_locals_t)},
/* fid_p_stat_9 */
{CR_LOCALS_SIZEOF(fid_parse_statement_locals_t)},
/* fid_p_stat_10 */
{CR_LOCALS_SIZEOF(fid_parse_statement_locals_t)},
/* fid_p_stat_11 */
{CR_LOCALS_SIZEOF(fid_parse_statement_locals_t)},
/* fid_p_stat_12 */
{CR_LOCALS_SIZEOF(fid_parse_statement_locals_t)},
/* fid_p_stat_13 */
{CR_LOCALS_SIZEOF(fid_parse_statement_locals_t)},
/* fid_p_stat_14 */
{CR_LOCALS_SIZEOF(fid_parse_statement_locals_t)},
/* fid_parse_expression ----------------------------------------- */
/* fid_parse_expression */
{CR_LOCALS_SIZEOF(fid_parse_expression_locals_t)},
/* fid_p_expr_1 */
{CR_LOCALS_SIZEOF(fid_parse_expression_locals_t)},
/* fid_parse_assign ----------------------------------------- */
/* fid_parse_assign */
{CR_LOCALS_SIZEOF(fid_parse_assign_locals_t)},
/* fid_p_assign_1 */
{CR_LOCALS_SIZEOF(fid_parse_assign_locals_t)},
/* fid_parse_binary ----------------------------------------- */
/* fid_parse_binary */
{CR_LOCALS_SIZEOF(fid_parse_binary_locals_t)},
/* fid_p_binary_1 */
{CR_LOCALS_SIZEOF(fid_parse_binary_locals_t)},
/* fid_p_binary_2 */
{CR_LOCALS_SIZEOF(fid_parse_binary_locals_t)},
/* fid_p_binary_3 */
{CR_LOCALS_SIZEOF(fid_parse_binary_locals_t)},
/* fid_p_binary_4 */
{CR_LOCALS_SIZEOF(fid_parse_binary_locals_t)},
/* fid_p_binary_5 */
{CR_LOCALS_SIZEOF(fid_parse_binary_locals_t)},
/* fid_p_binary_6 */
{CR_LOCALS_SIZEOF(fid_parse_binary_locals_t)},
/* fid_parse_prefix ----------------------------------------- */
/* fid_parse_prefix */
{CR_LOCALS_SIZEOF(fid_parse_prefix_locals_t)},
/* fid_p_prefix_1 */
{CR_LOCALS_SIZEOF(fid_parse_prefix_locals_t)},
/* fid_parse_postfix ----------------------------------------- */
/* fid_parse_postfix */
{CR_LOCALS_SIZEOF(fid_parse_postfix_locals_t)},
/* fid_p_postfix_1 */
{CR_LOCALS_SIZEOF(fid_parse_postfix_locals_t)},
/* fid_parse_callexpr ----------------------------------------- */
/* fid_parse_callexpr */
{CR_LOCALS_SIZEOF(fid_parse_callexpr_locals_t)},
/* fid_p_callexpr_1 */
{CR_LOCALS_SIZEOF(fid_parse_callexpr_locals_t)},
/* fid_p_callexpr_2 */
{CR_LOCALS_SIZEOF(fid_parse_callexpr_locals_t)},
/* fid_p_callexpr_3 */
{CR_LOCALS_SIZEOF(fid_parse_callexpr_locals_t)},
/* fid_parse_newexpr ----------------------------------------- */
/* fid_parse_newexpr */
{CR_LOCALS_SIZEOF(fid_parse_newexpr_locals_t)},
/* fid_p_newexpr_1 */
{CR_LOCALS_SIZEOF(fid_parse_newexpr_locals_t)},
/* fid_p_newexpr_2 */
{CR_LOCALS_SIZEOF(fid_parse_newexpr_locals_t)},
/* fid_p_newexpr_3 */
{CR_LOCALS_SIZEOF(fid_parse_newexpr_locals_t)},
/* fid_p_newexpr_4 */
{CR_LOCALS_SIZEOF(fid_parse_newexpr_locals_t)},
/* fid_parse_terminal ----------------------------------------- */
/* fid_parse_terminal */
{CR_LOCALS_SIZEOF(fid_parse_terminal_locals_t)},
/* fid_p_terminal_1 */
{CR_LOCALS_SIZEOF(fid_parse_terminal_locals_t)},
/* fid_p_terminal_2 */
{CR_LOCALS_SIZEOF(fid_parse_terminal_locals_t)},
/* fid_p_terminal_3 */
{CR_LOCALS_SIZEOF(fid_parse_terminal_locals_t)},
/* fid_p_terminal_4 */
{CR_LOCALS_SIZEOF(fid_parse_terminal_locals_t)},
/* fid_parse_block ----------------------------------------- */
/* fid_parse_block */
{CR_LOCALS_SIZEOF(fid_parse_block_locals_t)},
/* fid_p_block_1 */
{CR_LOCALS_SIZEOF(fid_parse_block_locals_t)},
/* fid_parse_if ----------------------------------------- */
/* fid_parse_if */
{CR_LOCALS_SIZEOF(fid_parse_if_locals_t)},
/* fid_p_if_1 */
{CR_LOCALS_SIZEOF(fid_parse_if_locals_t)},
/* fid_p_if_2 */
{CR_LOCALS_SIZEOF(fid_parse_if_locals_t)},
/* fid_p_if_3 */
{CR_LOCALS_SIZEOF(fid_parse_if_locals_t)},
/* fid_parse_while ----------------------------------------- */
/* fid_parse_while */
{CR_LOCALS_SIZEOF(fid_parse_while_locals_t)},
/* fid_p_while_1 */
{CR_LOCALS_SIZEOF(fid_parse_while_locals_t)},
/* fid_p_while_2 */
{CR_LOCALS_SIZEOF(fid_parse_while_locals_t)},
/* fid_parse_ident ----------------------------------------- */
/* fid_parse_ident */
{CR_LOCALS_SIZEOF(fid_parse_ident_locals_t)},
/* fid_parse_ident_allow_reserved_words -------------------- */
/* fid_parse_ident_allow_reserved_words */
{CR_LOCALS_SIZEOF(fid_parse_ident_allow_reserved_words_locals_t)},
/* fid_p_ident_allow_reserved_words_1 */
{CR_LOCALS_SIZEOF(fid_parse_ident_allow_reserved_words_locals_t)},
/* fid_parse_funcdecl ----------------------------------------- */
/* fid_parse_funcdecl */
{CR_LOCALS_SIZEOF(fid_parse_funcdecl_locals_t)},
/* fid_p_funcdecl_1 */
{CR_LOCALS_SIZEOF(fid_parse_funcdecl_locals_t)},
/* fid_p_funcdecl_2 */
{CR_LOCALS_SIZEOF(fid_parse_funcdecl_locals_t)},
/* fid_p_funcdecl_3 */
{CR_LOCALS_SIZEOF(fid_parse_funcdecl_locals_t)},
/* fid_p_funcdecl_4 */
{CR_LOCALS_SIZEOF(fid_parse_funcdecl_locals_t)},
/* fid_p_funcdecl_5 */
{CR_LOCALS_SIZEOF(fid_parse_funcdecl_locals_t)},
/* fid_p_funcdecl_6 */
{CR_LOCALS_SIZEOF(fid_parse_funcdecl_locals_t)},
/* fid_p_funcdecl_7 */
{CR_LOCALS_SIZEOF(fid_parse_funcdecl_locals_t)},
/* fid_p_funcdecl_8 */
{CR_LOCALS_SIZEOF(fid_parse_funcdecl_locals_t)},
/* fid_p_funcdecl_9 */
{CR_LOCALS_SIZEOF(fid_parse_funcdecl_locals_t)},
/* fid_parse_arglist ----------------------------------------- */
/* fid_parse_arglist */
{CR_LOCALS_SIZEOF(fid_parse_arglist_locals_t)},
/* fid_p_arglist_1 */
{CR_LOCALS_SIZEOF(fid_parse_arglist_locals_t)},
/* fid_parse_member ----------------------------------------- */
/* fid_parse_member */
{CR_LOCALS_SIZEOF(fid_parse_member_locals_t)},
/* fid_p_member_1 */
{CR_LOCALS_SIZEOF(fid_parse_member_locals_t)},
/* fid_parse_memberexpr ----------------------------------------- */
/* fid_parse_memberexpr */
{CR_LOCALS_SIZEOF(fid_parse_memberexpr_locals_t)},
/* fid_p_memberexpr_1 */
{CR_LOCALS_SIZEOF(fid_parse_memberexpr_locals_t)},
/* fid_p_memberexpr_2 */
{CR_LOCALS_SIZEOF(fid_parse_memberexpr_locals_t)},
/* fid_parse_var ----------------------------------------- */
/* fid_parse_var */
{CR_LOCALS_SIZEOF(fid_parse_var_locals_t)},
/* fid_p_var_1 */
{CR_LOCALS_SIZEOF(fid_parse_var_locals_t)},
/* fid_parse_prop ----------------------------------------- */
/* fid_parse_prop */
{CR_LOCALS_SIZEOF(fid_parse_prop_locals_t)},
#if V7_ENABLE_JS_GETTERS
/* fid_p_prop_1_getter */
{CR_LOCALS_SIZEOF(fid_parse_prop_locals_t)},
#endif
/* fid_p_prop_2 */
{CR_LOCALS_SIZEOF(fid_parse_prop_locals_t)},
#if V7_ENABLE_JS_SETTERS
/* fid_p_prop_3_setter */
{CR_LOCALS_SIZEOF(fid_parse_prop_locals_t)},
#endif
/* fid_p_prop_4 */
{CR_LOCALS_SIZEOF(fid_parse_prop_locals_t)},
/* fid_parse_dowhile ----------------------------------------- */
/* fid_parse_dowhile */
{CR_LOCALS_SIZEOF(fid_parse_dowhile_locals_t)},
/* fid_p_dowhile_1 */
{CR_LOCALS_SIZEOF(fid_parse_dowhile_locals_t)},
/* fid_p_dowhile_2 */
{CR_LOCALS_SIZEOF(fid_parse_dowhile_locals_t)},
/* fid_parse_for ----------------------------------------- */
/* fid_parse_for */
{CR_LOCALS_SIZEOF(fid_parse_for_locals_t)},
/* fid_p_for_1 */
{CR_LOCALS_SIZEOF(fid_parse_for_locals_t)},
/* fid_p_for_2 */
{CR_LOCALS_SIZEOF(fid_parse_for_locals_t)},
/* fid_p_for_3 */
{CR_LOCALS_SIZEOF(fid_parse_for_locals_t)},
/* fid_p_for_4 */
{CR_LOCALS_SIZEOF(fid_parse_for_locals_t)},
/* fid_p_for_5 */
{CR_LOCALS_SIZEOF(fid_parse_for_locals_t)},
/* fid_p_for_6 */
{CR_LOCALS_SIZEOF(fid_parse_for_locals_t)},
/* fid_parse_try ----------------------------------------- */
/* fid_parse_try */
{CR_LOCALS_SIZEOF(fid_parse_try_locals_t)},
/* fid_p_try_1 */
{CR_LOCALS_SIZEOF(fid_parse_try_locals_t)},
/* fid_p_try_2 */
{CR_LOCALS_SIZEOF(fid_parse_try_locals_t)},
/* fid_p_try_3 */
{CR_LOCALS_SIZEOF(fid_parse_try_locals_t)},
/* fid_p_try_4 */
{CR_LOCALS_SIZEOF(fid_parse_try_locals_t)},
/* fid_parse_switch ----------------------------------------- */
/* fid_parse_switch */
{CR_LOCALS_SIZEOF(fid_parse_switch_locals_t)},
/* fid_p_switch_1 */
{CR_LOCALS_SIZEOF(fid_parse_switch_locals_t)},
/* fid_p_switch_2 */
{CR_LOCALS_SIZEOF(fid_parse_switch_locals_t)},
/* fid_p_switch_3 */
{CR_LOCALS_SIZEOF(fid_parse_switch_locals_t)},
/* fid_p_switch_4 */
{CR_LOCALS_SIZEOF(fid_parse_switch_locals_t)},
/* fid_parse_with ----------------------------------------- */
/* fid_parse_with */
{CR_LOCALS_SIZEOF(fid_parse_with_locals_t)},
/* fid_p_with_1 */
{CR_LOCALS_SIZEOF(fid_parse_with_locals_t)},
/* fid_p_with_2 */
{CR_LOCALS_SIZEOF(fid_parse_with_locals_t)},
};
/*
* Union of arguments and return values for all existing "functions".
*
* Used as an accumulator when we call function, return from function,
* yield, or resume.
*/
union user_arg_ret {
/* arguments to the next function */
union {
#if 0
fid_parse_script_arg_t fid_parse_script;
fid_parse_use_strict_arg_t fid_parse_use_strict;
fid_parse_statement_arg_t fid_parse_statement;
fid_parse_expression_arg_t fid_parse_expression;
fid_parse_assign_arg_t fid_parse_assign;
fid_parse_prefix_arg_t fid_parse_prefix;
fid_parse_postfix_arg_t fid_parse_postfix;
fid_parse_callexpr_arg_t fid_parse_callexpr;
fid_parse_newexpr_arg_t fid_parse_newexpr;
fid_parse_terminal_arg_t fid_parse_terminal;
fid_parse_block_arg_t fid_parse_block;
fid_parse_if_arg_t fid_parse_if;
fid_parse_while_arg_t fid_parse_while;
fid_parse_ident_arg_t fid_parse_ident;
fid_parse_ident_allow_reserved_words_arg_t
fid_parse_ident_allow_reserved_words;
fid_parse_arglist_arg_t fid_parse_arglist;
fid_parse_memberexpr_arg_t fid_parse_memberexpr;
fid_parse_var_arg_t fid_parse_var;
fid_parse_prop_arg_t fid_parse_prop;
fid_parse_dowhile_arg_t fid_parse_dowhile;
fid_parse_for_arg_t fid_parse_for;
fid_parse_try_arg_t fid_parse_try;
fid_parse_switch_arg_t fid_parse_switch;
fid_parse_with_arg_t fid_parse_with;
#endif
fid_parse_body_arg_t fid_parse_body;
fid_parse_binary_arg_t fid_parse_binary;
fid_parse_funcdecl_arg_t fid_parse_funcdecl;
fid_parse_member_arg_t fid_parse_member;
} arg;
/* value returned from function */
/*
union {
} ret;
*/
};
static enum v7_tok next_tok(struct v7 *v7) {
int prev_line_no = v7->pstate.prev_line_no;
v7->pstate.prev_line_no = v7->pstate.line_no;
v7->pstate.line_no += skip_to_next_tok(&v7->pstate.pc, v7->pstate.src_end);
v7->after_newline = prev_line_no != v7->pstate.line_no;
v7->tok = v7->pstate.pc;
v7->cur_tok = get_tok(&v7->pstate.pc, v7->pstate.src_end, &v7->cur_tok_dbl,
v7->cur_tok);
v7->tok_len = v7->pstate.pc - v7->tok;
v7->pstate.line_no += skip_to_next_tok(&v7->pstate.pc, v7->pstate.src_end);
return v7->cur_tok;
}
#if !V7_DISABLE_LINE_NUMBERS
/*
* Assumes `offset` points to the byte right after a tag
*/
static void insert_line_no_if_changed(struct v7 *v7, struct ast *a,
ast_off_t offset) {
if (v7->pstate.prev_line_no != v7->line_no) {
v7->line_no = v7->pstate.prev_line_no;
ast_add_line_no(a, offset - 1, v7->line_no);
} else {
#if V7_AST_FORCE_LINE_NUMBERS
/*
* This mode is needed for debug only: to make sure AST consumers correctly
* consume all nodes with line numbers data encoded
*/
ast_add_line_no(a, offset - 1, 0);
#endif
}
}
#else
static void insert_line_no_if_changed(struct v7 *v7, struct ast *a,
ast_off_t offset) {
(void) v7;
(void) a;
(void) offset;
}
#endif
static ast_off_t insert_node(struct v7 *v7, struct ast *a, ast_off_t start,
enum ast_tag tag) {
ast_off_t ret = ast_insert_node(a, start, tag);
insert_line_no_if_changed(v7, a, ret);
return ret;
}
static ast_off_t add_node(struct v7 *v7, struct ast *a, enum ast_tag tag) {
return insert_node(v7, a, a->mbuf.len, tag);
}
static ast_off_t insert_inlined_node(struct v7 *v7, struct ast *a,
ast_off_t start, enum ast_tag tag,
const char *name, size_t len) {
ast_off_t ret = ast_insert_inlined_node(a, start, tag, name, len);
insert_line_no_if_changed(v7, a, ret);
return ret;
}
static ast_off_t add_inlined_node(struct v7 *v7, struct ast *a,
enum ast_tag tag, const char *name,
size_t len) {
return insert_inlined_node(v7, a, a->mbuf.len, tag, name, len);
}
static unsigned long get_column(const char *code, const char *pos) {
const char *p = pos;
while (p > code && *p != '\n') {
p--;
}
return p == code ? pos - p : pos - (p + 1);
}
static enum v7_err end_of_statement(struct v7 *v7) {
if (v7->cur_tok == TOK_SEMICOLON || v7->cur_tok == TOK_END_OF_INPUT ||
v7->cur_tok == TOK_CLOSE_CURLY || v7->after_newline) {
return V7_OK;
}
return V7_SYNTAX_ERROR;
}
static enum v7_tok lookahead(const struct v7 *v7) {
const char *s = v7->pstate.pc;
double d;
return get_tok(&s, v7->pstate.src_end, &d, v7->cur_tok);
}
static int parse_optional(struct v7 *v7, struct ast *a,
enum v7_tok terminator) {
if (v7->cur_tok != terminator) {
return 1;
}
add_node(v7, a, AST_NOP);
return 0;
}
/*
* On ESP8266 'levels' declaration have to be outside of 'parse_binary'
* in order to prevent reboot on return from this function
* TODO(alashkin): understand why
*/
#define NONE \
{ (enum v7_tok) 0, (enum v7_tok) 0, (enum ast_tag) 0 }
static const struct {
int len, left_to_right;
struct {
enum v7_tok start_tok, end_tok;
enum ast_tag start_ast;
} parts[2];
} levels[] = {
{1, 0, {{TOK_ASSIGN, TOK_URSHIFT_ASSIGN, AST_ASSIGN}, NONE}},
{1, 0, {{TOK_QUESTION, TOK_QUESTION, AST_COND}, NONE}},
{1, 1, {{TOK_LOGICAL_OR, TOK_LOGICAL_OR, AST_LOGICAL_OR}, NONE}},
{1, 1, {{TOK_LOGICAL_AND, TOK_LOGICAL_AND, AST_LOGICAL_AND}, NONE}},
{1, 1, {{TOK_OR, TOK_OR, AST_OR}, NONE}},
{1, 1, {{TOK_XOR, TOK_XOR, AST_XOR}, NONE}},
{1, 1, {{TOK_AND, TOK_AND, AST_AND}, NONE}},
{1, 1, {{TOK_EQ, TOK_NE_NE, AST_EQ}, NONE}},
{2, 1, {{TOK_LE, TOK_GT, AST_LE}, {TOK_IN, TOK_INSTANCEOF, AST_IN}}},
{1, 1, {{TOK_LSHIFT, TOK_URSHIFT, AST_LSHIFT}, NONE}},
{1, 1, {{TOK_PLUS, TOK_MINUS, AST_ADD}, NONE}},
{1, 1, {{TOK_REM, TOK_DIV, AST_REM}, NONE}}};
enum cr_status parser_cr_exec(struct cr_ctx *p_ctx, struct v7 *v7,
struct ast *a) {
enum cr_status rc = CR_RES__OK;
_cr_iter_begin:
rc = cr_on_iter_begin(p_ctx);
if (rc != CR_RES__OK) {
return rc;
}
/*
* dispatcher switch: depending on the fid, jump to the corresponding label
*/
switch ((enum my_fid) CR_CURR_FUNC()) {
CR_DEFINE_ENTRY_POINT(fid_none);
CR_DEFINE_ENTRY_POINT(fid_parse_script);
CR_DEFINE_ENTRY_POINT(fid_p_script_1);
CR_DEFINE_ENTRY_POINT(fid_p_script_2);
CR_DEFINE_ENTRY_POINT(fid_p_script_3);
CR_DEFINE_ENTRY_POINT(fid_p_script_4);
CR_DEFINE_ENTRY_POINT(fid_parse_use_strict);
CR_DEFINE_ENTRY_POINT(fid_parse_body);
CR_DEFINE_ENTRY_POINT(fid_p_body_1);
CR_DEFINE_ENTRY_POINT(fid_p_body_2);
CR_DEFINE_ENTRY_POINT(fid_parse_statement);
CR_DEFINE_ENTRY_POINT(fid_p_stat_1);
CR_DEFINE_ENTRY_POINT(fid_p_stat_2);
CR_DEFINE_ENTRY_POINT(fid_p_stat_3);
CR_DEFINE_ENTRY_POINT(fid_p_stat_4);
CR_DEFINE_ENTRY_POINT(fid_p_stat_5);
CR_DEFINE_ENTRY_POINT(fid_p_stat_6);
CR_DEFINE_ENTRY_POINT(fid_p_stat_7);
CR_DEFINE_ENTRY_POINT(fid_p_stat_8);
CR_DEFINE_ENTRY_POINT(fid_p_stat_9);
CR_DEFINE_ENTRY_POINT(fid_p_stat_10);
CR_DEFINE_ENTRY_POINT(fid_p_stat_11);
CR_DEFINE_ENTRY_POINT(fid_p_stat_12);
CR_DEFINE_ENTRY_POINT(fid_p_stat_13);
CR_DEFINE_ENTRY_POINT(fid_p_stat_14);
CR_DEFINE_ENTRY_POINT(fid_parse_expression);
CR_DEFINE_ENTRY_POINT(fid_p_expr_1);
CR_DEFINE_ENTRY_POINT(fid_parse_assign);
CR_DEFINE_ENTRY_POINT(fid_p_assign_1);
CR_DEFINE_ENTRY_POINT(fid_parse_binary);
CR_DEFINE_ENTRY_POINT(fid_p_binary_2);
CR_DEFINE_ENTRY_POINT(fid_p_binary_3);
CR_DEFINE_ENTRY_POINT(fid_p_binary_4);
CR_DEFINE_ENTRY_POINT(fid_p_binary_5);
CR_DEFINE_ENTRY_POINT(fid_p_binary_6);
CR_DEFINE_ENTRY_POINT(fid_parse_prefix);
CR_DEFINE_ENTRY_POINT(fid_p_prefix_1);
CR_DEFINE_ENTRY_POINT(fid_parse_postfix);
CR_DEFINE_ENTRY_POINT(fid_p_postfix_1);
CR_DEFINE_ENTRY_POINT(fid_parse_callexpr);
CR_DEFINE_ENTRY_POINT(fid_p_callexpr_1);
CR_DEFINE_ENTRY_POINT(fid_p_callexpr_2);
CR_DEFINE_ENTRY_POINT(fid_p_callexpr_3);
CR_DEFINE_ENTRY_POINT(fid_parse_newexpr);
CR_DEFINE_ENTRY_POINT(fid_p_newexpr_1);
CR_DEFINE_ENTRY_POINT(fid_p_newexpr_2);
CR_DEFINE_ENTRY_POINT(fid_p_newexpr_3);
CR_DEFINE_ENTRY_POINT(fid_p_newexpr_4);
CR_DEFINE_ENTRY_POINT(fid_parse_terminal);
CR_DEFINE_ENTRY_POINT(fid_p_terminal_1);
CR_DEFINE_ENTRY_POINT(fid_p_terminal_2);
CR_DEFINE_ENTRY_POINT(fid_p_terminal_3);
CR_DEFINE_ENTRY_POINT(fid_p_terminal_4);
CR_DEFINE_ENTRY_POINT(fid_parse_block);
CR_DEFINE_ENTRY_POINT(fid_p_block_1);
CR_DEFINE_ENTRY_POINT(fid_parse_if);
CR_DEFINE_ENTRY_POINT(fid_p_if_1);
CR_DEFINE_ENTRY_POINT(fid_p_if_2);
CR_DEFINE_ENTRY_POINT(fid_p_if_3);
CR_DEFINE_ENTRY_POINT(fid_parse_while);
CR_DEFINE_ENTRY_POINT(fid_p_while_1);
CR_DEFINE_ENTRY_POINT(fid_p_while_2);
CR_DEFINE_ENTRY_POINT(fid_parse_ident);
CR_DEFINE_ENTRY_POINT(fid_parse_ident_allow_reserved_words);
CR_DEFINE_ENTRY_POINT(fid_p_ident_arw_1);
CR_DEFINE_ENTRY_POINT(fid_parse_funcdecl);
CR_DEFINE_ENTRY_POINT(fid_p_funcdecl_1);
CR_DEFINE_ENTRY_POINT(fid_p_funcdecl_2);
CR_DEFINE_ENTRY_POINT(fid_p_funcdecl_3);
CR_DEFINE_ENTRY_POINT(fid_p_funcdecl_4);
CR_DEFINE_ENTRY_POINT(fid_p_funcdecl_5);
CR_DEFINE_ENTRY_POINT(fid_p_funcdecl_6);
CR_DEFINE_ENTRY_POINT(fid_p_funcdecl_7);
CR_DEFINE_ENTRY_POINT(fid_p_funcdecl_8);
CR_DEFINE_ENTRY_POINT(fid_p_funcdecl_9);
CR_DEFINE_ENTRY_POINT(fid_parse_arglist);
CR_DEFINE_ENTRY_POINT(fid_p_arglist_1);
CR_DEFINE_ENTRY_POINT(fid_parse_member);
CR_DEFINE_ENTRY_POINT(fid_p_member_1);
CR_DEFINE_ENTRY_POINT(fid_parse_memberexpr);
CR_DEFINE_ENTRY_POINT(fid_p_memberexpr_1);
CR_DEFINE_ENTRY_POINT(fid_p_memberexpr_2);
CR_DEFINE_ENTRY_POINT(fid_parse_var);
CR_DEFINE_ENTRY_POINT(fid_p_var_1);
CR_DEFINE_ENTRY_POINT(fid_parse_prop);
#if V7_ENABLE_JS_GETTERS
CR_DEFINE_ENTRY_POINT(fid_p_prop_1_getter);
#endif
CR_DEFINE_ENTRY_POINT(fid_p_prop_2);
#if V7_ENABLE_JS_SETTERS
CR_DEFINE_ENTRY_POINT(fid_p_prop_3_setter);
#endif
CR_DEFINE_ENTRY_POINT(fid_p_prop_4);
CR_DEFINE_ENTRY_POINT(fid_parse_dowhile);
CR_DEFINE_ENTRY_POINT(fid_p_dowhile_1);
CR_DEFINE_ENTRY_POINT(fid_p_dowhile_2);
CR_DEFINE_ENTRY_POINT(fid_parse_for);
CR_DEFINE_ENTRY_POINT(fid_p_for_1);
CR_DEFINE_ENTRY_POINT(fid_p_for_2);
CR_DEFINE_ENTRY_POINT(fid_p_for_3);
CR_DEFINE_ENTRY_POINT(fid_p_for_4);
CR_DEFINE_ENTRY_POINT(fid_p_for_5);
CR_DEFINE_ENTRY_POINT(fid_p_for_6);
CR_DEFINE_ENTRY_POINT(fid_parse_try);
CR_DEFINE_ENTRY_POINT(fid_p_try_1);
CR_DEFINE_ENTRY_POINT(fid_p_try_2);
CR_DEFINE_ENTRY_POINT(fid_p_try_3);
CR_DEFINE_ENTRY_POINT(fid_p_try_4);
CR_DEFINE_ENTRY_POINT(fid_parse_switch);
CR_DEFINE_ENTRY_POINT(fid_p_switch_1);
CR_DEFINE_ENTRY_POINT(fid_p_switch_2);
CR_DEFINE_ENTRY_POINT(fid_p_switch_3);
CR_DEFINE_ENTRY_POINT(fid_p_switch_4);
CR_DEFINE_ENTRY_POINT(fid_parse_with);
CR_DEFINE_ENTRY_POINT(fid_p_with_1);
CR_DEFINE_ENTRY_POINT(fid_p_with_2);
default:
/* should never be here */
printf("fatal: wrong func id: %d", CR_CURR_FUNC());
break;
};
/* static enum v7_err parse_script(struct v7 *v7, struct ast *a) */
fid_parse_script :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_script_locals_t)
{
L->start = add_node(v7, a, AST_SCRIPT);
L->outer_last_var_node = v7->last_var_node;
L->saved_in_strict = v7->pstate.in_strict;
v7->last_var_node = L->start;
ast_modify_skip(a, L->start, L->start, AST_FUNC_FIRST_VAR_SKIP);
CR_TRY(fid_p_script_1);
{
CALL_PARSE_USE_STRICT(fid_p_script_3);
v7->pstate.in_strict = 1;
}
CR_CATCH(PARSER_EXC_ID__SYNTAX_ERROR, fid_p_script_1, fid_p_script_2);
CR_ENDCATCH(fid_p_script_2);
CALL_PARSE_BODY(TOK_END_OF_INPUT, fid_p_script_4);
ast_set_skip(a, L->start, AST_END_SKIP);
v7->pstate.in_strict = L->saved_in_strict;
v7->last_var_node = L->outer_last_var_node;
CR_RETURN_VOID();
}
/* static enum v7_err parse_use_strict(struct v7 *v7, struct ast *a) */
fid_parse_use_strict :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_use_strict_locals_t)
{
if (v7->cur_tok == TOK_STRING_LITERAL &&
(strncmp(v7->tok, "\"use strict\"", v7->tok_len) == 0 ||
strncmp(v7->tok, "'use strict'", v7->tok_len) == 0)) {
next_tok(v7);
add_node(v7, a, AST_USE_STRICT);
CR_RETURN_VOID();
} else {
CR_THROW(PARSER_EXC_ID__SYNTAX_ERROR);
}
}
/*
* static enum v7_err parse_body(struct v7 *v7, struct ast *a,
* enum v7_tok end)
*/
fid_parse_body :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_body_locals_t)
{
while (v7->cur_tok != L->arg.end) {
if (ACCEPT(TOK_FUNCTION)) {
if (v7->cur_tok != TOK_IDENTIFIER) {
CR_THROW(PARSER_EXC_ID__SYNTAX_ERROR);
}
L->start = add_node(v7, a, AST_VAR);
ast_modify_skip(a, v7->last_var_node, L->start, AST_FUNC_FIRST_VAR_SKIP);
/* zero out var node pointer */
ast_modify_skip(a, L->start, L->start, AST_FUNC_FIRST_VAR_SKIP);
v7->last_var_node = L->start;
add_inlined_node(v7, a, AST_FUNC_DECL, v7->tok, v7->tok_len);
CALL_PARSE_FUNCDECL(1, 0, fid_p_body_1);
ast_set_skip(a, L->start, AST_END_SKIP);
} else {
CALL_PARSE_STATEMENT(fid_p_body_2);
}
}
CR_RETURN_VOID();
}
/* static enum v7_err parse_statement(struct v7 *v7, struct ast *a) */
fid_parse_statement :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_statement_locals_t)
{
switch (v7->cur_tok) {
case TOK_SEMICOLON:
next_tok(v7);
/* empty statement */
CR_RETURN_VOID();
case TOK_OPEN_CURLY: /* block */
CALL_PARSE_BLOCK(fid_p_stat_3);
/* returning because no semicolon required */
CR_RETURN_VOID();
case TOK_IF:
next_tok(v7);
CALL_PARSE_IF(fid_p_stat_4);
CR_RETURN_VOID();
case TOK_WHILE:
next_tok(v7);
CALL_PARSE_WHILE(fid_p_stat_5);
CR_RETURN_VOID();
case TOK_DO:
next_tok(v7);
CALL_PARSE_DOWHILE(fid_p_stat_10);
CR_RETURN_VOID();
case TOK_FOR:
next_tok(v7);
CALL_PARSE_FOR(fid_p_stat_11);
CR_RETURN_VOID();
case TOK_TRY:
next_tok(v7);
CALL_PARSE_TRY(fid_p_stat_12);
CR_RETURN_VOID();
case TOK_SWITCH:
next_tok(v7);
CALL_PARSE_SWITCH(fid_p_stat_13);
CR_RETURN_VOID();
case TOK_WITH:
next_tok(v7);
CALL_PARSE_WITH(fid_p_stat_14);
CR_RETURN_VOID();
case TOK_BREAK:
if (!(v7->pstate.in_loop || v7->pstate.in_switch)) {
CR_THROW(PARSER_EXC_ID__SYNTAX_ERROR);
}
next_tok(v7);
PARSE_WITH_OPT_ARG(AST_BREAK, AST_LABELED_BREAK, CALL_PARSE_IDENT,
fid_p_stat_7);
break;
case TOK_CONTINUE:
if (!v7->pstate.in_loop) {
CR_THROW(PARSER_EXC_ID__SYNTAX_ERROR);
}
next_tok(v7);
PARSE_WITH_OPT_ARG(AST_CONTINUE, AST_LABELED_CONTINUE, CALL_PARSE_IDENT,
fid_p_stat_8);
break;
case TOK_RETURN:
if (!v7->pstate.in_function) {
CR_THROW(PARSER_EXC_ID__SYNTAX_ERROR);
}
next_tok(v7);
PARSE_WITH_OPT_ARG(AST_RETURN, AST_VALUE_RETURN, CALL_PARSE_EXPRESSION,
fid_p_stat_6);
break;
case TOK_THROW:
next_tok(v7);
add_node(v7, a, AST_THROW);
CALL_PARSE_EXPRESSION(fid_p_stat_2);
break;
case TOK_DEBUGGER:
next_tok(v7);
add_node(v7, a, AST_DEBUGGER);
break;
case TOK_VAR:
next_tok(v7);
CALL_PARSE_VAR(fid_p_stat_9);
break;
case TOK_IDENTIFIER:
if (lookahead(v7) == TOK_COLON) {
add_inlined_node(v7, a, AST_LABEL, v7->tok, v7->tok_len);
next_tok(v7);
EXPECT(TOK_COLON);
CR_RETURN_VOID();
}
/* fall through */
default:
CALL_PARSE_EXPRESSION(fid_p_stat_1);
break;
}
if (end_of_statement(v7) != V7_OK) {
CR_THROW(PARSER_EXC_ID__SYNTAX_ERROR);
}
ACCEPT(TOK_SEMICOLON); /* swallow optional semicolon */
CR_RETURN_VOID();
}
/* static enum v7_err parse_expression(struct v7 *v7, struct ast *a) */
fid_parse_expression :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_expression_locals_t)
{
L->pos = a->mbuf.len;
L->group = 0;
while (1) {
CALL_PARSE_ASSIGN(fid_p_expr_1);
if (ACCEPT(TOK_COMMA)) {
L->group = 1;
} else {
break;
}
}
if (L->group) {
insert_node(v7, a, L->pos, AST_SEQ);
}
CR_RETURN_VOID();
}
/* static enum v7_err parse_assign(struct v7 *v7, struct ast *a) */
fid_parse_assign :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_assign_locals_t)
{
CALL_PARSE_BINARY(0, a->mbuf.len, fid_p_assign_1);
CR_RETURN_VOID();
}
/*
* static enum v7_err parse_binary(struct v7 *v7, struct ast *a, int level,
* ast_off_t pos)
*/
#if 1
fid_parse_binary :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_binary_locals_t)
{
/*
* Note: we use macro CUR_POS instead of a local variable, since this
* function is called really a lot, so, each byte on stack frame counts.
*
* It will work a bit slower of course, but slowness is not a problem
*/
#define CUR_POS ((L->level > L->arg.min_level) ? L->saved_mbuf_len : L->arg.pos)
L->saved_mbuf_len = a->mbuf.len;
CALL_PARSE_PREFIX(fid_p_binary_6);
for (L->level = (int) ARRAY_SIZE(levels) - 1; L->level >= L->arg.min_level;
L->level--) {
for (L->i = 0; L->i < levels[L->level].len; L->i++) {
L->tok = levels[L->level].parts[L->i].start_tok;
L->ast = levels[L->level].parts[L->i].start_ast;
do {
if (v7->pstate.inhibit_in && L->tok == TOK_IN) {
continue;
}
/*
* Ternary operator sits in the middle of the binary operator
* precedence chain. Deal with it as an exception and don't break
* the chain.
*/
if (L->tok == TOK_QUESTION && v7->cur_tok == TOK_QUESTION) {
next_tok(v7);
CALL_PARSE_ASSIGN(fid_p_binary_2);
EXPECT(TOK_COLON);
CALL_PARSE_ASSIGN(fid_p_binary_3);
insert_node(v7, a, CUR_POS, AST_COND);
CR_RETURN_VOID();
} else if (ACCEPT(L->tok)) {
if (levels[L->level].left_to_right) {
insert_node(v7, a, CUR_POS, (enum ast_tag) L->ast);
CALL_PARSE_BINARY(L->level, CUR_POS, fid_p_binary_4);
} else {
CALL_PARSE_BINARY(L->level, a->mbuf.len, fid_p_binary_5);
insert_node(v7, a, CUR_POS, (enum ast_tag) L->ast);
}
}
} while (L->ast = (enum ast_tag)(L->ast + 1),
L->tok < levels[L->level].parts[L->i].end_tok &&
(L->tok = (enum v7_tok)(L->tok + 1)));
}
}
CR_RETURN_VOID();
#undef CUR_POS
}
#endif
/* enum v7_err parse_prefix(struct v7 *v7, struct ast *a) */
fid_parse_prefix :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_prefix_locals_t)
{
for (;;) {
switch (v7->cur_tok) {
case TOK_PLUS:
next_tok(v7);
add_node(v7, a, AST_POSITIVE);
break;
case TOK_MINUS:
next_tok(v7);
add_node(v7, a, AST_NEGATIVE);
break;
case TOK_PLUS_PLUS:
next_tok(v7);
add_node(v7, a, AST_PREINC);
break;
case TOK_MINUS_MINUS:
next_tok(v7);
add_node(v7, a, AST_PREDEC);
break;
case TOK_TILDA:
next_tok(v7);
add_node(v7, a, AST_NOT);
break;
case TOK_NOT:
next_tok(v7);
add_node(v7, a, AST_LOGICAL_NOT);
break;
case TOK_VOID:
next_tok(v7);
add_node(v7, a, AST_VOID);
break;
case TOK_DELETE:
next_tok(v7);
add_node(v7, a, AST_DELETE);
break;
case TOK_TYPEOF:
next_tok(v7);
add_node(v7, a, AST_TYPEOF);
break;
default:
CALL_PARSE_POSTFIX(fid_p_prefix_1);
CR_RETURN_VOID();
}
}
}
/* static enum v7_err parse_postfix(struct v7 *v7, struct ast *a) */
fid_parse_postfix :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_postfix_locals_t)
{
L->pos = a->mbuf.len;
CALL_PARSE_CALLEXPR(fid_p_postfix_1);
if (v7->after_newline) {
CR_RETURN_VOID();
}
switch (v7->cur_tok) {
case TOK_PLUS_PLUS:
next_tok(v7);
insert_node(v7, a, L->pos, AST_POSTINC);
break;
case TOK_MINUS_MINUS:
next_tok(v7);
insert_node(v7, a, L->pos, AST_POSTDEC);
break;
default:
break; /* nothing */
}
CR_RETURN_VOID();
}
/* static enum v7_err parse_callexpr(struct v7 *v7, struct ast *a) */
fid_parse_callexpr :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_callexpr_locals_t)
{
L->pos = a->mbuf.len;
CALL_PARSE_NEWEXPR(fid_p_callexpr_1);
for (;;) {
switch (v7->cur_tok) {
case TOK_DOT:
case TOK_OPEN_BRACKET:
CALL_PARSE_MEMBER(L->pos, fid_p_callexpr_3);
break;
case TOK_OPEN_PAREN:
next_tok(v7);
CALL_PARSE_ARGLIST(fid_p_callexpr_2);
EXPECT(TOK_CLOSE_PAREN);
insert_node(v7, a, L->pos, AST_CALL);
break;
default:
CR_RETURN_VOID();
}
}
}
/* static enum v7_err parse_newexpr(struct v7 *v7, struct ast *a) */
fid_parse_newexpr :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_newexpr_locals_t)
{
switch (v7->cur_tok) {
case TOK_NEW:
next_tok(v7);
L->start = add_node(v7, a, AST_NEW);
CALL_PARSE_MEMBEREXPR(fid_p_newexpr_3);
if (ACCEPT(TOK_OPEN_PAREN)) {
CALL_PARSE_ARGLIST(fid_p_newexpr_4);
EXPECT(TOK_CLOSE_PAREN);
}
ast_set_skip(a, L->start, AST_END_SKIP);
break;
case TOK_FUNCTION:
next_tok(v7);
CALL_PARSE_FUNCDECL(0, 0, fid_p_newexpr_2);
break;
default:
CALL_PARSE_TERMINAL(fid_p_newexpr_1);
break;
}
CR_RETURN_VOID();
}
/* static enum v7_err parse_terminal(struct v7 *v7, struct ast *a) */
fid_parse_terminal :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_terminal_locals_t)
{
switch (v7->cur_tok) {
case TOK_OPEN_PAREN:
next_tok(v7);
CALL_PARSE_EXPRESSION(fid_p_terminal_1);
EXPECT(TOK_CLOSE_PAREN);
break;
case TOK_OPEN_BRACKET:
next_tok(v7);
L->start = add_node(v7, a, AST_ARRAY);
while (v7->cur_tok != TOK_CLOSE_BRACKET) {
if (v7->cur_tok == TOK_COMMA) {
/* Array literals allow missing elements, e.g. [,,1,] */
add_node(v7, a, AST_NOP);
} else {
CALL_PARSE_ASSIGN(fid_p_terminal_2);
}
ACCEPT(TOK_COMMA);
}
EXPECT(TOK_CLOSE_BRACKET);
ast_set_skip(a, L->start, AST_END_SKIP);
break;
case TOK_OPEN_CURLY:
next_tok(v7);
L->start = add_node(v7, a, AST_OBJECT);
if (v7->cur_tok != TOK_CLOSE_CURLY) {
do {
if (v7->cur_tok == TOK_CLOSE_CURLY) {
break;
}
CALL_PARSE_PROP(fid_p_terminal_3);
} while (ACCEPT(TOK_COMMA));
}
EXPECT(TOK_CLOSE_CURLY);
ast_set_skip(a, L->start, AST_END_SKIP);
break;
case TOK_THIS:
next_tok(v7);
add_node(v7, a, AST_THIS);
break;
case TOK_TRUE:
next_tok(v7);
add_node(v7, a, AST_TRUE);
break;
case TOK_FALSE:
next_tok(v7);
add_node(v7, a, AST_FALSE);
break;
case TOK_NULL:
next_tok(v7);
add_node(v7, a, AST_NULL);
break;
case TOK_STRING_LITERAL:
add_inlined_node(v7, a, AST_STRING, v7->tok + 1, v7->tok_len - 2);
next_tok(v7);
break;
case TOK_NUMBER:
add_inlined_node(v7, a, AST_NUM, v7->tok, v7->tok_len);
next_tok(v7);
break;
case TOK_REGEX_LITERAL:
add_inlined_node(v7, a, AST_REGEX, v7->tok, v7->tok_len);
next_tok(v7);
break;
case TOK_IDENTIFIER:
if (v7->tok_len == 9 && strncmp(v7->tok, "undefined", v7->tok_len) == 0) {
add_node(v7, a, AST_UNDEFINED);
next_tok(v7);
break;
}
/* fall through */
default:
CALL_PARSE_IDENT(fid_p_terminal_4);
}
CR_RETURN_VOID();
}
/* static enum v7_err parse_block(struct v7 *v7, struct ast *a) */
fid_parse_block :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_block_locals_t)
{
EXPECT(TOK_OPEN_CURLY);
CALL_PARSE_BODY(TOK_CLOSE_CURLY, fid_p_block_1);
EXPECT(TOK_CLOSE_CURLY);
CR_RETURN_VOID();
}
/* static enum v7_err parse_if(struct v7 *v7, struct ast *a) */
fid_parse_if :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_if_locals_t)
{
L->start = add_node(v7, a, AST_IF);
EXPECT(TOK_OPEN_PAREN);
CALL_PARSE_EXPRESSION(fid_p_if_1);
EXPECT(TOK_CLOSE_PAREN);
CALL_PARSE_STATEMENT(fid_p_if_2);
ast_set_skip(a, L->start, AST_END_IF_TRUE_SKIP);
if (ACCEPT(TOK_ELSE)) {
CALL_PARSE_STATEMENT(fid_p_if_3);
}
ast_set_skip(a, L->start, AST_END_SKIP);
CR_RETURN_VOID();
}
/* static enum v7_err parse_while(struct v7 *v7, struct ast *a) */
fid_parse_while :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_while_locals_t)
{
L->start = add_node(v7, a, AST_WHILE);
L->saved_in_loop = v7->pstate.in_loop;
EXPECT(TOK_OPEN_PAREN);
CALL_PARSE_EXPRESSION(fid_p_while_1);
EXPECT(TOK_CLOSE_PAREN);
v7->pstate.in_loop = 1;
CALL_PARSE_STATEMENT(fid_p_while_2);
ast_set_skip(a, L->start, AST_END_SKIP);
v7->pstate.in_loop = L->saved_in_loop;
CR_RETURN_VOID();
}
/* static enum v7_err parse_ident(struct v7 *v7, struct ast *a) */
fid_parse_ident :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_ident_locals_t)
{
if (v7->cur_tok == TOK_IDENTIFIER) {
add_inlined_node(v7, a, AST_IDENT, v7->tok, v7->tok_len);
next_tok(v7);
CR_RETURN_VOID();
}
CR_THROW(PARSER_EXC_ID__SYNTAX_ERROR);
}
/*
* static enum v7_err parse_ident_allow_reserved_words(struct v7 *v7,
* struct ast *a)
*
*/
fid_parse_ident_allow_reserved_words :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_ident_allow_reserved_words_locals_t)
{
/* Allow reserved words as property names. */
if (is_reserved_word_token(v7->cur_tok)) {
add_inlined_node(v7, a, AST_IDENT, v7->tok, v7->tok_len);
next_tok(v7);
} else {
CALL_PARSE_IDENT(fid_p_ident_arw_1);
}
CR_RETURN_VOID();
}
/* static enum v7_err parse_funcdecl(struct v7 *, struct ast *, int, int) */
fid_parse_funcdecl :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_funcdecl_locals_t)
{
L->start = add_node(v7, a, AST_FUNC);
L->outer_last_var_node = v7->last_var_node;
L->saved_in_function = v7->pstate.in_function;
L->saved_in_strict = v7->pstate.in_strict;
v7->last_var_node = L->start;
ast_modify_skip(a, L->start, L->start, AST_FUNC_FIRST_VAR_SKIP);
CR_TRY(fid_p_funcdecl_2);
{
if (L->arg.reserved_name) {
CALL_PARSE_IDENT_ALLOW_RESERVED_WORDS(fid_p_funcdecl_1);
} else {
CALL_PARSE_IDENT(fid_p_funcdecl_9);
}
}
CR_CATCH(PARSER_EXC_ID__SYNTAX_ERROR, fid_p_funcdecl_2, fid_p_funcdecl_3);
{
if (L->arg.require_named) {
/* function name is required, so, rethrow SYNTAX ERROR */
CR_THROW(PARSER_EXC_ID__SYNTAX_ERROR);
} else {
/* it's ok not to have a function name, just insert NOP */
add_node(v7, a, AST_NOP);
}
}
CR_ENDCATCH(fid_p_funcdecl_3);
EXPECT(TOK_OPEN_PAREN);
CALL_PARSE_ARGLIST(fid_p_funcdecl_4);
EXPECT(TOK_CLOSE_PAREN);
ast_set_skip(a, L->start, AST_FUNC_BODY_SKIP);
v7->pstate.in_function = 1;
EXPECT(TOK_OPEN_CURLY);
CR_TRY(fid_p_funcdecl_5);
{
CALL_PARSE_USE_STRICT(fid_p_funcdecl_7);
v7->pstate.in_strict = 1;
}
CR_CATCH(PARSER_EXC_ID__SYNTAX_ERROR, fid_p_funcdecl_5, fid_p_funcdecl_6);
CR_ENDCATCH(fid_p_funcdecl_6);
CALL_PARSE_BODY(TOK_CLOSE_CURLY, fid_p_funcdecl_8);
EXPECT(TOK_CLOSE_CURLY);
v7->pstate.in_strict = L->saved_in_strict;
v7->pstate.in_function = L->saved_in_function;
ast_set_skip(a, L->start, AST_END_SKIP);
v7->last_var_node = L->outer_last_var_node;
CR_RETURN_VOID();
}
/* static enum v7_err parse_arglist(struct v7 *v7, struct ast *a) */
fid_parse_arglist :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_arglist_locals_t)
{
if (v7->cur_tok != TOK_CLOSE_PAREN) {
do {
CALL_PARSE_ASSIGN(fid_p_arglist_1);
} while (ACCEPT(TOK_COMMA));
}
CR_RETURN_VOID();
}
/*
* static enum v7_err parse_member(struct v7 *v7, struct ast *a, ast_off_t pos)
*/
fid_parse_member :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_member_locals_t)
{
switch (v7->cur_tok) {
case TOK_DOT:
next_tok(v7);
/* Allow reserved words as member identifiers */
if (is_reserved_word_token(v7->cur_tok) ||
v7->cur_tok == TOK_IDENTIFIER) {
insert_inlined_node(v7, a, L->arg.pos, AST_MEMBER, v7->tok,
v7->tok_len);
next_tok(v7);
} else {
CR_THROW(PARSER_EXC_ID__SYNTAX_ERROR);
}
break;
case TOK_OPEN_BRACKET:
next_tok(v7);
CALL_PARSE_EXPRESSION(fid_p_member_1);
EXPECT(TOK_CLOSE_BRACKET);
insert_node(v7, a, L->arg.pos, AST_INDEX);
break;
default:
CR_RETURN_VOID();
}
/* not necessary, but let it be anyway */
CR_RETURN_VOID();
}
/* static enum v7_err parse_memberexpr(struct v7 *v7, struct ast *a) */
fid_parse_memberexpr :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_memberexpr_locals_t)
{
L->pos = a->mbuf.len;
CALL_PARSE_NEWEXPR(fid_p_memberexpr_1);
for (;;) {
switch (v7->cur_tok) {
case TOK_DOT:
case TOK_OPEN_BRACKET:
CALL_PARSE_MEMBER(L->pos, fid_p_memberexpr_2);
break;
default:
CR_RETURN_VOID();
}
}
}
/* static enum v7_err parse_var(struct v7 *v7, struct ast *a) */
fid_parse_var :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_var_locals_t)
{
L->start = add_node(v7, a, AST_VAR);
ast_modify_skip(a, v7->last_var_node, L->start, AST_FUNC_FIRST_VAR_SKIP);
/* zero out var node pointer */
ast_modify_skip(a, L->start, L->start, AST_FUNC_FIRST_VAR_SKIP);
v7->last_var_node = L->start;
do {
add_inlined_node(v7, a, AST_VAR_DECL, v7->tok, v7->tok_len);
EXPECT(TOK_IDENTIFIER);
if (ACCEPT(TOK_ASSIGN)) {
CALL_PARSE_ASSIGN(fid_p_var_1);
} else {
add_node(v7, a, AST_NOP);
}
} while (ACCEPT(TOK_COMMA));
ast_set_skip(a, L->start, AST_END_SKIP);
CR_RETURN_VOID();
}
/* static enum v7_err parse_prop(struct v7 *v7, struct ast *a) */
fid_parse_prop :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_prop_locals_t)
{
#if V7_ENABLE_JS_GETTERS
if (v7->cur_tok == TOK_IDENTIFIER && v7->tok_len == 3 &&
strncmp(v7->tok, "get", v7->tok_len) == 0 && lookahead(v7) != TOK_COLON) {
next_tok(v7);
add_node(v7, a, AST_GETTER);
CALL_PARSE_FUNCDECL(1, 1, fid_p_prop_1_getter);
} else
#endif
if (v7->cur_tok == TOK_IDENTIFIER && lookahead(v7) == TOK_OPEN_PAREN) {
/* ecmascript 6 feature */
CALL_PARSE_FUNCDECL(1, 1, fid_p_prop_2);
#if V7_ENABLE_JS_SETTERS
} else if (v7->cur_tok == TOK_IDENTIFIER && v7->tok_len == 3 &&
strncmp(v7->tok, "set", v7->tok_len) == 0 &&
lookahead(v7) != TOK_COLON) {
next_tok(v7);
add_node(v7, a, AST_SETTER);
CALL_PARSE_FUNCDECL(1, 1, fid_p_prop_3_setter);
#endif
} else {
/* Allow reserved words as property names. */
if (is_reserved_word_token(v7->cur_tok) || v7->cur_tok == TOK_IDENTIFIER ||
v7->cur_tok == TOK_NUMBER) {
add_inlined_node(v7, a, AST_PROP, v7->tok, v7->tok_len);
} else if (v7->cur_tok == TOK_STRING_LITERAL) {
add_inlined_node(v7, a, AST_PROP, v7->tok + 1, v7->tok_len - 2);
} else {
CR_THROW(PARSER_EXC_ID__SYNTAX_ERROR);
}
next_tok(v7);
EXPECT(TOK_COLON);
CALL_PARSE_ASSIGN(fid_p_prop_4);
}
CR_RETURN_VOID();
}
/* static enum v7_err parse_dowhile(struct v7 *v7, struct ast *a) */
fid_parse_dowhile :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_dowhile_locals_t)
{
L->start = add_node(v7, a, AST_DOWHILE);
L->saved_in_loop = v7->pstate.in_loop;
v7->pstate.in_loop = 1;
CALL_PARSE_STATEMENT(fid_p_dowhile_1);
v7->pstate.in_loop = L->saved_in_loop;
ast_set_skip(a, L->start, AST_DO_WHILE_COND_SKIP);
EXPECT(TOK_WHILE);
EXPECT(TOK_OPEN_PAREN);
CALL_PARSE_EXPRESSION(fid_p_dowhile_2);
EXPECT(TOK_CLOSE_PAREN);
ast_set_skip(a, L->start, AST_END_SKIP);
CR_RETURN_VOID();
}
/* static enum v7_err parse_for(struct v7 *v7, struct ast *a) */
fid_parse_for :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_for_locals_t)
{
/* TODO(mkm): for of, for each in */
/*
ast_off_t start;
int saved_in_loop;
*/
L->start = add_node(v7, a, AST_FOR);
L->saved_in_loop = v7->pstate.in_loop;
EXPECT(TOK_OPEN_PAREN);
if (parse_optional(v7, a, TOK_SEMICOLON)) {
/*
* TODO(mkm): make this reentrant otherwise this pearl won't parse:
* for((function(){return 1 in o.a ? o : x})().a in [1,2,3])
*/
v7->pstate.inhibit_in = 1;
if (ACCEPT(TOK_VAR)) {
CALL_PARSE_VAR(fid_p_for_1);
} else {
CALL_PARSE_EXPRESSION(fid_p_for_2);
}
v7->pstate.inhibit_in = 0;
if (ACCEPT(TOK_IN)) {
CALL_PARSE_EXPRESSION(fid_p_for_3);
add_node(v7, a, AST_NOP);
/*
* Assumes that for and for in have the same AST format which is
* suboptimal but avoids the need of fixing up the var offset chain.
* TODO(mkm) improve this
*/
ast_modify_tag(a, L->start - 1, AST_FOR_IN);
goto for_loop_body;
}
}
EXPECT(TOK_SEMICOLON);
if (parse_optional(v7, a, TOK_SEMICOLON)) {
CALL_PARSE_EXPRESSION(fid_p_for_4);
}
EXPECT(TOK_SEMICOLON);
if (parse_optional(v7, a, TOK_CLOSE_PAREN)) {
CALL_PARSE_EXPRESSION(fid_p_for_5);
}
for_loop_body:
EXPECT(TOK_CLOSE_PAREN);
ast_set_skip(a, L->start, AST_FOR_BODY_SKIP);
v7->pstate.in_loop = 1;
CALL_PARSE_STATEMENT(fid_p_for_6);
v7->pstate.in_loop = L->saved_in_loop;
ast_set_skip(a, L->start, AST_END_SKIP);
CR_RETURN_VOID();
}
/* static enum v7_err parse_try(struct v7 *v7, struct ast *a) */
fid_parse_try :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_try_locals_t)
{
L->start = add_node(v7, a, AST_TRY);
L->catch_or_finally = 0;
CALL_PARSE_BLOCK(fid_p_try_1);
ast_set_skip(a, L->start, AST_TRY_CATCH_SKIP);
if (ACCEPT(TOK_CATCH)) {
L->catch_or_finally = 1;
EXPECT(TOK_OPEN_PAREN);
CALL_PARSE_IDENT(fid_p_try_2);
EXPECT(TOK_CLOSE_PAREN);
CALL_PARSE_BLOCK(fid_p_try_3);
}
ast_set_skip(a, L->start, AST_TRY_FINALLY_SKIP);
if (ACCEPT(TOK_FINALLY)) {
L->catch_or_finally = 1;
CALL_PARSE_BLOCK(fid_p_try_4);
}
ast_set_skip(a, L->start, AST_END_SKIP);
/* make sure `catch` and `finally` aren't both missing */
if (!L->catch_or_finally) {
CR_THROW(PARSER_EXC_ID__SYNTAX_ERROR);
}
CR_RETURN_VOID();
}
/* static enum v7_err parse_switch(struct v7 *v7, struct ast *a) */
fid_parse_switch :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_switch_locals_t)
{
L->start = add_node(v7, a, AST_SWITCH);
L->saved_in_switch = v7->pstate.in_switch;
ast_set_skip(a, L->start, AST_SWITCH_DEFAULT_SKIP); /* clear out */
EXPECT(TOK_OPEN_PAREN);
CALL_PARSE_EXPRESSION(fid_p_switch_1);
EXPECT(TOK_CLOSE_PAREN);
EXPECT(TOK_OPEN_CURLY);
v7->pstate.in_switch = 1;
while (v7->cur_tok != TOK_CLOSE_CURLY) {
switch (v7->cur_tok) {
case TOK_CASE:
next_tok(v7);
L->case_start = add_node(v7, a, AST_CASE);
CALL_PARSE_EXPRESSION(fid_p_switch_2);
EXPECT(TOK_COLON);
while (v7->cur_tok != TOK_CASE && v7->cur_tok != TOK_DEFAULT &&
v7->cur_tok != TOK_CLOSE_CURLY) {
CALL_PARSE_STATEMENT(fid_p_switch_3);
}
ast_set_skip(a, L->case_start, AST_END_SKIP);
break;
case TOK_DEFAULT:
next_tok(v7);
EXPECT(TOK_COLON);
ast_set_skip(a, L->start, AST_SWITCH_DEFAULT_SKIP);
L->case_start = add_node(v7, a, AST_DEFAULT);
while (v7->cur_tok != TOK_CASE && v7->cur_tok != TOK_DEFAULT &&
v7->cur_tok != TOK_CLOSE_CURLY) {
CALL_PARSE_STATEMENT(fid_p_switch_4);
}
ast_set_skip(a, L->case_start, AST_END_SKIP);
break;
default:
CR_THROW(PARSER_EXC_ID__SYNTAX_ERROR);
}
}
EXPECT(TOK_CLOSE_CURLY);
ast_set_skip(a, L->start, AST_END_SKIP);
v7->pstate.in_switch = L->saved_in_switch;
CR_RETURN_VOID();
}
/* static enum v7_err parse_with(struct v7 *v7, struct ast *a) */
fid_parse_with :
#undef L
#define L CR_CUR_LOCALS_PT(fid_parse_with_locals_t)
{
L->start = add_node(v7, a, AST_WITH);
if (v7->pstate.in_strict) {
CR_THROW(PARSER_EXC_ID__SYNTAX_ERROR);
}
EXPECT(TOK_OPEN_PAREN);
CALL_PARSE_EXPRESSION(fid_p_with_1);
EXPECT(TOK_CLOSE_PAREN);
CALL_PARSE_STATEMENT(fid_p_with_2);
ast_set_skip(a, L->start, AST_END_SKIP);
CR_RETURN_VOID();
}
fid_none:
/* stack is empty, so we're done; return */
return rc;
}
V7_PRIVATE enum v7_err parse(struct v7 *v7, struct ast *a, const char *src,
size_t src_len, int is_json) {
enum v7_err rcode;
const char *error_msg = NULL;
const char *p;
struct cr_ctx cr_ctx;
union user_arg_ret arg_retval;
enum cr_status rc;
#if V7_ENABLE_STACK_TRACKING
struct stack_track_ctx stack_track_ctx;
#endif
int saved_line_no = v7->line_no;
#if V7_ENABLE_STACK_TRACKING
v7_stack_track_start(v7, &stack_track_ctx);
#endif
v7->pstate.source_code = v7->pstate.pc = src;
v7->pstate.src_end = src + src_len;
v7->pstate.file_name = "";
v7->pstate.line_no = 1;
v7->pstate.in_function = 0;
v7->pstate.in_loop = 0;
v7->pstate.in_switch = 0;
/*
* TODO(dfrank): `v7->parser.line_no` vs `v7->line_no` is confusing. probaby
* we need to refactor it.
*
* See comment for v7->line_no in core.h for some details.
*/
v7->line_no = 1;
next_tok(v7);
/*
* setup initial state for "after newline" tracking.
* next_tok will consume our token and position the current line
* position at the beginning of the next token.
* While processing the first token, both the leading and the
* trailing newlines will be counted and thus it will create a spurious
* "after newline" condition at the end of the first token
* regardless if there is actually a newline after it.
*/
for (p = src; isspace((int) *p); p++) {
if (*p == '\n') {
v7->pstate.prev_line_no++;
}
}
/* init cr context */
cr_context_init(&cr_ctx, &arg_retval, sizeof(arg_retval), _fid_descrs);
/* prepare first function call: fid_mul_sum */
if (is_json) {
CR_FIRST_CALL_PREPARE_C(&cr_ctx, fid_parse_terminal);
} else {
CR_FIRST_CALL_PREPARE_C(&cr_ctx, fid_parse_script);
}
/* proceed to coroutine execution */
rc = parser_cr_exec(&cr_ctx, v7, a);
/* set `rcode` depending on coroutine state */
switch (rc) {
case CR_RES__OK:
rcode = V7_OK;
break;
case CR_RES__ERR_UNCAUGHT_EXCEPTION:
switch ((enum parser_exc_id) CR_THROWN_C(&cr_ctx)) {
case PARSER_EXC_ID__SYNTAX_ERROR:
rcode = V7_SYNTAX_ERROR;
error_msg = "Syntax error";
break;
default:
rcode = V7_INTERNAL_ERROR;
error_msg = "Internal error: no exception id set";
break;
}
break;
default:
rcode = V7_INTERNAL_ERROR;
error_msg = "Internal error: unexpected parser coroutine return code";
break;
}
#if defined(V7_TRACK_MAX_PARSER_STACK_SIZE)
/* remember how much stack space was consumed */
if (v7->parser_stack_data_max_size < cr_ctx.stack_data.size) {
v7->parser_stack_data_max_size = cr_ctx.stack_data.size;
#ifndef NO_LIBC
printf("parser_stack_data_max_size=%u\n",
(unsigned int) v7->parser_stack_data_max_size);
#endif
}
if (v7->parser_stack_ret_max_size < cr_ctx.stack_ret.size) {
v7->parser_stack_ret_max_size = cr_ctx.stack_ret.size;
#ifndef NO_LIBC
printf("parser_stack_ret_max_size=%u\n",
(unsigned int) v7->parser_stack_ret_max_size);
#endif
}
#if defined(CR_TRACK_MAX_STACK_LEN)
if (v7->parser_stack_data_max_len < cr_ctx.stack_data_max_len) {
v7->parser_stack_data_max_len = cr_ctx.stack_data_max_len;
#ifndef NO_LIBC
printf("parser_stack_data_max_len=%u\n",
(unsigned int) v7->parser_stack_data_max_len);
#endif
}
if (v7->parser_stack_ret_max_len < cr_ctx.stack_ret_max_len) {
v7->parser_stack_ret_max_len = cr_ctx.stack_ret_max_len;
#ifndef NO_LIBC
printf("parser_stack_ret_max_len=%u\n",
(unsigned int) v7->parser_stack_ret_max_len);
#endif
}
#endif
#endif
/* free resources occupied by context (at least, "stack" arrays) */
cr_context_free(&cr_ctx);
#if V7_ENABLE_STACK_TRACKING
{
int diff = v7_stack_track_end(v7, &stack_track_ctx);
if (diff > v7->stack_stat[V7_STACK_STAT_PARSER]) {
v7->stack_stat[V7_STACK_STAT_PARSER] = diff;
}
}
#endif
/* Check if AST was overflown */
if (a->has_overflow) {
rcode = v7_throwf(v7, SYNTAX_ERROR,
"Script too large (try V7_LARGE_AST build option)");
V7_THROW(V7_AST_TOO_LARGE);
}
if (rcode == V7_OK && v7->cur_tok != TOK_END_OF_INPUT) {
rcode = V7_SYNTAX_ERROR;
error_msg = "Syntax error";
}
if (rcode != V7_OK) {
unsigned long col = get_column(v7->pstate.source_code, v7->tok);
int line_len = 0;
assert(error_msg != NULL);
for (p = v7->tok - col; p < v7->pstate.src_end && *p != '\0' && *p != '\n';
p++) {
line_len++;
}
/* fixup line number: line_no points to the beginning of the next token */
for (; p < v7->pstate.pc; p++) {
if (*p == '\n') {
v7->pstate.line_no--;
}
}
/*
* We already have a proper `rcode`, that's why we discard returned value
* of `v7_throwf()`, which is always `V7_EXEC_EXCEPTION`.
*
* TODO(dfrank): probably get rid of distinct error types, and use just
* `V7_JS_EXCEPTION`. However it would be good to have a way to get exact
* error type, so probably error object should contain some property with
* error code, but it would make exceptions even more expensive, etc, etc.
*/
{
enum v7_err _tmp;
_tmp = v7_throwf(v7, SYNTAX_ERROR, "%s at line %d col %lu:\n%.*s\n%*s^",
error_msg, v7->pstate.line_no, col, line_len,
v7->tok - col, (int) col - 1, "");
(void) _tmp;
}
V7_THROW(rcode);
}
clean:
v7->line_no = saved_line_no;
return rcode;
}
#endif /* V7_NO_COMPILER */
#ifdef V7_MODULE_LINES
#line 1 "v7/src/compiler.c"
#endif
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "v7/src/internal.h" */
/* Amalgamated: #include "v7/src/compiler.h" */
/* Amalgamated: #include "v7/src/std_error.h" */
/* Amalgamated: #include "v7/src/core.h" */
/* Amalgamated: #include "v7/src/function.h" */
/* Amalgamated: #include "v7/src/exceptions.h" */
/* Amalgamated: #include "v7/src/conversion.h" */
/* Amalgamated: #include "v7/src/regexp.h" */
#if !defined(V7_NO_COMPILER)
/*
* The bytecode compiler takes an AST as input and produces one or more
* bcode structure as output.
*
* Each script or function body is compiled into it's own bcode structure.
*
* Each bcode stream produces a new value on the stack, i.e. its overall
* stack diagram is: `( -- a)`
*
* This value will be then popped by the function caller or by v7_exec in case
* of scripts.
*
* In JS, the value of a script is the value of the last statement.
* A script with no statement has an `undefined` value.
* Functions instead require an explicit return value, so this matters only
* for `v7_exec` and JS `eval`.
*
* Since an empty script has an undefined value, and each script has to
* yield a value, the script/function prologue consists of a PUSH_UNDEFINED.
*
* Each statement will be compiled to push a value on the stack.
* When a statement begins evaluating, the current TOS is thus either
* the value of the previous statement or `undefined` in case of the first
* statement.
*
* Every statement of a given script/function body always evaluates at the same
* stack depth.
*
* In order to achieve that, after a statement is compiled out, a SWAP_DROP
* opcode is emitted, that drops the value of the previous statement (or the
* initial `undefined`). Dropping the value after the next statement is
* evaluated and not before has allows us to correctly implement exception
* behaviour and the break statement.
*
* Compound statements are constructs such as `if`/`while`/`for`/`try`. These
* constructs contain a body consisting of a possibly empty statement list.
*
* Unlike normal statements, compound statements don't produce a value
* themselves. Their value is either the value of their last executed statement
* in their body, or the previous statement in case their body is empty or not
* evaluated at all.
*
* An example is:
*
* [source,js]
* ----
* try {
* 42;
* someUnexistingVariable;
* } catch(e) {
* while(true) {}
* if(true) {
* }
* if(false) {
* 2;
* }
* break;
* }
* }
* ----
*/
static const enum ast_tag assign_ast_map[] = {
AST_REM, AST_MUL, AST_DIV, AST_XOR, AST_ADD, AST_SUB,
AST_OR, AST_AND, AST_LSHIFT, AST_RSHIFT, AST_URSHIFT};
#ifdef V7_BCODE_DUMP
extern void dump_bcode(struct v7 *v7, FILE *f, struct bcode *bcode);
#endif
V7_PRIVATE enum v7_err compile_expr_builder(struct bcode_builder *bbuilder,
struct ast *a, ast_off_t *ppos);
V7_PRIVATE enum v7_err compile_function(struct v7 *v7, struct ast *a,
ast_off_t *ppos, struct bcode *bcode);
V7_PRIVATE enum v7_err binary_op(struct bcode_builder *bbuilder,
enum ast_tag tag) {
uint8_t op;
enum v7_err rcode = V7_OK;
struct v7 *v7 = bbuilder->v7;
switch (tag) {
case AST_ADD:
op = OP_ADD;
break;
case AST_SUB:
op = OP_SUB;
break;
case AST_REM:
op = OP_REM;
break;
case AST_MUL:
op = OP_MUL;
break;
case AST_DIV:
op = OP_DIV;
break;
case AST_LSHIFT:
op = OP_LSHIFT;
break;
case AST_RSHIFT:
op = OP_RSHIFT;
break;
case AST_URSHIFT:
op = OP_URSHIFT;
break;
case AST_OR:
op = OP_OR;
break;
case AST_XOR:
op = OP_XOR;
break;
case AST_AND:
op = OP_AND;
break;
case AST_EQ_EQ:
op = OP_EQ_EQ;
break;
case AST_EQ:
op = OP_EQ;
break;
case AST_NE:
op = OP_NE;
break;
case AST_NE_NE:
op = OP_NE_NE;
break;
case AST_LT:
op = OP_LT;
break;
case AST_LE:
op = OP_LE;
break;
case AST_GT:
op = OP_GT;
break;
case AST_GE:
op = OP_GE;
break;
case AST_INSTANCEOF:
op = OP_INSTANCEOF;
break;
default:
rcode = v7_throwf(bbuilder->v7, SYNTAX_ERROR, "unknown binary ast node");
V7_THROW(V7_SYNTAX_ERROR);
}
bcode_op(bbuilder, op);
clean:
return rcode;
}
V7_PRIVATE enum v7_err compile_binary(struct bcode_builder *bbuilder,
struct ast *a, ast_off_t *ppos,
enum ast_tag tag) {
enum v7_err rcode = V7_OK;
struct v7 *v7 = bbuilder->v7;
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
V7_TRY(binary_op(bbuilder, tag));
clean:
return rcode;
}
/*
* `pos` should be an offset of the byte right after a tag
*/
static lit_t string_lit(struct bcode_builder *bbuilder, struct ast *a,
ast_off_t pos) {
size_t i = 0, name_len;
val_t v = V7_UNDEFINED;
struct mbuf *m = &bbuilder->lit;
char *name = ast_get_inlined_data(a, pos, &name_len);
/* temp disabled because of short lits */
#if 0
for (i = 0; i < m->len / sizeof(val_t); i++) {
v = ((val_t *) m->buf)[i];
if (v7_is_string(v)) {
size_t l;
const char *s = v7_get_string(bbuilder->v7, &v, &l);
if (name_len == l && memcmp(name, s, name_len) == 0) {
lit_t res;
memset(&res, 0, sizeof(res));
res.idx = i + bcode_max_inline_type_tag;
return res;
}
}
}
#else
(void) i;
(void) v;
(void) m;
#endif
return bcode_add_lit(bbuilder, v7_mk_string(bbuilder->v7, name, name_len, 1));
}
#if V7_ENABLE__RegExp
WARN_UNUSED_RESULT
static enum v7_err regexp_lit(struct bcode_builder *bbuilder, struct ast *a,
ast_off_t pos, lit_t *res) {
enum v7_err rcode = V7_OK;
size_t name_len;
char *p;
char *name = ast_get_inlined_data(a, pos, &name_len);
val_t tmp = V7_UNDEFINED;
struct v7 *v7 = bbuilder->v7;
for (p = name + name_len - 1; *p != '/';) p--;
V7_TRY(v7_mk_regexp(bbuilder->v7, name + 1, p - (name + 1), p + 1,
(name + name_len) - p - 1, &tmp));
*res = bcode_add_lit(bbuilder, tmp);
clean:
return rcode;
}
#endif
#if !V7_DISABLE_LINE_NUMBERS
static void append_lineno_if_changed(struct v7 *v7,
struct bcode_builder *bbuilder,
int line_no) {
(void) v7;
if (line_no != 0 && line_no != v7->line_no) {
v7->line_no = line_no;
bcode_append_lineno(bbuilder, line_no);
}
}
#else
static void append_lineno_if_changed(struct v7 *v7,
struct bcode_builder *bbuilder,
int line_no) {
(void) v7;
(void) bbuilder;
(void) line_no;
}
#endif
static enum ast_tag fetch_tag(struct v7 *v7, struct bcode_builder *bbuilder,
struct ast *a, ast_off_t *ppos,
ast_off_t *ppos_after_tag) {
enum ast_tag ret = ast_fetch_tag(a, ppos);
int line_no = ast_get_line_no(a, *ppos);
append_lineno_if_changed(v7, bbuilder, line_no);
if (ppos_after_tag != NULL) {
*ppos_after_tag = *ppos;
}
ast_move_to_children(a, ppos);
return ret;
}
/*
* a++ and a-- need to ignore the updated value.
*
* Call this before updating the lhs.
*/
static void fixup_post_op(struct bcode_builder *bbuilder, enum ast_tag tag) {
if (tag == AST_POSTINC || tag == AST_POSTDEC) {
bcode_op(bbuilder, OP_UNSTASH);
}
}
/*
* evaluate rhs expression.
* ++a and a++ are equivalent to a+=1
*/
static enum v7_err eval_assign_rhs(struct bcode_builder *bbuilder,
struct ast *a, ast_off_t *ppos,
enum ast_tag tag) {
enum v7_err rcode = V7_OK;
struct v7 *v7 = bbuilder->v7;
/* a++ and a-- need to preserve initial value. */
if (tag == AST_POSTINC || tag == AST_POSTDEC) {
bcode_op(bbuilder, OP_STASH);
}
if (tag >= AST_PREINC && tag <= AST_POSTDEC) {
bcode_op(bbuilder, OP_PUSH_ONE);
} else {
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
}
switch (tag) {
case AST_PREINC:
case AST_POSTINC:
bcode_op(bbuilder, OP_ADD);
break;
case AST_PREDEC:
case AST_POSTDEC:
bcode_op(bbuilder, OP_SUB);
break;
case AST_ASSIGN:
/* no operation */
break;
default:
binary_op(bbuilder, assign_ast_map[tag - AST_ASSIGN - 1]);
}
clean:
return rcode;
}
static enum v7_err compile_assign(struct bcode_builder *bbuilder, struct ast *a,
ast_off_t *ppos, enum ast_tag tag) {
lit_t lit;
enum ast_tag ntag;
enum v7_err rcode = V7_OK;
struct v7 *v7 = bbuilder->v7;
ast_off_t pos_after_tag;
ntag = fetch_tag(v7, bbuilder, a, ppos, &pos_after_tag);
switch (ntag) {
case AST_IDENT:
lit = string_lit(bbuilder, a, pos_after_tag);
if (tag != AST_ASSIGN) {
bcode_op_lit(bbuilder, OP_GET_VAR, lit);
}
V7_TRY(eval_assign_rhs(bbuilder, a, ppos, tag));
bcode_op_lit(bbuilder, OP_SET_VAR, lit);
fixup_post_op(bbuilder, tag);
break;
case AST_MEMBER:
case AST_INDEX:
switch (ntag) {
case AST_MEMBER:
lit = string_lit(bbuilder, a, pos_after_tag);
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
bcode_push_lit(bbuilder, lit);
break;
case AST_INDEX:
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
break;
default:
/* unreachable, compilers are dumb */
V7_THROW(V7_SYNTAX_ERROR);
}
if (tag != AST_ASSIGN) {
bcode_op(bbuilder, OP_2DUP);
bcode_op(bbuilder, OP_GET);
}
V7_TRY(eval_assign_rhs(bbuilder, a, ppos, tag));
bcode_op(bbuilder, OP_SET);
fixup_post_op(bbuilder, tag);
break;
default:
/* We end up here on expressions like `1 = 2;`, it's a ReferenceError */
rcode = v7_throwf(bbuilder->v7, REFERENCE_ERROR, "unexpected ast node");
V7_THROW(V7_SYNTAX_ERROR);
}
clean:
return rcode;
}
/*
* Walks through all declarations (`var` and `function`) in the current scope,
* and adds names of all of them to `bcode->ops`. Additionally, `function`
* declarations are compiled right here, since they're hoisted in JS.
*/
static enum v7_err compile_local_vars(struct bcode_builder *bbuilder,
struct ast *a, ast_off_t start,
ast_off_t fvar) {
ast_off_t next, fvar_end;
char *name;
size_t name_len;
lit_t lit;
enum v7_err rcode = V7_OK;
struct v7 *v7 = bbuilder->v7;
size_t names_end = 0;
ast_off_t pos_after_tag;
/* calculate `names_end`: offset at which names in `bcode->ops` end */
names_end =
(size_t)(bcode_end_names(bbuilder->ops.buf, bbuilder->bcode->names_cnt) -
bbuilder->ops.buf);
if (fvar != start) {
/* iterate all `AST_VAR`s in the current scope */
do {
V7_CHECK_INTERNAL(fetch_tag(v7, bbuilder, a, &fvar, &pos_after_tag) ==
AST_VAR);
next = ast_get_skip(a, pos_after_tag, AST_VAR_NEXT_SKIP);
if (next == pos_after_tag) {
next = 0;
}
fvar_end = ast_get_skip(a, pos_after_tag, AST_END_SKIP);
/*
* iterate all `AST_VAR_DECL`s and `AST_FUNC_DECL`s in the current
* `AST_VAR`
*/
while (fvar < fvar_end) {
enum ast_tag tag = fetch_tag(v7, bbuilder, a, &fvar, &pos_after_tag);
V7_CHECK_INTERNAL(tag == AST_VAR_DECL || tag == AST_FUNC_DECL);
name = ast_get_inlined_data(a, pos_after_tag, &name_len);
if (tag == AST_VAR_DECL) {
/*
* it's a `var` declaration, so, skip the value for now, it'll be set
* to `undefined` initially
*/
ast_skip_tree(a, &fvar);
} else {
/*
* tag is an AST_FUNC_DECL: since functions in JS are hoisted,
* we compile it and put `OP_SET_VAR` directly here
*/
lit = string_lit(bbuilder, a, pos_after_tag);
V7_TRY(compile_expr_builder(bbuilder, a, &fvar));
bcode_op_lit(bbuilder, OP_SET_VAR, lit);
/* function declarations are stack-neutral */
bcode_op(bbuilder, OP_DROP);
/*
* Note: the `is_stack_neutral` flag will be set by `compile_stmt`
* later, when it encounters `AST_FUNC_DECL` again.
*/
}
V7_TRY(bcode_add_name(bbuilder, name, name_len, &names_end));
}
if (next > 0) {
fvar = next - 1;
}
} while (next != 0);
}
clean:
return rcode;
}
/*
* Just like `compile_expr_builder`, but it takes additional argument:
*`for_call`.
* If it's non-zero, the stack is additionally populated with `this` value
* for call.
*
* If there is a refinement (a dot, or a subscript), then it'll be the
* appropriate object. Otherwise, it's `undefined`.
*
* In non-strict mode, `undefined` will be changed to Global Object at runtime,
* see `OP_CALL` handling in `eval_bcode()`.
*/
V7_PRIVATE enum v7_err compile_expr_ext(struct bcode_builder *bbuilder,
struct ast *a, ast_off_t *ppos,
uint8_t for_call) {
enum v7_err rcode = V7_OK;
struct v7 *v7 = bbuilder->v7;
ast_off_t pos_after_tag;
enum ast_tag tag = fetch_tag(v7, bbuilder, a, ppos, &pos_after_tag);
switch (tag) {
case AST_MEMBER: {
lit_t lit = string_lit(bbuilder, a, pos_after_tag);
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
if (for_call) {
/* current TOS will be used as `this` */
bcode_op(bbuilder, OP_DUP);
}
bcode_push_lit(bbuilder, lit);
bcode_op(bbuilder, OP_GET);
break;
}
case AST_INDEX:
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
if (for_call) {
/* current TOS will be used as `this` */
bcode_op(bbuilder, OP_DUP);
}
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
bcode_op(bbuilder, OP_GET);
break;
default:
if (for_call) {
/*
* `this` will be an `undefined` (but it may change to Global Object
* at runtime)
*/
bcode_op(bbuilder, OP_PUSH_UNDEFINED);
}
*ppos = pos_after_tag - 1;
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
break;
}
clean:
return rcode;
}
V7_PRIVATE enum v7_err compile_delete(struct bcode_builder *bbuilder,
struct ast *a, ast_off_t *ppos) {
enum v7_err rcode = V7_OK;
struct v7 *v7 = bbuilder->v7;
ast_off_t pos_after_tag;
enum ast_tag tag = fetch_tag(v7, bbuilder, a, ppos, &pos_after_tag);
switch (tag) {
case AST_MEMBER: {
/* Delete a specified property of an object */
lit_t lit = string_lit(bbuilder, a, pos_after_tag);
/* put an object to delete property from */
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
/* put a property name */
bcode_push_lit(bbuilder, lit);
bcode_op(bbuilder, OP_DELETE);
break;
}
case AST_INDEX:
/* Delete a specified property of an object */
/* put an object to delete property from */
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
/* put a property name */
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
bcode_op(bbuilder, OP_DELETE);
break;
case AST_IDENT:
/* Delete the scope variable (or throw an error if strict mode) */
if (!bbuilder->bcode->strict_mode) {
/* put a property name */
bcode_push_lit(bbuilder, string_lit(bbuilder, a, pos_after_tag));
bcode_op(bbuilder, OP_DELETE_VAR);
} else {
rcode =
v7_throwf(bbuilder->v7, SYNTAX_ERROR,
"Delete of an unqualified identifier in strict mode.");
V7_THROW(V7_SYNTAX_ERROR);
}
break;
case AST_UNDEFINED:
/*
* `undefined` should actually be an undeletable property of the Global
* Object, so, trying to delete it just yields `false`
*/
bcode_op(bbuilder, OP_PUSH_FALSE);
break;
default:
/*
* For all other cases, we evaluate the expression given to `delete`
* for side effects, then drop the result, and yield `true`
*/
*ppos = pos_after_tag - 1;
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
bcode_op(bbuilder, OP_DROP);
bcode_op(bbuilder, OP_PUSH_TRUE);
break;
}
clean:
return rcode;
}
V7_PRIVATE enum v7_err compile_expr_builder(struct bcode_builder *bbuilder,
struct ast *a, ast_off_t *ppos) {
enum v7_err rcode = V7_OK;
struct v7 *v7 = bbuilder->v7;
ast_off_t pos_after_tag;
enum ast_tag tag = fetch_tag(v7, bbuilder, a, ppos, &pos_after_tag);
switch (tag) {
case AST_ADD:
case AST_SUB:
case AST_REM:
case AST_MUL:
case AST_DIV:
case AST_LSHIFT:
case AST_RSHIFT:
case AST_URSHIFT:
case AST_OR:
case AST_XOR:
case AST_AND:
case AST_EQ_EQ:
case AST_EQ:
case AST_NE:
case AST_NE_NE:
case AST_LT:
case AST_LE:
case AST_GT:
case AST_GE:
case AST_INSTANCEOF:
V7_TRY(compile_binary(bbuilder, a, ppos, tag));
break;
case AST_LOGICAL_NOT:
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
bcode_op(bbuilder, OP_LOGICAL_NOT);
break;
case AST_NOT:
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
bcode_op(bbuilder, OP_NOT);
break;
case AST_POSITIVE:
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
bcode_op(bbuilder, OP_POS);
break;
case AST_NEGATIVE:
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
bcode_op(bbuilder, OP_NEG);
break;
case AST_IDENT:
bcode_op_lit(bbuilder, OP_GET_VAR,
string_lit(bbuilder, a, pos_after_tag));
break;
case AST_MEMBER:
case AST_INDEX:
/*
* These two are handled by the "extended" version of this function,
* since we may need to put `this` value on stack, for "method pattern
* invocation".
*
* First of all, restore starting AST position, and then call extended
* function.
*/
*ppos = pos_after_tag - 1;
V7_TRY(compile_expr_ext(bbuilder, a, ppos, 0 /*not for call*/));
break;
case AST_IN:
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
bcode_op(bbuilder, OP_IN);
break;
case AST_TYPEOF: {
ast_off_t lookahead = *ppos;
tag = fetch_tag(v7, bbuilder, a, &lookahead, &pos_after_tag);
if (tag == AST_IDENT) {
*ppos = lookahead;
bcode_op_lit(bbuilder, OP_SAFE_GET_VAR,
string_lit(bbuilder, a, pos_after_tag));
} else {
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
}
bcode_op(bbuilder, OP_TYPEOF);
break;
}
case AST_ASSIGN:
case AST_PREINC:
case AST_PREDEC:
case AST_POSTINC:
case AST_POSTDEC:
case AST_REM_ASSIGN:
case AST_MUL_ASSIGN:
case AST_DIV_ASSIGN:
case AST_XOR_ASSIGN:
case AST_PLUS_ASSIGN:
case AST_MINUS_ASSIGN:
case AST_OR_ASSIGN:
case AST_AND_ASSIGN:
case AST_LSHIFT_ASSIGN:
case AST_RSHIFT_ASSIGN:
case AST_URSHIFT_ASSIGN:
V7_TRY(compile_assign(bbuilder, a, ppos, tag));
break;
case AST_COND: {
/*
* A ? B : C
*
* ->
*
*
* JMP_FALSE false
*
* JMP end
* false:
*
* end:
*
*/
bcode_off_t false_label, end_label;
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
false_label = bcode_op_target(bbuilder, OP_JMP_FALSE);
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
end_label = bcode_op_target(bbuilder, OP_JMP);
bcode_patch_target(bbuilder, false_label, bcode_pos(bbuilder));
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
bcode_patch_target(bbuilder, end_label, bcode_pos(bbuilder));
break;
}
case AST_LOGICAL_OR:
case AST_LOGICAL_AND: {
/*
* A && B
*
* ->
*
*
* JMP_FALSE end
* POP
*
* end:
*
*/
bcode_off_t end_label;
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
bcode_op(bbuilder, OP_DUP);
end_label = bcode_op_target(
bbuilder, tag == AST_LOGICAL_AND ? OP_JMP_FALSE : OP_JMP_TRUE);
bcode_op(bbuilder, OP_DROP);
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
bcode_patch_target(bbuilder, end_label, bcode_pos(bbuilder));
break;
}
/*
* A, B, C
*
* ->
*
*
* DROP
*
* DROP
*
*/
case AST_SEQ: {
ast_off_t end = ast_get_skip(a, pos_after_tag, AST_END_SKIP);
while (*ppos < end) {
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
if (*ppos < end) {
bcode_op(bbuilder, OP_DROP);
}
}
break;
}
case AST_CALL:
case AST_NEW: {
/*
* f()
*
* ->
*
* PUSH_UNDEFINED (value for `this`)
* GET_VAR "f"
* CHECK_CALL
* CALL 0 args
*
* ---------------
*
* f(a, b)
*
* ->
*
* PUSH_UNDEFINED (value for `this`)
* GET_VAR "f"
* CHECK_CALL
* GET_VAR "a"
* GET_VAR "b"
* CALL 2 args
*
* ---------------
*
* o.f(a, b)
*
* ->
*
* GET_VAR "o" (so that `this` will be an `o`)
* DUP (we'll also need `o` for GET below, so, duplicate it)
* PUSH_LIT "f"
* GET (get property "f" of the object "o")
* CHECK_CALL
* GET_VAR "a"
* GET_VAR "b"
* CALL 2 args
*
*/
int args;
ast_off_t end = ast_get_skip(a, pos_after_tag, AST_END_SKIP);
V7_TRY(compile_expr_ext(bbuilder, a, ppos, 1 /*for call*/));
bcode_op(bbuilder, OP_CHECK_CALL);
for (args = 0; *ppos < end; args++) {
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
}
bcode_op(bbuilder, (tag == AST_CALL ? OP_CALL : OP_NEW));
if (args > 0x7f) {
rcode = v7_throwf(bbuilder->v7, SYNTAX_ERROR, "too many arguments");
V7_THROW(V7_SYNTAX_ERROR);
}
bcode_op(bbuilder, (uint8_t) args);
break;
}
case AST_DELETE: {
V7_TRY(compile_delete(bbuilder, a, ppos));
break;
}
case AST_OBJECT: {
/*
* {a:, ...}
*
* ->
*
* CREATE_OBJ
* DUP
* PUSH_LIT "a"
*
* SET
* POP
* ...
*/
/*
* Literal indices of property names of current object literal.
* Needed for strict mode: we need to keep track of the added
* properties, since duplicates are not allowed
*/
struct mbuf cur_literals;
lit_t lit;
ast_off_t end = ast_get_skip(a, pos_after_tag, AST_END_SKIP);
mbuf_init(&cur_literals, 0);
bcode_op(bbuilder, OP_CREATE_OBJ);
while (*ppos < end) {
tag = fetch_tag(v7, bbuilder, a, ppos, &pos_after_tag);
switch (tag) {
case AST_PROP:
bcode_op(bbuilder, OP_DUP);
lit = string_lit(bbuilder, a, pos_after_tag);
/* disabled because we broke get_lit */
#if 0
if (bbuilder->bcode->strict_mode) {
/*
* In strict mode, check for duplicate property names in
* object literals
*/
char *plit;
for (plit = (char *) cur_literals.buf;
(char *) plit < cur_literals.buf + cur_literals.len;
plit++) {
const char *str1, *str2;
size_t size1, size2;
v7_val_t val1, val2;
val1 = bcode_get_lit(bbuilder->bcode, lit);
str1 = v7_get_string(bbuilder->v7, &val1, &size1);
val2 = bcode_get_lit(bbuilder->bcode, *plit);
str2 = v7_get_string(bbuilder->v7, &val2, &size2);
if (size1 == size2 && memcmp(str1, str2, size1) == 0) {
/* found already existing property of the same name */
rcode = v7_throwf(bbuilder->v7, SYNTAX_ERROR,
"duplicate data property in object literal "
"is not allowed in strict mode");
V7_THROW2(V7_SYNTAX_ERROR, ast_object_clean);
}
}
mbuf_append(&cur_literals, &lit, sizeof(lit));
}
#endif
bcode_push_lit(bbuilder, lit);
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
bcode_op(bbuilder, OP_SET);
bcode_op(bbuilder, OP_DROP);
break;
default:
rcode = v7_throwf(bbuilder->v7, SYNTAX_ERROR, "not implemented");
V7_THROW2(V7_SYNTAX_ERROR, ast_object_clean);
}
}
ast_object_clean:
mbuf_free(&cur_literals);
if (rcode != V7_OK) {
V7_THROW(rcode);
}
break;
}
case AST_ARRAY: {
/*
* [,,,...]
*
* ->
*
* CREATE_ARR
* PUSH_ZERO
*
* 2DUP
*
* SET
* POP
* PUSH_ONE
* ADD
*
* PUSH_ONE
* ADD
*
* 2DUP
*
* ...
* POP // tmp index
*
* TODO(mkm): optimize this out. we can have more compact array push
* that uses a special marker value for missing array elements
* (which are not the same as undefined btw)
*/
ast_off_t end = ast_get_skip(a, pos_after_tag, AST_END_SKIP);
bcode_op(bbuilder, OP_CREATE_ARR);
bcode_op(bbuilder, OP_PUSH_ZERO);
while (*ppos < end) {
ast_off_t lookahead = *ppos;
tag = fetch_tag(v7, bbuilder, a, &lookahead, &pos_after_tag);
if (tag != AST_NOP) {
bcode_op(bbuilder, OP_2DUP);
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
bcode_op(bbuilder, OP_SET);
bcode_op(bbuilder, OP_DROP);
} else {
*ppos = lookahead; /* skip nop */
}
bcode_op(bbuilder, OP_PUSH_ONE);
bcode_op(bbuilder, OP_ADD);
}
bcode_op(bbuilder, OP_DROP);
break;
}
case AST_FUNC: {
lit_t flit;
/*
* Create half-done function: without scope and prototype. The real
* function will be created from this one during bcode evaluation: see
* `bcode_instantiate_function()`.
*/
val_t funv = mk_js_function(bbuilder->v7, NULL, V7_UNDEFINED);
/* Create bcode in this half-done function */
struct v7_js_function *func = get_js_function_struct(funv);
func->bcode = (struct bcode *) calloc(1, sizeof(*bbuilder->bcode));
bcode_init(func->bcode, bbuilder->bcode->strict_mode,
NULL /* will be set below */, 0);
bcode_copy_filename_from(func->bcode, bbuilder->bcode);
retain_bcode(bbuilder->v7, func->bcode);
flit = bcode_add_lit(bbuilder, funv);
*ppos = pos_after_tag - 1;
V7_TRY(compile_function(v7, a, ppos, func->bcode));
bcode_push_lit(bbuilder, flit);
bcode_op(bbuilder, OP_FUNC_LIT);
break;
}
case AST_THIS:
bcode_op(bbuilder, OP_PUSH_THIS);
break;
case AST_VOID:
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
bcode_op(bbuilder, OP_DROP);
bcode_op(bbuilder, OP_PUSH_UNDEFINED);
break;
case AST_NULL:
bcode_op(bbuilder, OP_PUSH_NULL);
break;
case AST_NOP:
case AST_UNDEFINED:
bcode_op(bbuilder, OP_PUSH_UNDEFINED);
break;
case AST_TRUE:
bcode_op(bbuilder, OP_PUSH_TRUE);
break;
case AST_FALSE:
bcode_op(bbuilder, OP_PUSH_FALSE);
break;
case AST_NUM: {
double dv = ast_get_num(a, pos_after_tag);
if (dv == 0) {
bcode_op(bbuilder, OP_PUSH_ZERO);
} else if (dv == 1) {
bcode_op(bbuilder, OP_PUSH_ONE);
} else {
bcode_push_lit(bbuilder, bcode_add_lit(bbuilder, v7_mk_number(v7, dv)));
}
break;
}
case AST_STRING:
bcode_push_lit(bbuilder, string_lit(bbuilder, a, pos_after_tag));
break;
case AST_REGEX:
#if V7_ENABLE__RegExp
{
lit_t tmp;
rcode = regexp_lit(bbuilder, a, pos_after_tag, &tmp);
if (rcode != V7_OK) {
rcode = V7_SYNTAX_ERROR;
goto clean;
}
bcode_push_lit(bbuilder, tmp);
break;
}
#else
rcode =
v7_throwf(bbuilder->v7, SYNTAX_ERROR, "Regexp support is disabled");
V7_THROW(V7_SYNTAX_ERROR);
#endif
case AST_LABEL:
case AST_LABELED_BREAK:
case AST_LABELED_CONTINUE:
/* TODO(dfrank): implement */
rcode = v7_throwf(bbuilder->v7, SYNTAX_ERROR, "not implemented");
V7_THROW(V7_SYNTAX_ERROR);
case AST_WITH:
rcode = v7_throwf(bbuilder->v7, SYNTAX_ERROR, "not implemented");
V7_THROW(V7_SYNTAX_ERROR);
default:
/*
* We end up here if the AST is broken.
*
* It might be appropriate to return `V7_INTERNAL_ERROR` here, but since
* we might receive AST from network or something, we just interpret
* it as SyntaxError.
*/
rcode = v7_throwf(bbuilder->v7, SYNTAX_ERROR, "unknown ast node %d",
(int) tag);
V7_THROW(V7_SYNTAX_ERROR);
}
clean:
return rcode;
}
V7_PRIVATE enum v7_err compile_stmt(struct bcode_builder *bbuilder,
struct ast *a, ast_off_t *ppos);
V7_PRIVATE enum v7_err compile_stmts(struct bcode_builder *bbuilder,
struct ast *a, ast_off_t *ppos,
ast_off_t end) {
enum v7_err rcode = V7_OK;
struct v7 *v7 = bbuilder->v7;
while (*ppos < end) {
V7_TRY(compile_stmt(bbuilder, a, ppos));
if (!bbuilder->v7->is_stack_neutral) {
bcode_op(bbuilder, OP_SWAP_DROP);
} else {
bbuilder->v7->is_stack_neutral = 0;
}
}
clean:
return rcode;
}
V7_PRIVATE enum v7_err compile_stmt(struct bcode_builder *bbuilder,
struct ast *a, ast_off_t *ppos) {
ast_off_t end;
enum ast_tag tag;
ast_off_t cond, pos_after_tag;
bcode_off_t body_target, body_label, cond_label;
struct mbuf case_labels;
enum v7_err rcode = V7_OK;
struct v7 *v7 = bbuilder->v7;
tag = fetch_tag(v7, bbuilder, a, ppos, &pos_after_tag);
mbuf_init(&case_labels, 0);
switch (tag) {
/*
* if(E) {
* BT...
* } else {
* BF...
* }
*
* ->
*
*
* JMP_FALSE body
*
* JMP end
* body:
*
* end:
*
* If else clause is omitted, it will emit output equivalent to:
*
* if(E) {BT} else undefined;
*/
case AST_IF: {
ast_off_t if_false;
bcode_off_t end_label, if_false_label;
end = ast_get_skip(a, pos_after_tag, AST_END_SKIP);
if_false = ast_get_skip(a, pos_after_tag, AST_END_IF_TRUE_SKIP);
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
if_false_label = bcode_op_target(bbuilder, OP_JMP_FALSE);
/* body if true */
V7_TRY(compile_stmts(bbuilder, a, ppos, if_false));
if (if_false != end) {
/* `else` branch is present */
end_label = bcode_op_target(bbuilder, OP_JMP);
/* will jump here if `false` */
bcode_patch_target(bbuilder, if_false_label, bcode_pos(bbuilder));
/* body if false */
V7_TRY(compile_stmts(bbuilder, a, ppos, end));
bcode_patch_target(bbuilder, end_label, bcode_pos(bbuilder));
} else {
/*
* `else` branch is not present: just remember where we should
* jump in case of `false`
*/
bcode_patch_target(bbuilder, if_false_label, bcode_pos(bbuilder));
}
bbuilder->v7->is_stack_neutral = 1;
break;
}
/*
* while(C) {
* B...
* }
*
* ->
*
* TRY_PUSH_LOOP end
* JMP cond
* body:
*
* cond:
*
* JMP_TRUE body
* end:
* JMP_IF_CONTINUE cond
* TRY_POP
*
*/
case AST_WHILE: {
bcode_off_t end_label, continue_label, continue_target;
end = ast_get_skip(a, pos_after_tag, AST_END_SKIP);
cond = *ppos;
ast_skip_tree(a, ppos);
end_label = bcode_op_target(bbuilder, OP_TRY_PUSH_LOOP);
/*
* Condition check is at the end of the loop, this layout
* reduces the number of jumps in the steady state.
*/
cond_label = bcode_op_target(bbuilder, OP_JMP);
body_target = bcode_pos(bbuilder);
V7_TRY(compile_stmts(bbuilder, a, ppos, end));
continue_target = bcode_pos(bbuilder);
bcode_patch_target(bbuilder, cond_label, continue_target);
V7_TRY(compile_expr_builder(bbuilder, a, &cond));
body_label = bcode_op_target(bbuilder, OP_JMP_TRUE);
bcode_patch_target(bbuilder, body_label, body_target);
bcode_patch_target(bbuilder, end_label, bcode_pos(bbuilder));
continue_label = bcode_op_target(bbuilder, OP_JMP_IF_CONTINUE);
bcode_patch_target(bbuilder, continue_label, continue_target);
bcode_op(bbuilder, OP_TRY_POP);
bbuilder->v7->is_stack_neutral = 1;
break;
}
case AST_BREAK:
bcode_op(bbuilder, OP_BREAK);
break;
case AST_CONTINUE:
bcode_op(bbuilder, OP_CONTINUE);
break;
/*
* Frame objects (`v7->vals.call_stack`) contain one more hidden property:
* `____t`, which is an array of offsets in bcode. Each element of the array
* is an offset of either `catch` or `finally` block (distinguished by the
* tag: `OFFSET_TAG_CATCH` or `OFFSET_TAG_FINALLY`). Let's call this array
* as a "try stack". When evaluator enters new `try` block, it adds
* appropriate offset(s) at the top of "try stack", and when we unwind the
* stack, we can "pop" offsets from "try stack" at each level.
*
* try {
* TRY_B
* } catch (e) {
* CATCH_B
* } finally {
* FIN_B
* }
*
* ->
* OP_TRY_PUSH_FINALLY finally
* OP_TRY_PUSH_CATCH catch
*
* OP_TRY_POP
* JMP finally
* catch:
* OP_TRY_POP
* OP_ENTER_CATCH
*
* OP_EXIT_CATCH
* finally:
* OP_TRY_POP
*
* OP_AFTER_FINALLY
*
* ---------------
*
* try {
* TRY_B
* } catch (e) {
* CATCH_B
* }
*
* ->
* OP_TRY_PUSH_CATCH catch
*
* OP_TRY_POP
* JMP end
* catch:
* OP_TRY_POP
* OP_ENTER_CATCH
*
* OP_EXIT_CATCH
* end:
*
* ---------------
*
* try {
* TRY_B
* } finally {
* FIN_B
* }
*
* ->
* OP_TRY_PUSH_FINALLY finally
*
* finally:
* OP_TRY_POP
*
* OP_AFTER_FINALLY
*/
case AST_TRY: {
ast_off_t acatch, afinally;
bcode_off_t finally_label, catch_label;
end = ast_get_skip(a, pos_after_tag, AST_END_SKIP);
acatch = ast_get_skip(a, pos_after_tag, AST_TRY_CATCH_SKIP);
afinally = ast_get_skip(a, pos_after_tag, AST_TRY_FINALLY_SKIP);
if (afinally != end) {
/* `finally` clause is present: push its offset */
finally_label = bcode_op_target(bbuilder, OP_TRY_PUSH_FINALLY);
}
if (acatch != afinally) {
/* `catch` clause is present: push its offset */
catch_label = bcode_op_target(bbuilder, OP_TRY_PUSH_CATCH);
}
/* compile statements of `try` block */
V7_TRY(compile_stmts(bbuilder, a, ppos, acatch));
if (acatch != afinally) {
/* `catch` clause is present: compile it */
bcode_off_t after_catch_label;
/*
* pop offset pushed by OP_TRY_PUSH_CATCH, and jump over the `catch`
* block
*/
bcode_op(bbuilder, OP_TRY_POP);
after_catch_label = bcode_op_target(bbuilder, OP_JMP);
/* --- catch --- */
/* in case of exception in the `try` block above, we'll get here */
bcode_patch_target(bbuilder, catch_label, bcode_pos(bbuilder));
/* pop offset pushed by OP_TRY_PUSH_CATCH */
bcode_op(bbuilder, OP_TRY_POP);
/*
* retrieve identifier where to store thrown error, and make sure
* it is actually an indentifier (AST_IDENT)
*/
tag = fetch_tag(v7, bbuilder, a, ppos, &pos_after_tag);
V7_CHECK(tag == AST_IDENT, V7_SYNTAX_ERROR);
/*
* when we enter `catch` block, the TOS contains thrown value.
* We should create private frame for the `catch` clause, and populate
* a variable with the thrown value there.
* The `OP_ENTER_CATCH` opcode does just that.
*/
bcode_op_lit(bbuilder, OP_ENTER_CATCH,
string_lit(bbuilder, a, pos_after_tag));
/*
* compile statements until the end of `catch` clause
* (`afinally` points to the end of the `catch` clause independently
* of whether the `finally` clause is present)
*/
V7_TRY(compile_stmts(bbuilder, a, ppos, afinally));
/* pop private frame */
bcode_op(bbuilder, OP_EXIT_CATCH);
bcode_patch_target(bbuilder, after_catch_label, bcode_pos(bbuilder));
}
if (afinally != end) {
/* `finally` clause is present: compile it */
/* --- finally --- */
/* after the `try` block above executes, we'll get here */
bcode_patch_target(bbuilder, finally_label, bcode_pos(bbuilder));
/* pop offset pushed by OP_TRY_PUSH_FINALLY */
bcode_op(bbuilder, OP_TRY_POP);
/* compile statements until the end of `finally` clause */
V7_TRY(compile_stmts(bbuilder, a, ppos, end));
bcode_op(bbuilder, OP_AFTER_FINALLY);
}
bbuilder->v7->is_stack_neutral = 1;
break;
}
case AST_THROW: {
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
bcode_op(bbuilder, OP_THROW);
break;
}
/*
* switch(E) {
* default:
* D...
* case C1:
* B1...
* case C2:
* B2...
* }
*
* ->
*
* TRY_PUSH_SWITCH end
*
* DUP
*
* EQ
* JMP_TRUE_DROP l1
* DUP
*
* EQ
* JMP_TRUE_DROP l2
* DROP
* JMP dfl
*
* dfl:
*
*
* l1:
*
*
* l2:
*
*
* end:
* TRY_POP
*
* If the default case is missing we treat it as if had an empty body and
* placed in last position (i.e. `dfl` label is replaced with `end`).
*
* Before emitting a case/default block (except the first one) we have to
* drop the TOS resulting from evaluating the last expression
*/
case AST_SWITCH: {
bcode_off_t dfl_label, end_label;
ast_off_t case_end, case_start;
enum ast_tag case_tag;
int i, has_default = 0, cases = 0;
end = ast_get_skip(a, pos_after_tag, AST_END_SKIP);
end_label = bcode_op_target(bbuilder, OP_TRY_PUSH_SWITCH);
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
case_start = *ppos;
/* first pass: evaluate case expression and generate jump table */
while (*ppos < end) {
case_tag = fetch_tag(v7, bbuilder, a, ppos, &pos_after_tag);
assert(case_tag == AST_DEFAULT || case_tag == AST_CASE);
case_end = ast_get_skip(a, pos_after_tag, AST_END_SKIP);
switch (case_tag) {
case AST_DEFAULT:
/* default jump table entry must be the last one */
break;
case AST_CASE: {
bcode_off_t case_label;
bcode_op(bbuilder, OP_DUP);
V7_TRY(compile_expr_builder(bbuilder, a, ppos));
bcode_op(bbuilder, OP_EQ);
case_label = bcode_op_target(bbuilder, OP_JMP_TRUE_DROP);
cases++;
mbuf_append(&case_labels, &case_label, sizeof(case_label));
break;
}
default:
assert(case_tag == AST_DEFAULT || case_tag == AST_CASE);
}
*ppos = case_end;
}
/* jmp table epilogue: unconditional jump to default case */
bcode_op(bbuilder, OP_DROP);
dfl_label = bcode_op_target(bbuilder, OP_JMP);
*ppos = case_start;
/* second pass: emit case bodies and patch jump table */
for (i = 0; *ppos < end;) {
case_tag = fetch_tag(v7, bbuilder, a, ppos, &pos_after_tag);
assert(case_tag == AST_DEFAULT || case_tag == AST_CASE);
assert(i <= cases);
case_end = ast_get_skip(a, pos_after_tag, AST_END_SKIP);
switch (case_tag) {
case AST_DEFAULT:
has_default = 1;
bcode_patch_target(bbuilder, dfl_label, bcode_pos(bbuilder));
V7_TRY(compile_stmts(bbuilder, a, ppos, case_end));
break;
case AST_CASE: {
bcode_off_t case_label = ((bcode_off_t *) case_labels.buf)[i++];
bcode_patch_target(bbuilder, case_label, bcode_pos(bbuilder));
ast_skip_tree(a, ppos);
V7_TRY(compile_stmts(bbuilder, a, ppos, case_end));
break;
}
default:
assert(case_tag == AST_DEFAULT || case_tag == AST_CASE);
}
*ppos = case_end;
}
mbuf_free(&case_labels);
if (!has_default) {
bcode_patch_target(bbuilder, dfl_label, bcode_pos(bbuilder));
}
bcode_patch_target(bbuilder, end_label, bcode_pos(bbuilder));
bcode_op(bbuilder, OP_TRY_POP);
bbuilder->v7->is_stack_neutral = 1;
break;
}
/*
* for(INIT,COND,IT) {
* B...
* }
*
* ->
*
*